<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