weekCodeing / interview-answe

🌍 前端程序员训练 求星星 ✨ 各位同学可以在issues中提问,无论是实际项目中遇到的问题,或者是技术问题都可以, 大家一起解决💯 👍 😄。
http://www.dadaqianduan.cn/
76 stars 9 forks source link

153.[vue]Vue组件data为什么必须是个函数而Vue的根实例则没有此限制? #153

Open webVueBlog opened 4 years ago

webVueBlog commented 4 years ago

[vue]

webVueBlog commented 4 years ago

注册组件的本质其实就是建立一个组件构造器的引用。使用组件才是真正创建一个组件实例。所以,注册组件其实并不产生新的组件类,但会产生一个可以用来实例化的新方式。

vue组件实例化后,同一组件不同的实例引用同一个data对象(函数)。

对于vue组件来说,要保证复用性,data必须是函数,返回一个对象,这样多个组件实例就能得到返回对象独立的拷贝。

对于vue根实例来说,因为只实例化一次,所以data可以是对象或函数

webVueBlog commented 4 years ago

组件data为函数缘由

在创建或注册模板的时候传入一个 data 属性作为用来绑定的数据。但是在组件中,data必须是一个函数,因为每一个 vue 组件都是一个 vue 实例,通过 new Vue() 实例化,引用同一个对象,如果 data 直接是一个对象的话,那么一旦修改其中一个组件的数据,其他组件相同数据就会被改变,而 data 是函数的话,每个 vue 组件的 data 都因为函数有了自己的作用域,互不干扰。

webVueBlog commented 4 years ago

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

"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)
    }
// 在initData时候会先判断data的类型, 在根实例中无论是对象或者是函数类型均会解析

    let data = vm.$options.data

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

在Vue组件中data的使用

只能为函数

    vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options|| {},
        vm
    )
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)
}

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

    const MyComponents = function() {};

    MyComponents.prototype.data = {
        number:1
    };

    let component1 = newMyComponents();
    let component2 = newMyComponents();

    component1.data.number = 2
    console.log(component1.data.number, 'component1-data') // 2
    console.log(component2.data.number, 'component2-data') // 2