本文是受当面试官问你Vue响应式原理,你能够这么回答他
启发自行绘制的vue动态响应原理图解 画风诡异,仅表明我的理解若是有不当的地方,望请斧正。vue
官方大图
面试
JserWang的原理代码,为了看懂我自行添加了好多注释与console。bash
//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 get
console.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 get
console.log('data set调用-----------------------------------------------------------------');
vue._data.text = 'aaaaa';
console.log(vue._data.text);
console.log(vue._data.text1);
console.log(vue._data.text2);
复制代码