源码中找答案:src\core\instance\state.js - initData()
function initData (vm: Component) {
let data = vm.$options.data
/*
先判断 data 的类型
如果data类型是 函数,则执行data函数,并将其结果作为data选项的值,
否则使用用户设置的 data
*/
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
// 代理这些数据到实例上
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
}
// observe data
// 执行递归处理
observe(data, true /* asRootData */)
}
测试代码:
<body>
<div id="demo">
<h1>vue组件data为什么必须是个函数? </h1>
<comp></comp>
<comp></comp>
</div>
<script src="../../dist/vue.js"></script>
<script>
Vue.component('comp', {
template:'<div @click="counter++">{{counter}}</div>', data: {counter: 0}
})
// 创建实例
const app = new Vue({
el: '#demo',
});
</script>
</body>
结论:
1. Vue.component 创建组件时,只会执行一次,当组件调用多次,多实例时,如果返回的是对象,则会导致多实例共用一个data对象,那么当状态变更时,会影响所有组件实例,造成数据污染。所以必须使用工厂函数返回数据。
2.根组件因为是单例,所以没有此限制,data直接返回对象就可以。
3.通过源码可以看到,在初始化数据时,会先判断 data 的类型, 如果data类型是 函数,则执行data函数,并将其结果作为data选项的值,否则使用用户设置的 data。