Vue组件data选项为什么必须是个函数而Vue的根实例则没有此限制?

Ekko_

前言

在使用Vue开发的过程中,data这个options对于我们来说是最熟悉不过的了。一般来说我们的data通常会写成函数形式,通过return将数据返回,但是官方在以根实例编写demo的时候,我们发现他直接使用了对象的形式。

image.png

既然这样,在组件中这样写岂不是更加清爽便利,结果尝试之后,发现给出如下提示:

image.png

用我的渣渣英语翻译一下,"data"选项应该是一个函数,并且在组件定义中返回每个实例的值。

emmmmm,"data"选项应该是一个函数???那为什么在根实例中就data可以使用对象,但在组件中就不可以?下面我们分析一下为什么要这样搞,并从源码级别看一下,Vue中究竟做了什么处理。

在Vue根实例中data的使用

可以为函数,可以为对象

vue的实例的时候会调用一个init方法

    function Vue (options) {
        if (process.env.NODE_ENV!=='production'&& !(thisinstanceofVue)) {
            warn('Vue is a constructor and should be called with the`new` keyword')
        }

        this._init(options)
    }

这个init方法会将我们的options传入,做一系列的初始化,包括对数据的初始化,感兴趣的同学可以去Vue源码的src/core/instance/init.js中一探究竟。话不多说,咱们接着看。

在对数据初始化的时候,我们就可以找到对实例的处理

    // 在initData时候会先判断data的类型, 在根实例中无论是对象或者是函数类型均会解析

    let data = vm.$options.data

    data = vm._data = typeofdata === 'function'
        ? getData(data, vm)
        : data || {}

在Vue组件中data的使用

只能为函数

其实在初始化数据之前,Vue在init时候还会先合并options

    vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options|| {},
        vm
    )

在mergeOptions的合并策略其中有一段会判断是根实例还是组件,并作出处理

strats.data = function (
    parentVal:any,
    childVal:any,
    vm?:Component
    ):?Function {
        if (!vm) {
            if (childVal&&typeof childVal!=='function') {
                process.env.NODE_ENV!=='production'&&warn('The "data" option should be a function '+'that returns a per-instance value in component '+'definitions.', vm)
                return parentVal
            }
            return mergeDataOrFn(parentVal, childVal)
        }
        return mergeDataOrFn(parentVal, childVal, vm)
}

从上面源码可以看出,如果当前实例判断为!vm,即是普通组件的时候,如果选项类型不是function,会抛出一个错误,即刚才图片中看到的,data选项应该为一个函数。

结论

通过上面的代码,我们可以看出Vue中做了怎样的处理。所以我们可以分析一下Vue这样设计的目的。
在一个项目中,组件可以有多个,每一个组件均可当作一个构造器,注册组件的本质其实就是构造器的引用。如果直接使用对象,他们的内存地址是一样的,一个数据改变了其他也改变了,这就造成了数据污染,如果使用函数的话,会形成一个全新的作用域,这样data中的数据不会相互影响,从而避免数据污染。但由于根实例只有一个,所以不存在数据污染这种情况,也就可以使用对象了。

例子

为了验证上面的结论,我们从原型链出发,写个简单的例子。

    const MyComponents = function() {};
    
    MyComponents.prototype.data = {
        number:1
    };
    
    let component1 = new MyComponents();
    let component2 = new MyComponents();
    
    component1.data.number = 2
    console.log(component1.data.number, 'component1-data') // 2
    console.log(component2.data.number, 'component2-data') // 2

写在最后

Vue中有很多值得我们讨论和注意的细节,并且通过阅读源码我们可以得到最笃定和准确的答案,之后我也会在学习源码的过程中分享一些我的心得和学到的知识,如果有理解错误的地方,希望大家指出来,一起学习,一起进步。
克服恐惧最好的办法就是面对恐惧,加油,奥利给!!!

阅读 987

一个前端菜鸟,级别为前端中的负级

80 声望
4 粉丝
0 条评论

一个前端菜鸟,级别为前端中的负级

80 声望
4 粉丝
宣传栏