V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
answershuto
V2EX  ›  Vue.js

Vue.js 响应式原理

  •  2
     
  •   answershuto ·
    answershuto · 2017-08-21 13:00:21 +08:00 · 2083 次点击
    这是一个创建于 2647 天前的主题,其中的信息可能已经有所发展或是发生改变。

    写在前面

    因为对 Vue.js 很感兴趣,而且平时工作的技术栈也是 Vue.js ,这几个月花了些时间研究学习了一下 Vue.js 源码,并做了总结与输出。 文章的原地址:answershuto/learnVue。 在学习过程中,为 Vue 加上了中文的注释 answershuto/learnVue,希望可以对其他想学习 Vue 源码的小伙伴有所帮助。 可能会有理解存在偏差的地方,欢迎提 issue 指出,共同学习,共同进步。

    关于 Vue.js

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

    将数据 data 变成可观察( observable )的

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

    function observer(value) {
        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: ()=>{
                /*....依赖收集等....*/
            },
            set:newVal=> {
                cb();/*订阅者收到消息的回调*/
            }
        })
    }
    
    class Vue {
        constructor(options) {
            this._data = options.data;
            observer(this._data, options.render)
        }
    }
    
    let app = new Vue({
        el: '#app',
        data: {
            text: 'text',
            text2: 'text2'
        },
        render(){
            console.log("render");
        }
    })
    

    为了便于理解,首先考虑一种最简单的情况,不考虑数组等情况,代码如上所示。在initData中会调用observe这个函数将 Vue 的数据设置成 observable 的。当_data 数据发生改变的时候就会触发 set,对订阅者进行回调(在这里是 render )。

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

    代理

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

    _proxy(options.data);/*构造函数中*/
    
    /*代理*/
    function _proxy (data) {
        const that = this;
        Object.keys(data).forEach(key => {
            Object.defineProperty(that, key, {
                configurable: true,
                enumerable: true,
                get: function proxyGetter () {
                    return that._data[key];
                },
                set: function proxySetter (val) {
                    that._data[key] = val;
                }
            })
        });
    }
    

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

    1 条回复    2017-08-21 17:26:14 +08:00
    answershuto
        1
    answershuto  
    OP
       2017-08-21 17:26:14 +08:00
    原文入口写错了,这里贴一下 https://github.com/answershuto/learnVue
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2764 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 15:23 · PVG 23:23 · LAX 07:23 · JFK 10:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.