Axiu Blog
拿出了一点点时间来扩充一下`Vue`的背景知识。 ### 为什么组件的data属性必须是函数? 在自定义模块的新手上路部分,`Vue`文档是这么写的 > 通过 `Vue` 构造器传入的各种选项大多数都可以在组件里用。`data` 是一个例外,它必须是函数。如果定义了一个对象,那么 `Vue` 会停止,并在控制台发出警告,告诉你在组件中 `data` 必
一般在公司环境使用中,各种前端框架都会用一下,因为不同框架理念和使用场景有些许区别,有的重规模化,有的追求轻便易上手;有的模块化程度很高,有的通常全部写一起;有的规则安排的明明白白,有的又需要各种语法糖……虽然最近几个月工作特别忙,但是还是拿出了一点点时间来扩充一下`Vue`的背景知识。 ### 为什么组件的data属性必须是函数? 在自定义模块的新手上路部分,`Vue`文档是这么写的 >
一般在公司环境使用中,各种前端框架都会用一下,因为不同框架理念和使用场景有些许区别,有的重规模化,有的追求轻便易上手;有的模块化程度很高,有的通常全部写一起;有的规则安排的明明白白,有的又需要各种语法糖……虽然最近几个月工作特别忙,但是还是拿出了一点点时间来扩充一下`Vue`的背景知识。 ### 为什么组件的data属性必须是函数? 在自定义模块的新手上路部分,`Vue`文档是这么写的 >
Vue-组件的data属性为什么必须是函数?
Max

一般在公司环境使用中,各种前端框架都会用一下,因为不同框架理念和使用场景有些许区别,有的重规模化,有的追求轻便易上手;有的模块化程度很高,有的通常全部写一起;有的规则安排的明明白白,有的又需要各种语法糖……虽然最近几个月工作特别忙,但是还是拿出了一点点时间来扩充一下Vue的背景知识。

为什么组件的data属性必须是函数?

在自定义模块的新手上路部分,Vue文档是这么写的

通过 Vue 构造器传入的各种选项大多数都可以在组件里用。data 是一个例外,它必须是函数。如果定义了一个对象,那么 Vue 会停止,并在控制台发出警告,告诉你在组件中 data 必须是一个函数。

有一点觉得很奇怪,明明new Vue()的时候,data是可以传入一个对象的,为什么在组件这里,data就必须为函数了呢?

简而言之,组件的配置(options)和实例(instance)是需要分开的。最根本原因是**js对于对象(以及数组等)是传引用的**,因为如果直接写一个对象进去,那么当依此配置初始化了多个实例之后,这个对象必定是多个实例共享的。

举两个例子就明白了

例子1

config = { data: { name: 'foo' } };

function someComponent (config) { this.data = config.data; }

let c1 = new someComponent(config);

let c2 = new someComponent(config);

c1.data.name = 'bar'; console.log(c2.data.name); // 'bar'

例子2

config = { data: function () { return { name: 'foo' }; } };

function someComponent (config) { this.data = config.data(); }

let c1 = new someComponent(config);

let c2 = new someComponent(config);

c1.data.name = 'bar';

console.log(c2.data.name); // 'foo'

为了加深印象,还是把相关部分都扯一点。

组件(Component)定义方式

写完hello world的同学都知道,组件在定义的时候,可以全局(Vue.component())或者局部注册

new Vue({ // ... components: { // 将只在父模板可用 'my-component': Child } })

两种方法并没有本质区别,都需要在data属性里传入对象。局部注册只是放在了new Vueoptions处理部分,仍然是Vue.extend(definition)里判断。

下面以全局注册为例过一遍Vue源码。

前面说的报错位置在这里

strats.data // vue-template-compiler/build.js ... if (typeof childVal !== 'function') { "development" !== 'production' && warn( 'The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm ) return parentVal } ...

这个函数简单来说,是负责data字段内容处理的,不管是new Vue的参数里data还是组件初始化的data,都要经过这里。

简单起见,从这个位置往上倒(二声)到开头:

initGlobalAPI (Vue) -function initAssetRegisters (Vue) ...

到这一步结束,按照_assetTypes(包括'component','directive','filter')挂载方法。这里挂载了Vue.component的初始化方法,但还没调用。

经过一众其他内部操作。直到执行我们的代码(组件是从官方文档抄过来的)

Vue.component('button-counter', { data: function () { return { counter: 0 } } template: 'You clicked me {{ count }} times.' })

-Vue.extend(definition)

definition内容:

{ data: function () {...}, "template": "You clicked me {{ count }} times.", "name": "button-counter" }

写入组件的配置:

Sub.options = mergeOptions(Super.options, definition) -mergeField('data') -strats.data

这一步就是前面报错的那一步,会判断data是否为函数,是则执行并挂载函数方法。否则返回父级属性。

vue-component-config

组件挂载结果

看起来3个_assetTypes加了s,是在代码里搞的

config._assetTypes.forEach(function (type) { strats[type + 's'] = mergeAssets })

在实际使用时,才会初始化组件,即调用

function VueComponent (options) { this._init(options) }

初始化Vue实例时的处理

new Vue的时候,也会调用mergeOptions,不同的是这时候传入了vm实例。这时在mergeField('data')里走了另外一条路线:

return function mergedInstanceDataFn () { // ... var instanceData = typeof childVal === 'function' ? childVal.call(vm) : childVal // ... }

Comments