Vue响应式原理

我们知道Vue的响应式,即当我们在控制台通过类似于app.message='hello'的操作, 修改挂载到app上data中message属性的值,就可以修改当前视图所显示的值。那Vue为什么可以做到这种响应式呢?我们第一时间就会想到对该属性进行监听,那么Vue内部具体是怎么完成的呢?
vue官方图片

如何监听?

当new Vue()之后,初始化时Observer(观察者)调用Object.defineproperty方法对data中每个属性进行数据劫持和监听,并为data中的每个属性创建一个Dep对象(依赖对象)。Dep对象中有一个由所有该对象的订阅者组成的数组。这样我们就可以知道该属性都是谁在使用。

什么是订阅者(Watcher)?

在解析el模板指令时,每个模板指令都回调需要展示的属性的get方法,为每条指令创建一个Watcher实例。然后该属性的Dep对象调用添加订阅者方法(例如:addSubs)将订阅者实例添加到Dep的订阅者数组中。【Watcher的个数与模板中表达式一一对应】

知道了属性如何被监听,被谁监听。那么修改属性的值如何使视图中的值也随之改变?

Object.defineProperty方法中为每个data属性定义了一个set方法一个get方法,set用于对值的更新,get用于watcher创建完回调函数渲染视图。所以当监听到属性的值发生改变时,就调用set方法将新的Value赋值给旧的值,并且调用dep.notify()的方法,遍历该Dep对象中的订阅者数组,使得每个订阅者调用update()方法,updata就是对视图进行更新。

还是看代码
<script>
    const obj = {
        message1: "1",
        message2: "2"
    } 

    Object.keys(obj).forEach(key => {
        let value = obj[key]
        Object.defineProperty(obj, key, {
            set(newValue){
                value = newValue
                dep.notify()
            },
            get(){
                  const dep = new Dep()
                return value
            }
        })
    })

    class Dep {
        constructor(){
            this.subs = [] //记录谁订阅了属性
        }
        addSub(watcher){
            this.subs.push(watcher)
        }
        notify(){
            this.subs.forEach(item => {
                item.update()
                /*
                为什么item可以调用update方法,因为每个item元素
                都是一个Watcher对象实例
                */
            })
        }
    }

    class Watcher{
        constructor(name){
            this.name = name
        }

        update(){

        }
    }

    const dep = new Dep() //拦截之后为每个属性创建一个Dep
    const watcher1 = new Watcher()  //解析模板指令后,每个指令都是一个Watcher对象
    dep.addSub(watcher1)   /*将对象加入订阅者数组,
    就可以知道哪些对象监听了哪个属性,当属性发生改变新的value被赋值,
    就调用notify()遍历这个属性的所有订阅者,每个订阅者都进行update。
    最后视图也被更新,实现响应式   */

</script>