总结一下,以便忘了回头看看

响应式数据

主要用到Object.defineProperty这个API,改写对象属性的get属性和set属性。

function defineReactive(obj, key, value) {
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: () => {
            console.log(`${key}属性被读`)
            return value
        },
        set: (newValue) => {
            if (value === newValue) {
                return
            }
            value = newValue
            console.log('数据被改变了,我要渲染新的值')
        }
    })
}
let obj = {
    name: '小明'
}
defineReactive(obj, 'name', obj.name)
console.log(obj.name)// name属性被读 小明
obj.name = '小豪' //数据被改变了,我要渲染新的值

至于监听对象里的所有属性,就是循环一下就可以了

function observe(data) {
    const keys = Object.keys(data)
    keys.forEach(key => {
        defineReactive(data, key, data[key])
    })
}

Dep--依赖管理

我们通过defineReactive方法将data中的数据进行响应式后,虽然可以监听到数据的变化了,那我们怎么处理通知视图就更新呢?Dep就是帮我们收集【究竟要通知到哪里的】。比如下面的代码案例,我们发现,虽然data中有text和message属性,但是只有message被渲染到页面上,至于text无论怎么变化都影响不到视图的展示,因此我们仅仅对message进行收集即可,可以避免一些无用的工作。那这个时候message的Dep就收集到了一个依赖,这个依赖就是用来管理data中message变化的。

<div>
    <p>{{message}}</p>
</div>
data: {
    text: 'hello world',
    message: 'hello vue',
}

Dep主要用于收集三个地方的依赖,一个是data(就是上面的例子),一个是computed,一个是watch,比如下面的代码

computed: {
    messageT() {
        return this.message + ‘!’;
    }
}
watch: {
    message: function (val, oldVal) {
        console.log('new: %s, old: %s', val, oldVal)
    },
}   

图片说明

图中的data、computed、watch被我们称之为Watcher

收集依赖

所谓的依赖是什么呢,就是Watcher。Watcher扮演的是一个类似于中介的角色,比如message就有三个中介,当message变化时,就通知这三个中介,让他们去执行各自需要做的变化。所以Watcher里应该主要有两个方法,一个是通知变化,一个是被收集到Dep中去。

function Watcher() {
    this.addDep = function () {

    }
    this.update = function () {

    }
}

总结

回顾一下,Vue响应式原理的核心就是Observer、Dep、Watcher。Observer中进行响应式的绑定,在数据被读的时候,触发get方法,执行Dep来收集依赖,也就是收集Watcher。在数据被改的时候,触发set方法,通过对应的所有依赖(Watcher),去执行更新。比如watch和computed就执行开发者自定义的回调方法。