难度级别:中高级及以上 提问概率:80%
在Vue项目中,App.vue下的每个子组件都会生成一个单独的Vue实例对象,但这些子对象都是通过通过vue.extend方法创建而来的,也就是说我们平时在项目中所定义的Vue组件,都有一个相同的父类对象。这样也就导致了,所有的子组件最终所指向的内存空间是相同的。
但我们的需求是,子组件间的data数据是互相独立的,不能互相干扰,虽然很多时候我们都希望通过组件间数据共享,达到一个组件内数据变更,其他组件的数据也响应式变更的效果,但这些却属于开发人员主动操作的范围。所以组件间一定不能出现不受开发人员控制,data数据自动互相影响的情况。
通过读源码文件src/core/util/options.js和src/core/instance/init.js可知,Vue项目入口文件的new Vue的data属性可以是一个对象,因为这个最外层Vue实例对象只被new一次,不会再被复用。而我们自定义的这些Vue业务组件,都会通过vue.extend创建子类实例对象,然后把我们在业务组件中定义的data数据依赖通过mergeOptions方法与父类做属性合并。合并的过程中,就要求子类的data属性是一个函数,如果不是函数就会给出The "data" option should be a function的异常提示。在面试的时候,自己感觉单纯的源码描述不够充分,可以通过下面这个例子进行说明,代码如下
Javascript代码:
<script>
function MyComponent(){
this.data = this.data();
}
MyComponent.prototype.data = function (){
return {
name : 'default'
}
}
const vueComponent1 = new MyComponent();
const vueComponent2 = new MyComponent();
vueComponent1.data.name = '张三';
// 张三
console.log(vueComponent1.data.name);
// default
console.log(vueComponent2.data.name);
vueComponent2.data.name = '李四';
// 李四
console.log(vueComponent2.data.name);
// 张三
console.log(vueComponent1.data.name);
</script>
上面的代码中,因为data是一个函数,我们通过创建新的子类vueComponent1和vueComponent2,可以单独修改data数据而子组件间互不影响。
Javascript代码:
<script>
function MyComponent(){
this.data = this.data;
}
MyComponent.prototype.data = {
name : 'default'
}
const vueComponent1 = new MyComponent();
const vueComponent2 = new MyComponent();
vueComponent1.data.name = '张三';
// 张三
console.log(vueComponent1.data.name);
// 张三
console.log(vueComponent2.data.name);
vueComponent2.data.name = '李四';
// 李四
console.log(vueComponent2.data.name);
// 李四
console.log(vueComponent1.data.name);
</script>
上面的代码,因为data是一个对象,一旦其中一个子类修改了data数据,那么另外一个紧跟着就一起修改了,会造成组件间的相互影响的问题。
刷题思考
因为很多脚手架会自动生成自定义Vue组件,所以很多开发人员也就忽略了data是一个对象还是一个函数的问题。Vue这个框架已经流行很久了,源码就那么多,也不经常变,所以想要在本题上得分,单纯的描述源码中是如何实现的已经不够了。求职者最后如果能清晰的描述出,子类继承父类后,对于修改函数的对象区别,那就再好不过了。
类似考点
面试官在这道题的基础上,很可能还会提出以下问题,例如你知道Vue组件对象在合并的时候,mergeOptions方法都做了哪些事情吗?例如你知道Vue的data属性什么情况下可以是对象吗?