前言
本文仅以记录自己的学习过程,有其他理解的同学可留言。注意 我学习原理一直保持 28 策略 所谓百分之 20 的代码实现了百分之 80 的功能 所以此系列咱们只关心核心逻辑以及功能的实现
正文
上篇文章介绍了Vue的初次渲染流程,本篇就记录、分析下更新渲染的流程
入口
通过上篇文章可以知道初次渲染的过程,那么在实例化之后更改数据如何更新视图,Vue可没让我们改变数据后,调用this._update(this._render()); 我们学习vue源码的都知道在Object.defineProperty接口中,get时添加依赖,set时通知更新,也就是 deps.forEach(watcherObj => watcherObj.update());下面先看下watcher
Wathcer;vue最核心、代码最美妙的地方
// 之所以说时最核心、代码最美妙的地方,因为Watcher是data computed watch的核心,一个类实现了这几块的功能,有时候学了一块,我就想其他块是如何实现,再去看源码 so beautiful !!!
// 参数解析
// expOrFn: 路径表达式/computed的函数
// callback: 回调,渲染wathcer为更新视图的方法 / watch中的方法
// options 额外的选项 true代表渲染watcher
// 渲染wathcer的options: true
// watch的options:{ user: true, deep: boolean, immediate: boolean}
// computed的options:{lazy: true, ...}
let id = 0;
class Watcher {
constructor(vm, exprOrFn, cb, options) {
this.vm = vm;
this.exprOrFn = exprOrFn;
this.cb = cb;
this.options = options; //
this.id = id++; // watcher的唯一标识
this.deps = []; // 存储 有此订阅者实例的 deps
this.depsId = new Set();// 存储 有此订阅者实例的 deps的id属性值
// 如果表达式是一个函数
if (typeof exprOrFn === "function") {
this.getter = exprOrFn;
}
// 实例化就会默认调用get方法
this.get();
}
get(){
// pushTarget popTarget是用模拟栈的方式来讲watcher实例暴露出来;
// 后入先出,防止当前已有watcher实例暴露出来,而被本次操作给覆盖;
pushTarget(this);
this.getter();
popTarget();
}
// 属性的let deps = new Dep()还记得吗,deps中存储了订阅者Wathcer实例
// 于此同时 Watcher实例中也存储了哪些deps存储了我(Watcher实例),是以多对多的形式存在的
// 这样才可以达到去重的效果
addDep(dep) {
if (!this.depsId.has(dep.id)) {
this.depsId.add(dep.id);
this.deps.push(dep);
dep.addSub(this);
}
}
update() {
this.cb();
// 这一块还会改,后面会结合 实现异步更新、computed
}
}
创建渲染Watcher
export function mountComponent(vm, el) {
// _update和._render方法都是挂载在Vue原型的方法 类似_init
// 引入watcher的概念 这里注册一个渲染watcher 执行vm._update(vm._render())方法渲染视图
let updateComponent = () => {
console.log("刷新页面");
vm._update(vm._render());
};
// 第二个参数是默认执行的,第三个参数是回调,数据改变时触发set会执行updateComponent更新视图
new Watcher(vm, updateComponent, updateComponent, true);
}
依赖收集Dep
// src/observer/dep.js
// dep和watcher是多对多的关系
// 每个属性都有自己的dep
let id = 0; //dep实例的唯一标识
export default class Dep {
constructor() {
this.id = id++;
this.subs = []; // 这个是存放watcher的容器
}
depend() {
// 如果当前存在watcher
if (Dep.target) {
Dep.target.addDep(this); // 把自身-dep实例存放在watcher里面
}
}
notify() {
// 依次执行subs里面的watcher更新方法
this.subs.forEach((watcher) => watcher.update());
}
addSub(watcher) {
// 把watcher加入到自身的subs容器
this.subs.push(watcher);
}
}
// 默认Dep.target为null
Dep.target = null;
// 栈结构用来存watcher
const targetStack = [];
export function pushTarget(watcher) {
targetStack.push(watcher);
Dep.target = watcher; // Dep.target指向当前watcher
}
export function popTarget() {
targetStack.pop(); // 当前watcher出栈 拿到上一个watcher
Dep.target = targetStack[targetStack.length - 1];
}
思维导图
如果觉得本文对你有帮助,记得点赞、收藏、评论,十分感谢!