Vue2 深度响应
windliang 人气:01、场景
import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { text: { innerText: { childText: "hello", }, }, }; observe(data); const updateComponent = () => { console.log(data.text.innerText.childText); }; new Watcher(updateComponent); data.text.innerText.childText = "liang";
我们的响应式系统到现在还没有支持属性是对象时候的响应,因此我们改变 的时候不会有任何输出。childText
我们只收集了 的依赖,所以如果想要响应的话必须给 整个赋值为一个新对象。data.text
data.text
import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { text: { innerText: { childText: "hello", }, }, }; observe(data); const updateComponent = () => { console.log(data.text.innerText.childText); }; new Watcher(updateComponent); data.text = { innerText: { childText: "liang", }, };
我们当然不希望每次都赋值整个对象,我们需要做一些修改,把嵌套的对象也变成响应式的。
2、方案
我们只需要在给某个 重写 和 之前,把它的 就像上边给 调用 函数一样,也调用一次 函数即可。key
get
set
value
data
observe
observe
同时提供 参数,留下扩展,让外界决定是否需要深度响应。shallow
/*******************新增 shallow*******************/ export function defineReactive(obj, key, val, shallow) { /****************************************************/ const property = Object.getOwnPropertyDescriptor(obj, key); // 读取用户可能自己定义了的 get、set const getter = property && property.get; const setter = property && property.set; // val 没有传进来话进行手动赋值 if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } const dep = new Dep(); // 持有一个 Dep 对象,用来保存所有依赖于该变量的 Watcher /*******************新增****************************/ !shallow && observe(val); /******************************************************/ Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { const value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); } return value; }, set: function reactiveSetter(newVal) { const value = getter ? getter.call(obj) : val; if (setter) { setter.call(obj, newVal); } else { val = newVal; } dep.notify(); }, }); }
同时,在 函数中,传进来的 不是对象的话我们直接 。observe
value
return
/* util.js export function isObject(obj) { return obj !== null && typeof obj === "object"; } */ export function observe(value) { if (!isObject(value)) { return; } let ob = new Observer(value); return ob; }
3、场景2
import { observe } from "./reactive"; import Watcher from "./watcher"; const data = { text: { innerText: { childText: "hello", }, }, }; observe(data); const updateComponent = () => { console.log(data.text.innerText.childText); }; new Watcher(updateComponent); data.text.innerText.childText = "liang"; data.text = { innerText: { childText: "liang2", }, }; data.text.innerText.childText = "liang3";
可以一分钟想一下上边会输出什么。
new Watcher(updateComponent);
,执行一次 输出 。updateComponent
hello
data.text.innerText.childText = "liang";
,我们已经解决了属性是对象的情况,因此这里也会输出 。liang
data.text = { innerText: { childText: "liang2", }, };
上边代码就是文章最开头的方法,因此也会触发函数执行,输出 。liang2
data.text.innerText.childText = "liang3";
最后这句会执行吗?
答案是否定的了,因为我们的 赋值为了一个新对象,但这个新对象我们并没有将其设置为响应式的。data.text
因此我们需要在 的时候把对象也设置为响应式的。set
/** * Define a reactive property on an Object. */ export function defineReactive(obj, key, val, shallow) { const property = Object.getOwnPropertyDescriptor(obj, key); // 读取用户可能自己定义了的 get、set const getter = property && property.get; const setter = property && property.set; // val 没有传进来话进行手动赋值 if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } const dep = new Dep(); // 持有一个 Dep 对象,用来保存所有依赖于该变量的 Watcher let childOb = !shallow && observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { const value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); } return value; }, set: function reactiveSetter(newVal) { const value = getter ? getter.call(obj) : val; if (setter) { setter.call(obj, newVal); } else { val = newVal; } /******新增 *************************/ childOb = !shallow && observe(newVal); /************************************/ dep.notify(); }, }); }
4、总结
通过递归解决了属性是对象的依赖,可以为未来数组的依赖留下基础。
加载全部内容