vue 源碼學習之 面試那些事 ----vue組件的 data 爲何是一個函數呢?

<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

相關文章
相關標籤/搜索