VUE之object变化侦测

1.什么是变化侦测

Vue会自动通过状态生成DOM,并将其输出到页面上显示出来,这个过程叫渲染。Vue的渲染过程是声明式的,我们通过模板来描述状态与DOM之间的映射关系。
通常,在运行时应用内部的状态是不断发生变化的,此时需要不停地重新渲染。这时如何确定状态中发生了什么变化?变化侦测就是来解决这个问题的,它分为两种形式,一种是推(push)一种是拉(pull)。
vue的变化侦测属于推。当状态发生变化时,Vue立刻就知道了,而且在一定程度上知道哪些状态发生变化了。因此,它知道的信息更多,也就可以进行更深粒度的更新。

2.如何追踪变化

如何侦测变化?
.在js中,可以使用Object.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;
        }
    })
}

这里的函数defineReactive用来对Object.defineProperty进行封装。从函数的名字可以看出,其作用是定义一个响应式数据。也就是在这个函数中进行变化追踪,封装后只需要传递data、key、value 即可。

3.如何收集依赖

如果只是把Object.defineProperty进行封装,那其实并没有什么作用
,其实真正有用的是收集依赖:
先看一个例子:

<template>
	<h1>{{name}}</h1>
</template>

这个例子中使用了数据name,所以当它发生变化时,要向使用了它的地方发送通知。
先收集依赖,把用到数据name的地方收集起来,然后等属性发生变化时,把用到数据name的地方收集起来,然后等属性发生变化时把之前收集好的依赖循环触发一遍。
即在getter中收集依赖,在setter中触发依赖。

4.依赖收集在哪里

假设依赖是一个函数,保存在window.target中,就可以把defineReactive函数稍微改造一下:

function defineReactive(data, key, val){
    let dep = [];//new
    Object.defineProperty(data, key ,{
        enumerable : true,
        configurable : true,
        get : function () {
            dep.push(window.target)//new
            return val;
        },
        set : function (newVal) {
            if (val == newVal){
                return;
            }
            //新增
            for(let i = 0; i < dep.length; i++){
                dep[i](newVal, val)
            }
            val = newVal;
        }
    })
}

这里新增了数组dep,用来储存被收集的依赖。
然后在set被触发时,循环dep用来储存被收集的到的依赖、然后在set被触发时,循环dep以触发收集到的依赖。
也可以将依赖收的代码封装成一个dep类,它专门帮我们管理依赖。使用这个类,我们可以收集依赖、删除依赖或向依赖发送通知等。

export default class Dep {
    constructor() {
        this.subs = [];
    }
    addSub(sub){
        this.subs.push(sub)
    }
    removeSub(sub){
        remove(this.subs.sub)
    }
    depend(){
        if (window.target){
            this.addSub(window.target)
        }
    }
    notify(){
        const subs = this.subs.slice()
        for (let i = 0, l = subs.length; i < l; i++){
            subs[i].update()
        }
    }
}
function remove(arr, item){
    if (arr.length){
        const index = arr.indexOf(item)
        if (index >= -1){
            return arr.splice(index, 1)
        }
    }
}
function defineReactive(data, key, val){
    let dep = new Dep();//new
    Object.defineProperty(data, key ,{
        enumerable : true,
        configurable : true,
        get : function () {
            dep.depend()//new
            return val;
        },
        set : function (newVal) {
            if (val == newVal) {
                return;
            }
            val = newVal;
            dep.notify();//new
        }
    })
}

5.依赖是谁

在上面的代码中,我们收集的依赖是window.target,那么它到底是什么?究竟要研究什么?
收集谁,也就是当属性发生变化后通知谁。
我们要通知用到的数据的地方,而使用这个数据的地方有很多,而且类型还不一样,既有可能是模板,也有可能是用户写的一个watch,这时需要抽象出一个能集中处理这些情况的类。然后,在依赖收集阶段只收集这个封装好的类的实例进来,通知也只通知它一个,接着,它再负责通知其他地方。

本文参考:深入浅出Vue.js/刘博文著

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值