vue数据驱动:data中的数据是如何通过this访问到的呢
//html <div id="app"> {{message}} </div> //script new Vue({ el:"#app", router, template:"<App/>", data(){ return { message } },
mounted(){
console.log(this.message)//问题:为什么可以通过this.message这种方式来访问data中的数据呢
} })
在vue源码中,可以发现数据data是定义在初始化对象$options中的,要想访问到data中的key字段,正常来说是通过vm.$options.data.key来访问的。那么,在vue中是怎么实现利用vm.key就能访问到key字段呢。在源码中,有一个方法为initData(),方法所在为:initMixin() -> _init() -> initState() ->initData(),具体如下:
function initData (vm) { var data = vm.$options.data; data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {};//将vm.$options.data存储在变量data中,同时vm新建私有属性_data映射该对象,这样就可以通过vm._data访问到data数据 if (!isPlainObject(data)) { data = {}; process.env.NODE_ENV !== 'production' && warn( 'data functions should return an object:\n' + 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm ); } // proxy data on instance var keys = Object.keys(data); var props = vm.$options.props; var methods = vm.$options.methods; var i = keys.length; while (i--) { var key = keys[i]; if (process.env.NODE_ENV !== 'production') { if (methods && hasOwn(methods, key)) {//处理data中字段与methods方法字段命名冲突的情况 warn( ("Method \"" + key + "\" has already been defined as a data property."), vm ); } } if (props && hasOwn(props, key)) {//处理data中字段与props字段命名冲突的情况 process.env.NODE_ENV !== 'production' && warn( "The data property \"" + key + "\" is already declared as a prop. " + "Use prop default value instead.", vm ); } else if (!isReserved(key)) { proxy(vm, "_data", key);//代理,实现vm.key直接访问的关键方法。 } } // observe data observe(data, true /* asRootData */);//对数据添加观察 }
在initData方法中,主要做的工作有:
- 将vm.$options.data映射到vm._data中,使得可以通过vm._data访问数据变量
- 处理data与props、methods之间的变量命名冲突的情况
- 通道代理方法proxy来实现vm直接访问data中的数据变量
proxy方法解读为:
var sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop };//设置通用数据属性变量。此变量就是对vm._data(也就是vm.$options.data)的代理 function proxy (target, sourceKey, key) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] };/对属性变量添加get方法。在读取此属性的时候,实际上读取的是vm._data,也就是此变量就是对vm._data(也就是vm.$options.data)的代理
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val;
}; //添加set方法
Object.defineProperty(target, key, sharedPropertyDefinition); //通过Object.defineProperty方法,将key直接挂在vm下,并且通过sharePropertyDefinition属性的get和set方式,将对
vm._data的读取读和写操作映射到了vm.key,到此,便实现了vm.key直接操作数据的代理。
}