深入浅出VUE之双向数据绑定

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来通知自己是否需要发生变化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值