平凡之路。

Vue.js响应式原理

Date: Author: ZJ

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。

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

Vue.js响应式原理

将数据data变成可观察的

通过Object.defineProperty简单实现数据观察。

function observer(obj, cb) {
    if (!obj || typeof obj !== 'object') {
        return;
    }
    Object.keys(obj).forEach(key => {
        defineReactive(obj, key, obj[key], cb)
    });
}

function defineReactive(obj, key, val, cb) {
    // 递归,监测子属性
    observer(val);
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        // 数据劫持
        get : () => {
            return val
        },
        set : newVal => {
            cb();
        }
    })
}

class Vue {
    constructor(option) {
        this._data = option.data;
        observer(this._data, option.render);
    }
}

let vm = new Vue({
    data: {
        text: 'hello, world'
    },
    render() {
        console.log('isRender');
    }
})
vm._data.text = 'change'; // isRender

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

代理

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

// 构造函数中加入
_proxy(options.data, this);

// proxy函数实现

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

这样就可以用vm.text来代替vm._data.text


对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。