前言:本文是另一篇文章的续文,阅读前文后再阅读此文会更舒畅。
- Vue响应式原理
Vue 递归遍历data对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。在 property 被修改时,也就是setter 触发,会重新渲染。(看不懂的话后面有简单解释)
- 针对添加或删除对象不重新渲染页面现象,下面从官网找到解释:
Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。(本人读了好几遍,感觉这个解释不怎么合理,后面列出了个人见解)
下面是VUE实现源码~的大致,差不多就是这样实现的,自己阅读下,不难。
const data = {
head: 'hello today',
obj: {
name: 'ludeng',
age: 18,
sex: 'male'
},
arr: [1, 2, 3, 4]
}
// 变异数组,修改原型链上的push,pop等函数,使用它们会渲染页面
const ArrayPrototype = Object.create(Array.prototype);
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method =>{
ArrayPrototype[method] = function() {
Array.prototype[method].call(this, ...arguments);
render();
}
})
function defineReactive(data, key, value){
observer(data[key])
Object.defineProperty(data, key, {
get() { //当访问该属性时,会调用此函数
console.log("读")
return value;
},
set(newVal) { //当属性值被修改时,会调用此函数
console.log("写")
value = newVal;
render()
}
})
}
//监视对象及所有子项,不监视数组项
function observer(data) {
if(Array.isArray(data)) {
data.__proto__ = ArrayPrototype;
return;
}
if(typeof data === 'object') { //数组也是对象,vue不监控数组,上面单独处理数组
for(const key in data) {
defineReactive(data, key, data[key]);
}
}
}
function render() {
console.log("页面渲染了");
}
observer(data); //data是要监视的数据
先来稍微解析下上面代码,
defineReactive()
就是给对象的某属性设置Object.defineProperty
。
observer()
是循环一个对象的属性,每个属性都调用defineReactive()
。
数组那块就是修改原型链上的方法,使用push、pop方法时会渲染。
解释原理:使用递归,利用Object.defineProperty,给所有子成员添加get()&set()。当属性值被修改时会调用set() (set()里调用render()),所以修改对象属性值会渲染,而删除和添加不会(删除添加不会调用set())
举例解释:data{ obj: { name: "a", age: 18 } },递归处理obj, name, age。给obj添加属性(obj.a = "a"),obj本身地址未修改,不会调用set(),所以不会渲染,删除同理。
修改子成员(name,age)为什么会渲染?obj地址未修改啊!修改name渲染,因为name也被处理过,会调用set()。
数组则修改原型链上的push\pop等方法,调用变异数组方法会调用render()。所以使用push修改数组的时候,页面会重新渲染。
以上个人理解,可能会不准确,欢迎指正。
[vue官方]深入响应式原理:https://cn.vuejs.org/v2/guide/reactivity.html