看了网上好多文章都是说使用函数是为了每个组件的数据独立,不被污染。其实使用传参正常是没有问题,问题出在Vue实现组件的原理我们只能使用函数。
为什么在定义组件的data使用对象就会制成数据污染呢?首先要了解出现问题的原因,这是JS对象指针搞出的问题,对象是用指针地址引用的,请看下面事例代码
var obj = {name:'vue'};
var obj2 = obj;
//改变obj2的name
obj2.name = '我修改一下';
console.log(obj, obj2);
控制台打印结果如下
为什么修改了obj2的name,obj的name也变了,这就是js对象的指针搞的鬼,原理如下图
obj指向内存地址:001,obj2 = obj,当我们修改了obj2.name的时候,其实就是修改了内存地址:001的数据,因为obj的内存地址为001,所以obj.name也被修改了。
再看一下Vue组件实现的流程,为什么data使用对象会出问题。
Vue的组件是定义在Vue.options.components静态属性下,下图中的todo-item就是我定义的组件,data我使用了对象,默认是不可以的,但我修改了代码,修改方法看第二张图。
vue.js
我使用的是vue2版本,查找strats.data关键字可以导航到,源代码会检查组件data是否为函数,如果为函数就会返回,我们注释这一行就行了。我定义的组件代码如下。
// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
template: '<div><b>这是个待办项:{{name}}<button @click="changeName">click</button></b></div>',
//data:{name:'haha'},
methods:{
changeName(){
this.name = new Date();
}
},
data(){return {name:'haha'}}
})
var vm = new Vue(
{
el:'#app',
data:{
message:'hello world'
}
}
);
html代码如下,使用两次组件todo-item
<div id="app">
<div>{{ message }}</div>
<todo-item></todo-item>
<todo-item></todo-item>
</div>
当点击click的时候会改变组件的变量name,这时我们点击其中一个的时候两个组件的值会一样,如下图,这就是出问题了
我们来分析一下,它是怎么产生这个问题的。前面我们提到Vue的组件是定义在Vue.options.components静态属性下,组件在实例化的时候会使用这里定义的数据,我们可以看到data定义为对象{name:'haha'},当我们实例化组件的时候都会引用这个对象,你没听错是引用,引用就会有指针问题,当我们实例化两个对象的时候,任意一个组件修改了data的变量,data指向的源数据就会被修改,引用的组件的data也会改变。
修改后的数据如下图,可以看到组件定义的源对象和两个组件的值一样,因为是引用。
当我们组件的data使用函数就不会出现对象污染问题了,如下图,因为每次组件实例化后的data会产生不一样的内存地址,所以组件的data修改只会影响自己的内存地址。
其实组件的data定义也可以是对象的,在实例化组件的时候对data作深度克隆产生新的地址就可以了,那为什么Vue没有作这个兼容性呢?它是老大它说了算,我们按说明书操作就可以了。
对象指针问题在实际项目中也会产生的,大家要多加注意。例如在列表点击详细,在详细修改了变量,列表的变量也会跟着修改,这时候在详细页就要做深度克隆处理了。怎么做深度克隆你们自行学习了。好像Vue没有提供深度克隆的函数,要自己引用第三方的函数。
神奇的JS指针就讲到这了,指针应该是给全局使用场景用的(统一性,改一处全部生效),如果是拷贝场景(独立性,自己管理自己)就要对它做深度克隆再使用了,减少不必要的麻烦。
上面所讲只是个人认知,如有错误请自行学习改正。本人使用Vue不到两个星期,还是一个Vue小白。