本文是受
启发自行绘制的vue动态响应原理图解 画风诡异,仅代表个人理解如果有不当的地方,望请斧正。官方大图
我的见解JserWang的原理代码,为了看懂我自行添加了好多注释与console。
//3.const Observer = function(data) { // 循环修改为每个属性添加get set for (let key in data) { console.log("给value的没一个key设置一个收集器"); defineReactive(data, key); }}//4.const defineReactive = function(obj, key) { // 局部变量dep,用于get set内部调用 console.log("创建收集器***********"); const dep = new Dep(); // 获取当前值 let val = obj[key]; console.log("observe 开始重写data每个属性的get/set"); Object.defineProperty(obj, key, { // 设置当前描述属性为可被循环 enumerable: true, // 设置当前描述属性可被修改 configurable: true, get() { console.log('observe get触发'); // 调用依赖收集器中的addSub,用于收集当前属性与Watcher中的依赖关系 console.log("observe dep.depend收集当前属性和watcher的依赖关系"); console.log("当^^^^^^^^^^^^^^^^^^^^^^^^前属性:", key); dep.depend(); return val; }, set(newVal) { if (newVal === val) { console.log("observe set 设置属性值未变化"); return; } val = newVal; // 当值发生变更时,通知依赖收集器,更新每个需要更新的Watcher, // 这里每个需要更新通过什么断定?dep.subs console.log("observe 设置属性值变化通知依赖收集器更新watcher"); dep.notify(); } });}const observe = function(data) { return new Observer(data);}//1.const Vue = function(options) { const self = this; self.a = 1; self.b = 1; // 将data赋值给this._data,源码这部分用的Proxy所以我们用最简单的方式临时实现 if (options && typeof options.data === 'function') { console.log("Vue 将传入配绑定给新建的vue对象:", this); this._data = options.data.apply(this); } // 挂载函数 this.mount = function() { let tmp = self.a; self.a += 1; console.log("Vue 挂载watcher实例到当前vue对象", tmp); new Watcher(self, self.render); } // 渲染函数 this.render = function() { let tmp = self.b; self.b += 1; console.log("Vue 触发渲染vue对象", tmp); console.log('Vue 看该属性是否被组件引用,引用则重新渲染'); // with(self) { // _data.text;//获取当前的data中的属性 // _data.a; // _data.b; // } console.log(self._data.text); console.log(self._data.text1); console.log(self._data.text2) } // 监听this._data //2. console.log("Vue 设置观察对象, 重写set、get"); observe(this._data); }const Watcher = function(vm, fn) { const self = this; //保存传入的data对象 this.vm = vm; // 将当前Dep.target指向当前watcher console.log("Watcher 将当前Dep.target指向当前watcher"); console.log("-----------------------临时关联watcher----------------------------"); Dep.target = this; // 向Dep方法添加当前Wathcer this.addDep = function(dep) { console.log("watcher向当前收集器dep添加当前Wathcer"); dep.addSub(self); } // 更新方法,用于触发vm._render this.update = function() { console.log("watcher 触发render"); fn();//vue.render } // 这里会首次调用vm._render,从而触发text的get // 从而将当前的Wathcer与Dep关联起来 //vue.render 首次渲染 console.log("-----------------------首次渲染----------------------------"); this.value = fn(); // 这里清空了Dep.target,为了防止notify触发时,不停的绑定Watcher与Dep, // 造成代码死循环 console.log("————————————————————清空Dep.target"); Dep.target = null;}//5.const Dep = function() { console.log("Dep新建收集器"); const self = this; // 收集目标 this.target = null; // 存储收集器中需要通知的Watcher this.subs = []; // 当有目标时,绑定Dep与Wathcer的关系 this.depend = function() { //确认实例是否绑定watcher if (Dep.target) { // 这里其实可以直接写self.addSub(Dep.target), // 没有这么写因为想还原源码的过程。 console.log("Dep向当前收集器dep添加当前Wathcer"); Dep.target.addDep(self); } } // 为当前收集器添加Watcher this.addSub = function(watcher) { console.log("Dep addSub为当前收集器添加watcher"); self.subs.push(watcher); } // 通知收集器中所的所有Wathcer,调用其update方法 this.notify = function() { console.log("Dep 通知所有的watcher 触发更新"); for (let i = 0; i < self.subs.length; i += 1) { self.subs[i].update(); console.log(`第${i}个watcher`, self.subs[i]); } }}const vue = new Vue({ data() { return { text: 'hello world', text1: "aaa", text2: "333" }; }})console.log("触发挂载-----------------------");vue.mount(); // in getconsole.log(vue._data.text);console.log(vue._data.text1);console.log(vue._data.text2);console.log('data set调用------------------------------------------------------');vue._data.text = '123'; // in watcher update /n in getconsole.log('data set调用-----------------------------------------------------------------');vue._data.text = 'aaaaa';console.log(vue._data.text);console.log(vue._data.text1);console.log(vue._data.text2);复制代码