假设有如下代码:
this.$options.data
是demo组件的data函数
this._data
是demo组件的data函数执行结果
看源码src/core/instance/state可知
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
}
function getData (data: Function, vm: Component): any {
try {
return data.call(vm, vm)
} catch (e) {
handleError(e, vm, `data()`)
return {}
}
}
this.$data
实际上访问的是this._data
export function stateMixin (Vue: Class<Component>) {
// flow somehow has problems with directly declared definition object
// when using Object.defineProperty, so we have to procedurally build up
// the object here.
const dataDef = {}
dataDef.get = function () { return this._data }
const propsDef = {}
propsDef.get = function () { return this._props }
if (process.env.NODE_ENV !== 'production') {
dataDef.set = function (newData: Object) {
warn(
'Avoid replacing instance root $data. ' +
'Use nested data properties instead.',
this
)
}
propsDef.set = function () {
warn(`$props is readonly.`, this)
}
}
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
}
this.xxx
实际上访问的是this._data.xxx
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
const keys = Object.keys(data)
const props = vm.$options.props
let i = keys.length
while (i--) {
const key = keys[i]
if (props && hasOwn(props, key)) {
// 如何key在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)) {//key不以$和_开头
proxy(vm, `_data`, key)
}
}
}
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
// 假设访问this.name,实际代理如下
// Object.defineProperty(vm,'name',{
// enumerable: true,
// configurable: true,
// get:function proxyGetter(){return this._data.name},
// set:function proxySetter(val){this._data.name=val}
// });
}
- 所以可以通过
Object.assign(this.$data,this.$options.data.call(this))
重置数据为当前组件的初始数据
- 事实上访问
this.$data
就是访问this._data
,Object.assign把this.$options.data.call(this)执行的结果对象,依次遍历属性调用function proxySetter(val){this._data.name=val} ,修改了this._data的属性为初始值
下面是一个简化版的测试代码:
let obj={
_data:{num:3}
}
Object.defineProperty(obj,'num',{
get:function proxyGetter(){return this._data.num},
set:function proxySetter(val){this._data.num=val}
})
Object.defineProperty(obj,'$data',{
get:function () { return this._data }
})
Object.assign(obj.$data,{num:333})
// 相当于Object.assign(obj._data,{num:333})
console.log(obj.num,obj.$data.num) //333 333