【若川视野 x 源码共读】第23期 | 为什么 Vue2 this 能够直接获取到 data 和 methods

414 阅读2分钟

本文是参与若川的源码共读活动第23期 以下是我阅读源码之后的笔记

1、解读构造Vue的源码

1.1 准备页面,写上vue构造函数


<script src="http://unpkg.com/vue@2.6.14/dist/vue.js"></script>     
<script>         
const vm = new Vue({          
    data: {             
        name: 'coco'            
    },       
    methods: {            
        sayName() {         
            console.log(this.name)    
        }           
   }     
})        
console.log(vm.name)       
console.log(vm.sayName())
</script>
2、vscode安装Liver server插件,在浏览器中打开页面(npm i -g http-server也可以,但是我没有下载和启动成功)

   a、在new Vue处进行断点,进入vue函数里,查看函数构造过程
function Vue (options) { 
    // 判断是不是用了new关键词调用构造函数,因为Vue一个项目一般只需要new一次,因此调用的时候需要new一次 
    // (jQuery在项目中需要经常调用,因此把new操作放在了源码里面) 
    if (!(this instanceof Vue) ) {
        warn('Vue is a constructor and should be called with the `new` keyword'); 
    }
    // 初始化各种数据状态 
    this._init(options);
}
    b、初始化methods---(1、判断是否为函数,不为函数则初始化一个函数,2、如果为函数,则用bind把函数的this指向绑定到vm上面)

    c、初始化data

1.2、判断data是否为对象

1.3、遍历data,并和methods、props,进行比较,若有相同则报错

1.4、代理data数据为_data,并设置data里的属性为响应式数据

// 初始化数据initData() 
// isReserved 判断属性是否为保留字段或者是以$/_开头的属性名 
// 调用data函数,或者数据属性对象 
function getData (data, vm) {
    // #7573 disable dep collection when invoking data getters 
    pushTarget(); 
    try { 
        return data.call(vm, vm) 
    } catch (e) { 
        handleError(e, vm, "data()");
        return {} 
    } finally { 
        popTarget(); 
    }
}

1.5、object.defineProperty(obj,prop,descriptor)

 1.5.1、obj要定义属性的对象,prop要定义或修改的属性名称或者symbol、descriptor要定义或修改的属性描述符

 1.5.2、descriptor包括
configurable(是否可以删除)enumerable(枚举)value(值)writable(值修改)get(接收函数)set(设置函数)
数据描述符可以可以可以可以不可以不可以
存取描述符可以可以不可以不可以可以可以

如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符。

如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常。

2、简单的源码实现

        // 初始化函数
        function noop(a, b, c) {}
        // 定义数据属性值
        var sharedPropertyDefinition = {
            enumerable: true,
            configurable: true,
            get: noop,
            set: noop
        };
        // proxy 函数的原理是通过 Object.defineProperty 函数在实例对象 vm 上定义与 data 数据字段同名的访问器属性,并且这些属性代理的值是 vm._data 上对应属性的值
        function proxy(target, sourceKey, key) {
            sharedPropertyDefinition.get = function proxyGetter() {
                return this[sourceKey][key]
            };
            sharedPropertyDefinition.set = function proxySetter(val) {
                this[sourceKey][key] = val;
            }
            Object.defineProperty(target, key, sharedPropertyDefinition);
        }
        
        // 初始化值
        function initData(vm) {
            const data = vm._data = vm.$options.data;
            const keys = Object.keys(data);
            var i = keys.length;
            while(i --) {
                var key = keys[i];
                proxy(vm, '_data', key);
            }
        }
        
        // 初始化方法
        function initMethods(vm, methods) {
            for(var key in methods) {
                vm[key] = typeof methods[key] !== 'function' ? noop : methods[key].bind(vm);
            }
        }
        
        
        function Person(options) {
            let vm = this;
            vm.$options = options;
            var opts = vm.$options;
            if(opts.data) {
                initData(vm);
            }
            if(opts.methods) {
                initMethods(vm, opts.methods)
            }
        }

        const p = new Person({
            data: {
                name: 'coco'
            },
            methods: {
                sayName() {
                    console.log(this.name)
                }
            }
        })
        console.log(p.name,"name");  // coco name
        console.log(p.sayName(),"sayName") 

疑问 pushTarget、 popTarget这两个方法还不是特别懂,了解了一些

3、总结

3.1、 复习了代码调试方法和object。defineproperty的描述符属性

3.2、 知道了vue和jQuery使用new的不同

3.3、this可以直接访问到methods和data,methods的方法用bind把this指向绑定到了vm上面,data--数据在object.defineproperty代理前存储到了vm._data对象中

3.4、 本文笔记大多是借鉴若川的文章,小部分是自己思考并查阅资料整合

(阅读源码文章还是很有收获的,从以前的看不懂不耐心到现在的慢慢摸索, 只不过看完文章之后写笔记这个能力比较欠缺,有点不知道从那里下手,慢慢改进 大家一起进步加油ヾ(◍°∇°◍)ノ゙)