1.vue2与vue3响应式原理
vue2的响应式原理
对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
Object.defineProperty(data, 'count', { get () {}, set () {}})
问题
对象直接新添加的属性或删除已有属性, 界面不会自动更新
直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}
vue3的响应式原理
通过Proxy(代理): 拦截对数据任意属性的任意操作, 包括属性值的读写, 属性的添加, 属性的删除等。
通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作。
文档:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
const obj = {name:"张三"};const user = new Proxy(obj, { // 拦截读取属性值 get (target, prop) { return Reflect.get(target, prop) }, // 拦截设置属性值或添加新属性 set (target, prop, value) { return Reflect.set(target, prop, value) }, // 拦截删除属性 deleteProperty (target, prop) { return Reflect.deleteProperty(target, prop) }})proxy.name = 'tom';
2.计算属性和监听
computed()用来创建计算属性,返回值是一个ref的实例。
getter
传入一个函数则代表了get操作
setup() { const user = reactive({ firstName: "张", lastName: "三", fullName: "" }); user.fullName = computed(() => { return user.firstName + "-" + user.lastName; });}
setter和getter
传入一个对象具有set和get函数
setup() { const user = reactive({ firstName: "张", lastName: "三", fullName: "" }); user.fullName = computed({ get() { return user.firstName + "-" + user.lastName; }, set(val) { user.firstName = val.split("-")[0]; user.lastName = val.split("-")[1]; } });}
watch函数:
第一个参数被监听对象。
第二个参数回调函数。
第三个是配置对象
immediate 为 true 默认会执行一次watch的回调函数
deep 为 true 深度监视
watch(user,({ firstName, lastName },prevName) => { user.fullName = firstName + "-" + lastName;},{ immediate: true, deep: true});
watch可以监视多个对象
使用数组来指定
如果是ref对象, 直接指定
如果是reactive对象中的属性, 必须通过函数来指定
watch( [() => user.firstName, () => user.lastName, () => user.fullName], values => { console.log("监视多个数据", values); // ['张','三','张-三'] } );
watchEffect函数:
默认执行一次
不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
watchEffect(()=>{ // user.fullName = user.firstName + "-" + user.lastName; user.firstName = user.fullName.split('-')[0]; user.lastName = user.fullName.split('-')[1];})
watch清除监视:
在setup()函数内创建的watch监视,会在当前组件被销毁的时候自动停止。如果想要明确的停止某个监视,可以调用watch()函数的返回值即可
// 创建监视,并得到 停止函数const stop = watch(() => { /* ... */})// 调用停止函数,清除对应的监视stop()
<template> <van-form> <van-field v-model="user.firstName" name="姓氏" label="姓氏" placeholder="请输入姓氏" /> <van-field v-model="user.lastName" name="名字" label="名字" placeholder="请输入名字" /> van-form> <van-form> <van-field v-model="user.fullName" name="姓名" label="姓名" placeholder="请输入姓名" /> van-form>template><script>import { reactive, computed, watch, watchEffect } from "vue";export default { setup() { const user = reactive({ firstName: "张", lastName: "三", fullName: "" }); // 1.computed传入一个函数则代表了get操作 // user.fullName = computed(() => { // return user.firstName + "-" + user.lastName; // }); // 2.computed传入一个对象,对象中有get和set函数 // user.fullName = computed({ // get() { // return user.firstName + "-" + user.lastName; // }, // set(val) { // user.firstName = val.split("-")[0]; // user.lastName = val.split("-")[1]; // } // }); // 3.使用watch监听对象 // 第一个参数被监听对象。第二个参数回调函数。第三个是配置对象 // immediate 为 true 默认会执行一次watch的回调函数 // deep 为 true 深度监视 // watch(user,({ firstName, lastName },prevName) => { // user.fullName = firstName + "-" + lastName; // },{ // immediate: true, // deep: true // }); // 4.watchEffect默认会执行一次 // 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据 // watchEffect(()=>{ // // user.fullName = user.firstName + "-" + user.lastName; // user.firstName = user.fullName.split('-')[0]; // user.lastName = user.fullName.split('-')[1]; // }) // 5.watch可以监视多个对象 // 使用数组来指定 // 如果是ref对象, 直接指定 // 如果是reactive对象中的属性, 必须通过函数来指定 watch( [() => user.firstName, () => user.lastName, () => user.fullName], values => { console.log("监视多个数据", values); // ['张','三','张-三'] } ); return { user }; }};script>