首发地址:CJWbiu's Blog
在这里思考一个问题,使用Vue
的时候需要在创建Vue实例时传入一个option
,这里包含了我们定义的props、methods、data
等。而在methods
的方法中获取data
中的key
值都是直接通过this.key
获取option
对象中的methods
中的定义的方法如何通过this
访问到data中的数据呢?
let vue = new Vue({ el: '#app', methods: { say() { console.log(this.msg) } }, data: { msg: 'jjjjj' } })
一开始我想是将data
和methods
中的数据全都挂载到了vm
上,然而Vue
实例上有methods
中定义的方法,却没有data
中的属性,data中的数据全部存储在vm._data中,通过this.key
访问其实是this._data.key
,Vue
在这里做了一层代理,通过defineProperty
设置了vm的getter和setter,而methods中的方法在initMethods方法中将其中的this
绑定到了vm
上,这样methods中方法访问的this
也就指向了_data
。
下面是参照源码相关逻辑的简化代码:
function MyVue(option) { this._init(option); } MyVue.prototype._init = function(option) { const vm = this; vm.$options = option; //源码在此做了对子组件option的合并处理 if(vm.$options.methods) initMethods(vm, vm.$options.methods); //源码中还有对props的处理,data、props、methods都会做查重处理,不能有相同的属性名 if(vm.$options.data) initData(vm); } function initMethods(vm, methods) { const props = vm.$options.props for (const key in methods) { vm[key] = methods[key].bind(vm); //将methods上的方法挂载到vm上并将方法中所有的this指向vm,通过下面的proxy就可以访问到_data上的属性 } } function initData(vm) { //将data上数据复制到_data并遍历所有属性添加代理 vm._data = vm.$options.data; const keys = Object.keys(vm._data); let i = keys.length; while(i--) { const key = keys[i]; proxy(vm, `_data`, key); } } function proxy(target, sourceKey, key) { let sharedPropertyDefinition = {}; sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) //一层代理,每次访问this[key]时代理到this._data[key] } let app = new MyVue({ methods: { say: function() { console.log(this.msg + this.age); } }, data: { msg: 'jjj', age: 33 } }) app.say(); //jjj33