Vue响应式原理

105 阅读2分钟

vue是如何实现响应式的? 随之而来便有两个问题:

  • vue是如何实现监听实例中的属性的变化的
  • 当数据发生变化时,vue是如何做到通知相应的组件,并使界面实现刷新

vue是如何实现监听实例中的属性的变化的

vue通过设置Object.defineProperty() 来监听对象属性的改变。

我们不妨回到一个最简单的例子

    <div id="app">
        {{message}}
    </div>

    <script>
        const app = new Vue({
            el:'app',
            data:{
                message:'我是data'
            }
        })
    </script>

此时页面是这样监听message的:

  1. 先在内部拿到一个对象obj(内容即data中的内容)
  2. 然后通过 Object.defineProperty对原本data中的属性进行重新定义具体操作如下
    Object.keys(obj).forEach(key =>{
        // 将obj中的每一个属性(key)中的值赋值到value中
        let value = obj[key]
        // 使用defineProperty将每个value在obj中进行定义,一旦数值发生改变,便会调用set函数
        Object.defineProperty(obj,key,{
            set(newValue){
                value = newValue
            },
            get(){
                return value
            }
        })
        
    })

当数据发生变化时,vue是如何做到通知相应的组件,并使界面实现刷新

vue通过发布订阅者模式,实现通知。所谓发布订阅者模式,就是谁在使用特定属性(通过解析html文件得知),该属性更新时就会通知该组件(使用get函数获取最新的数值),该模式的大致实现思路如下:

// 发布者
    class dep{
        constructor(){
            this.subs=[]
        }
        // 添加订阅者
        addSub(watcher){
            this.subs.push(watcher)
        }
        // 通知订阅者进行更新
        notify(){
            this.subs.forEach(item =>{
                item.update()
            })
        }
    }
    // 订阅者
    class watcher{
        constructor(name){
            this.name = name;
        }

    //更新函数
        update(){

        }
    }


    const dep = new dep();
    const w1 = new watcher('张三')
    dep.addSub(w1)

了解完响应式原理实现的两大重要技术后,我们重温一下响应式的原理图

原理图

我们在生成一个实例之后,里面的数据就换传入observer中(通过defineProperty实现),里面的每一个key都将会对应生成一个dep对象。在解析的时候,每解析到一个key时,都会生成一个watcher并将其添加到对应dep对象中的subs数组中(发布订阅者模式),而当某个key的值发生改变的时候,便会调用该key对应的dep对象中的notify函数,并通知该dep的所有订阅者调用update函数,最后更新视图,实现响应式。