说说响应式原理中哪些让你"卧槽"的知识点
你真的了解响应式原理么?
响应式原理在平时面试中十之八九会被问到,在面对这个问题时候,我们大多数能想到的就是对Object.defindeProperty
侃侃而谈,确实Vue 的响应式原理是核心是通过 ES5 的保护对象的Object.defindeProperty
中的访问器属性中的 get
和 set
方法,data
中声明的属性都被添加了访问器属性,当读取 data
中的数据时自动调用 get
方法,当修改 data
中的数据时,自动调用 set
方法,检测到数据的变化;但是问自己几个小问题:
- 对象和数组是如何进行检测
- 数组中的
push
、pop
、shift
、unshift
、reverse
、sort
、splice
原生方法又是如何做到新增数据的检测的 - 数组又是如何进行替代索引赋值的
如果你还有点迷糊,请认真阅读我之前写的一篇文章vue响应式原理以及依赖收集;这篇文章就不来细说,就谈谈几个让人为之一震的知识点。
让你‘卧槽’的vue响应式原理知识点
- 数组方法的劫持
在Vue中我们在处理数组的时候劫持
了数组原生的一些方法,从而达到对数组各个元素进行检测;那么是如何进行劫持
的呢?vue很巧妙的重写了数组的原型方法;具体做法如下:
let oldArrayProtoMethods = Array.prototype;
export let arrayMethods = Object.create(oldArrayProtoMethods);
let methods = [
'push',
'pop',
'shift',
'unshift',
'reverse',
'sort',
'splice'
];
methods.forEach(method => {
arrayMethods[method] = function (...args) {
const result = oldArrayProtoMethods[method].apply(this, args);
const ob = this.__ob__;
let inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2)
default:
break;
}
if (inserted) ob.observeArray(inserted); // 对新增的每一项进行观测
return result
}
})
想要更详细点的可参考我之前写的一篇文章vue中数组的一些方法是如何进行试图更新的;
__ob__
的“跨域”相联;
前面提到的我们在做数组的检测时会重写这个原型方法,在vue源码中这个作为了一个单独模块;那么就有了值得思考的问题,就是我们在重写原型方法模块如何调用Observe
类里的observeArray
方法进行检测?Vue中很巧妙的在观测的对象中加入一个__ob__
属性,指向当前这个Observe
实例,这样每个响应式数据就可以访问Observe
实例上的方法了;- 使用
Object.defineProperty
增加__ob__
;
Object.defineProperty(value,'__ob__',{
enumerable:false,
configurable:false,
value:this
});
试想下如果没有设置不可枚举,那么在递归进行响应式检测的时候就会无限循环了;所以要排除掉添加了__ob__
的检测对象;
写在最后
你有感觉到'卧槽'了么?
参考文章;