vue会自动通过状态生成DOM,并输出到页面上显示出来,这个过程叫渲染,Vue.js的渲染过程是声明式的,我们可以通过模板描述动态与DOM之间的关系
变化桢测就是来解决这个问题的 他的两个类型为 ‘推(push)’ ,‘拉(pull)’
react 的变化桢测是属于 ‘拉’ ,这就是说当状态发生变化时,他不知道那个状态变了,只知道状态有可能变了,然后发送一个信号告诉框架,框架收到信号后,会进行一个
暴力的比对来找出哪些DOM节点需要重新渲染,在react中使用的虚拟DOM
而 ‘VUE.JS’ 的变化桢测属于 ’推‘,当状态发生变化时,vue.js立刻就知道,而且在一定程度上知道哪些状态变了
因此,他知道更多的信息,也可以跟细粒度的更新
Object.defineProperty 或者ES6 的 proxy
```javascript
// 封装 Objet.defineProperty
function defineReactive (data, key, val) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function () {
return val;
},
set: function (newVal) {
if (val === newVal) {
return ;
}
val = newVal;
}
});
}
// 收集依赖
/**
* 模板使用的数据等同于组件使用数据,所以当数据发送变化时,会将通知发送到组件,然后组件
* 内部在通过虚拟DOM重新渲染
*
* 如何收集依赖。
* 先收集依赖,即把用到数据那么的地方收集起来,
* 然后等属性发生变化时,把之前收集的依赖循环触发一遍
* 总结: 在getter中收集依赖,在setter中触发依赖
*/
// 定义依赖容器
```javascript
```javascript
class Dep {
constructor () {
this.subs = [];
}
addSub (sub) {
this.subs.push(sub);
}
removerSub (sub) {
remove(this.subs, sub);
}
depend () {
if (window.target) {
this.addSub(window.target);
}
}
notify(){
const subs = this.subs.slice();
for (let index = 0; index < subs.length; index++) {
subs[i].update()
}
}
}
function remove (arr, item) {
if (arr.length) {
const index = arr.index(item);
if (index > -1) {
return arr.splice(index, 1);
}
}
}
function defineReactive2 (data, key , val) {
let dep = new Dep(); // 收集依赖
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function () {
dep.depend()
return val;
},
set: function (newVal) {
if (val === newVal) {
return ;
}
dep.notify()
val = newVal;
}
});
}
//定制window.target
/**
* window.target = watcher.target
* 上面的收集是收集的谁 当属性发生改变后 通知谁? 通知watcher
* watcher 是一个中介的角色
* 经典使用方式 vm.$watch(a,b,c, function(newVal, oldVal){
* //执行代码块
* })
*/
```javascript
class Watcher{
constructor (vm, exporFn, cb) {
this.vm = vm;
// 执行this.getter() 就可以直接读取a,b,c的内容
this.getter = parsePath(exporFn)
this.cb = cb;
this.value = this.get();
}
get () {
window.target = this; //将window.target 设置为this 也就是Watcher实列
let value = this.getter.call(this.vm, this.vm); // 触发getter,触发收集依赖的逻辑 读取一个依赖到Dep中
window.target = undefined;
return value;
}
update () {
const oldValue = this.value;
this.value = this.get();
this.cb.call(this.vm, this.value, oldValue);
}
}
- 整体执行逻辑
- 因为我在get方法中先把window.target设置成this,也就是当前的watcher实列,然后再去读data.a,b,c的值,这肯定会触发getter
- 触发了getter,就会触发收集依赖的逻辑,而关于收集依赖,会从widow.target中读取一个依赖并添加到Dep中。
- 这就导致,只要先在window.target赋一个this,然后再读一个值,去触发getter,就可以把this主动添加到keypath的Dep中
- 依赖注入到Dep中后,每当data.a.b.c的值发生变化后,就会让依赖列表中所有的依赖循环调用update方法,也就是watcher中的update方法。而update方法会执行参数中的回调方法,将value, oldValue
- 其实不管用户执行的是vm.$wach 还是模板中用到的data 都是通过Watcher来通知自己是否需要发生变化