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
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
Table of content
Loading...