Vue之响应式原理

119 阅读1分钟

关于Vue.js

Vue.js是一款MVVM框架,通过响应式在修改数据的时候更新视图。Vue.js的响应式原理依赖于 Object.defineProperty,尤大大在Vue.js文档中就已经提到过,这也是Vue.js不支持IE8 以及更低版本浏览器的原因。Vue通过设定对象属性的 setter/getter 方法来监听数据的变化,通过getter进行依赖收集,而每个setter方法就是一个观察者,在数据变更的时候通知订阅者更新视图。

将数据data变为可观察(observable)

那么Vue是如何将所有data下面的所有属性变成可观察的(observable)呢?

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

function defineReactive(obj, key, val, cb) {
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: () =>{
            return val;
        },
        set: newVal =>{
            val = newVal;
            cb();
        }
    })
}

class Vue {
    constructor(options){
        this._data = options.data;
        observe(this._data,options.render)
        _proxy.call(this,options.data)
    }
}

/*
*   注意:new Vue()的实例对象的数据属性名为_data
*/ 
let app = new Vue({
    el: '#app',
    data: {
        text: 'hahaha',
        text2: 'xixixi'
    },
    render(){
        console.log('---render---');
    }
})

当_data数据发生改变的时候就会触发set,对订阅者进行回调(在这里是render)。

那么问题来了,需要对app._data.text操作才会触发set。为了偷懒,我们需要一种方便的方法通过app.text直接设置就能触发set对视图进行重绘。那么就需要用到代理。

代理

我们可以在Vue的构造函数constructor中为data执行一个代理proxy。这样我们就把data上面的属性代理到了vm实例上。

function _proxy(data) {
    const that = this;
    Object.keys(data).forEach((key)=>{
        Object.defineProperty(that,key,{
            enumerable: true,
            configurable: true,
            get: () =>{
                return that._data[key];   
            },
            set: newVal =>{
                that._data[key] = newVal;
            }
        })
    })
}

我们就可以用app.text代替app._data.text了。