在这里思考一个问题,使用
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