VUE响应式原理-如何追踪变化
Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接
如何追踪变化
当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data
选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty
把这些 property 全部转为 getter/setter。Object.defineProperty
是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <script type="text/javascript"> let oldArrPrototype = Array.prototype; // 继承 let proto = Object.create(oldArrPrototype); ['push', 'shift', 'unshift','splice'].forEach(method => { proto[method] = function() { // 切片编程 // 更新视图 updateView(); oldArrPrototype[method].call(this, ...arguments) } }) // 观察函数 function observer(target) { if (typeof target !== 'object' || target === null) { return target; } if (Array.isArray(target)) { target.__proto__ = proto; // target.constructor.prototype = proto; } for (let key in target) { /* target:对象本身 key:对象的属性 target[key]:对象的值 */ definedReactive(target, key, target[key]) } } // defineProperty函数 function definedReactive(target, key, value) { // 重新观察一下传过来的值 observer(value); //递归 Object.defineProperty(target, key, { // get 读取属性值触发 get() { return value; }, // 修改属性值触发,默认有参数,是最新值 set(newValue) { // 为了方式更改后的值也是对象 observer(newValue); // 判断新值和旧值是否相同 // 如果不同,改变旧值,更新视图 if (newValue !== value) { value = newValue; updateView(value); } else { return newValue; } } }) } // 更新视图方法 function updateView(value) { console.log("更新视图" + value) } // 使用Object.defineProperty可以给属性重新定义属性,给属性增加getter,setter // 这样给属性增加或者读取属性是都会触发getter 或者 setter let data = { name: "oldValue", onj: { name: "szx" }, a: null, n:[1,2,3] } // 定义一个观察函数,只要数据发生变动会触发更新视图的方法 observer(data) // data.name = "newValue" // data.a = { // name: "100" // }, // data.a.name = 200 data.n.splice(0) console.log(data) // 如果属性不存在,即没有事先在data里面定义,赋值不存在的属性值的时候不会触发更新 </script> </body> </html>