Vue源码学习笔记1—数据代理

数据代理

分析准备

Object.defineProperty(obj, prop, descriptor)

该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

  • configurable :属性的描述符是否够被改变
  • enumerable :是否可以枚举
  • value :该属性对应的值
  • writable :属性的值是否可以修改
  • get :回调函数,根据其他相关的属性动态计算得到当前属性值
  • set:回调函数,监视当前属性值的变化,更新其他相关的属性值
        const obj = {
            firstName: 'A',
            lastName: 'B'
        }
        Object.defineProperty(obj, 'fullName', {
            get() {
                return this.firstName + '-' + this.lastName
            },
            set(value) {
                const names = value.split('-')
                this.firstName = names[0]
                this.lastName = names[1]
            }
        })
        console.log(obj.fullName) //A-B
        obj.firstName = 'C'
        obj.lastName = 'D'
        console.log(obj.fullName) //C-D
        obj.fullName = 'E-F'
        console.log(obj.firstName, obj.lastName) //E F

        Object.defineProperty(obj, 'fullName2', {
            configurable: false,
            enumerable: true,
            value: 'G-H',
            writable: false
        })
        console.log(obj.fullName2) //G-H
        obj.fullName2 = 'I-J'
        console.log(obj.fullName2) //G-H
        Object.defineProperty(obj, 'fullName2', {
            configurable: false,
            enumerable: true,
            value: 'G-H',
            writable: true
        }) //Cannot redefine property: fullName2(不能重新定义)

Object.keys(obj)

该方法会返回一个由一个给定对象的自身可枚举属性组成的数组
数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致 。

        const names = Object.keys(obj)
        console.log(names) //["firstName", "lastName", "fullName2"]

通过分析结果可知,fullName中没有定义enumerable时默认值为false,而fullName2定义了enumerable的值为true,输出的数字由可枚举属性组成。

Vue实例

什么是数据代理?
通过一个对象代理对另一个对象中属性的操作(读/写)
vue数据代理:
通过vm对象来代理data对象中所有属性的操作,更方便的操作data中的数据。
vm是对象代理

    <script>
        const vm = new Vue({
            el: "#app",
            data: {
                name: 'kk'
            }
        })
        console.log(vm.name) //kk
        vm.name = 'xx'
        console.log(vm.name) //xx
    </script>

源码分析

以下代码用的是:https://github.com/DMQ/mvvm.git,此版进行简化改造主要说明原理与实现

        const vm = new MVVM({
            el: "#app",
            data: {
                name: 'kk'
            }
        })
        console.log(vm.name, vm) //kk MVVM {$options: {…}, _data: {…}, $compile: Compile}
        vm.name = 'xx'
        console.log(vm.name) //xx

实现:

  1. 通过Object.defineProperty(vm,key,{})给vm添加与data对象的属性对应的属性
  2. 所有添加的属性都包含get/set方法
  3. 在get/set方法中去操作data中对应的属性
/*
相当于Vue的构造函数
*/
function MVVM(options) {
    // 将配置对象保存到vm
    this.$options = options || {};
    // 将data对象保存到vm和变量data中
    var data = this._data = this.$options.data;
    // 保存vm到变量me
    var me = this;

    // 数据代理:实现 vm.name -> vm._data.name
    // 遍历data中所有属性
    Object.keys(data).forEach(function (key) {
        //对指定属性实现代理
        me._proxyData(key);
    });
    this._initComputed();
    observe(data, this);
    this.$compile = new Compile(options.el || document.body, this)
}
MVVM.prototype = {
    constructor: MVVM,
    $watch: function (key, cb, options) {
        new Watcher(this, key, cb);
    },
    /*
    实现指定属性代理的方法
    */
    _proxyData: function (key, setter, getter) {
        var me = this;
        setter = setter ||
            // 给vm添加指定属性名的属性
            Object.defineProperty(me, key, {
                configurable: false, //不能重新定义
                enumerable: true, //可以枚举遍历
                // 从data中获取对应的属性值返回
                get: function proxyGetter() {
                    return me._data[key];
                },
                // value被保存到data中对应的属性上
                set: function proxySetter(newVal) {
                    me._data[key] = newVal;
                }
            });
    },
    _initComputed: function () {
        var me = this;
        var computed = this.$options.computed;
        if (typeof computed === 'object') {
            Object.keys(computed).forEach(function (key) {
                Object.defineProperty(me, key, {
                    get: typeof computed[key] === 'function' ?
                        computed[key] : computed[key].get,
                    set: function () {}
                });
            });
        }
    }
};

在这里插入图片描述
简单的理解:首先如红框所示将配置对象保存到vm,其次如浅蓝框所示将data对象保存到vm和变量data中,完成初始一些配置。遍历data中所有属性,依次对指定属性实现代理。如绿框所示给vm添加指定属性名的属性,在get/set方法中去操作data中对应的属性。重点还是在对代理概念,以及Object.defineProperty和Object.keys的理解上。

若有错误,欢迎指正!!

猜你喜欢

转载自blog.csdn.net/xicc1112/article/details/105930304