共计 2050 个字符,预计需要花费 6 分钟才能阅读完成。
写文章不容易,点个赞呗兄弟
专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue 版本 【2.5.17】
如果你觉得排版难看,请点击 下面链接 或者 拉到 下面 关注公众号 也可以吧
【Vue 原理】依赖更新 – 源码版
如果对依赖收集完全没有概念的同学,可以先看我这篇白话版
响应式原理 – 白话版
我们已经讲过了 依赖收集
【Vue 原理】依赖收集 – 源码版之基本数据类型
【Vue 原理】依赖收集 – 源码版之引用数据类型
现在就要看依赖更新了哈哈哈,毕竟收集完是要更新的嘛
其实依赖更新挺简单的,就是两步
修改属性值
通知保存的依赖进行更新
重点只需要看 Object.defineProperty 设置的 set 函数,当给数据重赋新值的时候,自然会触发 set 函数,完成依赖更新
function defineReactive(obj, key, val) {var dep = new Dep();
var childOb = observe(val);
Object.defineProperty(obj, key, {get(){
... 属性被读取,完成依赖收集
// 返回闭包值
return val
},
set(newVal) {
// 值没有变化
if (newVal ===val) return
// 修改闭包值
val = newVal;
// 如果属性已经存在过,设置新值的时候,会重新调用一遍
childOb = observe(newVal);
// 触发更新
dep.notify();}
});
}
依赖更新重点就重在 通知更新
而通知更新的重点,只有一句话,【dep.notify】
所以,我们重点去了解这句话,如何通知,如何更新
好的,dep 在第一篇讲过了
【Vue 原理】依赖收集 – 源码版之基本数据类型
我们知道,dep 主要是存储依赖的,再看一遍源码
var Dep = function Dep() {this.subs = []; // 依赖存储器
};
// 遍历 subs,逐个通知依赖,就是逐个调用 watcher.update
Dep.prototype.notify = function() {var subs = this.subs.slice();
for (var i = 0, l = subs.length; i < l; i++) {subs[i].update();}
};
看过了源码,我们知道了,原来通知更新是【遍历依赖存储器】,然后一个个【调用 watcher.update】
因为 subs 装的是 watcher,所以,subs[0].update 就是 watcher.update
于是问题又来了,watcher.update 是怎么就更新了???
function Watcher(vm, expOrFn) {
this.vm = vm;
// 保存传入的更新函数
this.getter = expOrFn;
// 新建 watcher 的时候,立即执行更新函数
this.get();};
Watcher.prototype.get = function() {
// 执行更新函数
this.getter.call(this.vm,this.vm);
};
Watcher.prototype.update = function() {this.get()
}
看到上面的源码
1Watcher 新建实例的时候,会保存传入的函数(这个函数会作为更新用)
2watcher 实例有 update 方法,作用是执行上一步保存的更新函数
那么 watcher 是什么时候开始创建的呢?
以页面 watcher 举例,探索整个实例构建的基本流程
function Vue(options) {this._init(options);
}
Vue.prototype._init = function(options) {
// ... 处理组件选项等
this.$mount()}
Vue.prototype.$mount = function() {
// ... 解析 template 成 redner 函数保存
/** 每个实例新建一个 watcher,并且利用 watcher 保存更新函数 **/
new Watcher(this,
// 这个函数是更新函数,传入 watcher 保存下来,用于后面页面初始化或者页面更新
function() {
/** ... 调用保存的渲染函数生成 VNode,并生成 DOM 插入页面中 **/
}
);
};
看上面的源码 和注释大概就可以很清楚了
从【new Vue】到【vm._init】初始化 到【vm.$mount】挂载到页面,整个流程就完整了
重点是清楚 watcher 的更新函数
更新函数
我们可以看到这个页面的更新函数,作用是调用 渲染函数,然后生成 DOM 节点插入页面中。
更新函数会传入 Watcher,然后被保存到 watcher 的实例中
“整个函数涉及的源码很多,但是这里一律而过”
所以,通知更新做了这些工作
1、直接调用 watcher.update,也就是重新调用给 watcher 保存的更新函数
2、更新更新函数就是执行渲染函数,然后读取实例最新的值(已被修改过的值),最后重新生成 DOM 节点
3、DOM 节点 插入或替换页面,完成更新
画个通知流程图