VUE2与VUE3响应式的原理及区别
一、vue2响应式缺陷
1、对接添加的新属性或者删除已有属性界面不会更新(不是响应式)
2、当响应式数据为数组时直接通过下表(arr[x] = xxx)或者更新数据的长度(arr=[0] arr[1] = 1)界面不会更新(不是响应式)
<template>
<div class="test">
<div v-for="(item, key) in person" :key="key">
{{ key }}-{{ item }}
</div>
<button @click="addObj">添加对象属性</button>
<div v-for="item in arr" :key="item">
{{item}}
</div>
<button @click="changeArr">添加</button>
</div>
</template>
<script>
export default {
data() {
return {
person: {
name: 'ls',
age: 29
},
arr: [1, 2, 3]
}
},
mounted () {
;
},
methods: {
addObj() {
this.person.sex = '男' // 点击后不是响应式
},
changeArr() {
arr[2] = 4 // 点击后不是响应式
arr[8] = 8 // 点击后不是响应式
}
},
}
</script>
<style lang="scss" scoped>
</style>
Vue3中不存在以上两个问题请往下看
二、Vue2的响应式原理
-
核心
对象:通过defineProperty对对象的已有属性值的读取和修改进行劫持(监听/拦截)
/** * const vm = new Vue({ * el: '#app', * data: { * name: 'ls' , * age: 29 * } * }) */ // vm充当vue实例 const vm = {} // data中的响应式数据 const data = { name: 'ls', age: 29 } // 通过遍历data将其数值绑定到实例vm上,对属性进行修改和读取进行拦截 Object.entries(data).forEach(([key, value]) => { let inval = value /* defineProperty Object.defineProperty(obj, prop, descriptor) obj 要定义属性的对象。 prop 要定义或修改的属性的名称或 Symbol 。 descriptor 要定义或修改的属性描述符。 @return 被传递给函数的对象 */ Object.defineProperty(vm, key, { set (newVal) { console.log('执行set') inval = newVal }, get () { console.log('执行get') return inval } }) }) // 读取值 console.log(vm.name) // '执行get' ls // 修改值 vm.name = 'xm' // '执行set' console.log(vm.name) // '执行get' xm vm.sex = '男' // 不会执行set方法 console.log(vm.name) // 只能打印出'男' 不会执行get方法
-
疑问?
按照以上逻辑vue2中数组中的push、splice、pop等改变原有数组的方法也是不能响应式。但事实上Vue的这些数组方法能做到响应式。为什么?
事实是push、splice、pop等方法已经被vue重写。请看以下代码
// 把数组方法放到一个对象中 const obj = { push () {}, pop () {}, shift () {}, unshift () {}, splice () {}, sosrt () {}, reverse () {} } // 遍历对象,并且监听 Object.keys().forEach(key => { Object.defineProperty(obj, key, { value: function (...args) { console.log('我执行了') return Array.prototype[key].call(this, ...args) } }) }) const arr = [] // arr.__proto__其实就是Array.prototype,将原型设置为 obj相当于 new Array(实例) 继承了obj arr.__proto__ = obj // arr继承了obj中方法 arr.push(1) // 会打印 '我执行了'
三、Vue3的响应式原理
-
核心
- 通过Proxy代理:拦截对象对自己本身的操作,包括属性值的读取、修改和删除等
- 通过Reflect反射:动态对被代理对象的相应属性进行劫持操作
- 文档
const person = {
name: 'ls',
age: 29
}
// 代理对象
const proxyPerson = new Proxy(person, {
set (target, key, val) {
console.log('劫持set', key)
return Reflect.set(target, key, val)
},
get (target, key) {
console.log('劫持get', key)
return Reflect.get(target, key)
},
deleteProperty(target, key) {
console.log('劫持delete', key)
return Reflect.deleteProperty(target, key)
}
})
// 读取值
console.log(proxyPerson === person) // false
console.log(proxyPerson.name) // '劫持get' name ls
// 设置值
proxyPerson.name = 'xm' // '劫持set' name xm
proxyPerson.age = 18 // '劫持set' age 18
console.log(person)
/*
{
name: 'ls',
age: 29
}
*/
-
总结
Vue3通过使用Proxy代理的方式拦截对象本身,所以Vue3中添加/删除属性都是响应式的,包括通过数组下标修改数组值也是响应式的。
-
end