<script> vue.component('comp', { template: '<div @click="counter++">{{counter}}</div>', data() { return { counter: 1 } } }) let a_vue = new Vue({ el: '#app', data: { counter: 1 } }) </script>
其实这个问题咱们在vue 源码中能够获得解答
javascript
首先咱们去看 src/core/instance/init.js :31html
vm._isVue = true // merge options // 选项合并 // 自定义组件的合并 if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { // 根 实例 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) }
这个部分主要是描述 自定义组件实例和根实例 在合并的时候会出发不一样方法vue
以后咱们看 src/core/instance/state.js :112 ---> initDatajava
function initData (vm: Component) { // 获取data选项 let data = vm.$options.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 // data/method/props不能重复 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 */) }
接下来看最重要的部分: src/core/util/options.js:121-----> strats.dataapp
// 策略合并 data strats.data = function ( parentVal: any, childVal: any, vm?: Component // 只有是 vm = Vue 根实例的时候, 第三个参数才会调用到 ): ?Function { // 第一次响应的时候 是 谁触发的 ? 根实例吗,no // 若是是一个普通的实例 if (!vm) { // run --> vm = Vue 的时候才会走 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 } // 并无传递 vm return mergeDataOrFn(parentVal, childVal) } // 若是是根实例会走下面的逻辑 return mergeDataOrFn(parentVal, childVal, vm) }
组件须要函数, 根实例不须要函数。
为啥呢,这么具体说说:
vue组件可能存在多个实例,若是使用对象形式定义 data, 则会致使他们公用一个data 对象, 那么状态变动将会影响全部的组件实例,这是不合理的; 采用函数形式定义, 在 initData时会将其做为工厂函数返回全新data 对象,有效的规避多实例之间状态污染的问题。而在 vue 根实例建立的过程当中则不应存在该限制,也是由于根实例只有一个,不须要担忧这种状况的发生
ide