首先,我们看Vue官网解释
data必须是一个函数
当我们定义这个 <button-counter> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:
data: {
count: 0
}
取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
data: function () {
return {
count: 0
}
}
如果 Vue 没有这条规则,点击一个按钮就可能会像如下代码一样影响到其它所有实例
在官网上还提到
把一个普通 Javascript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是仅 ES5 支持,且无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。
OK,现在我们来举个🌰
var option = {
data: {
a: 1
}
}
class component {
constructor(opt) {
this.data = opt.data;
Object.defineProperty(this.data, `a`, {
get: function () {
console.log('get val');
return this._a;
},
set: function (newVal) {
console.log('set val:' + newVal);
this._a = newVal;
}
});
}
}
var c1 = new component(option);
var c2 = new component(option);
c1.data.a = 3;
c2.data.a = 5;
console.log(`c1 : ` + c1.data.a);//c1 : 5
console.log(`c2 : ` + c2.data.a);//c2 : 5
示例代码中 Object.defineProperty 把传进来组件中的data 的 a 属性转化为 getter 和 setter,可以实现 data.a 的数据监控。这个转化是 Vue.js 响应式的基石。
这样就不难理解data为什么不能是对象了,如果传进来是对象,new出来的两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改。
现在我们修改一下
var option = {
data: function () {
return {a: 1}
}
}
class component {
constructor(opt) {
this.data = opt.data();
Object.defineProperty(this.data, `a`, {
get: function () {
console.log('get val');
return this._a;
},
set: function (newVal) {
console.log('set val:' + newVal);
this._a = newVal;
}
});
}
}
var c1 = new component(option);
var c2 = new component(option);
c1.data.a = 3;
c2.data.a = 5;
console.log(`c1 : ` + c1.data.a);//c1 : 3
console.log(`c2 : ` + c2.data.a);//c2 :5
传进去一个函数,这样每一个实例的data属性都是独立的,不会相互影响了。
总结:
对象是对于内存地址的引用。直接定义个对象的话,组件之间都会使用这个对象,这样会造成组件之间数据相互影响。组件就是为了抽离开来提高复用的, 如果组件之间数据默认存在关系,就违背了组件的意义。 函数 return 一个新的对象,其实还是为 data 定义了一个对象, 只不过这个对象是内存地址的单独引用了,这样组件之间就不会存在数据干扰的问题。
以上为个人理解,如果有误,请指出,谢谢。