大家都能懂的Vue源碼系列—02—Vue組織函數

上篇博文中說到了Vue源碼的目次組織是什麼樣的,每一個目次的作用我們應當也有所相識。我們曉得core/instance目次主假如用來實例化Vue對象,所以我們在這個目次下面去尋覓Vue組織函數。果真我們找到了Vue的組織函數定義。

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

當你新建一個Vue實例時刻,會推斷假如當前的環境不是臨盆環境,且你在挪用Vue的時刻,沒有用new操作符。就會挪用warn函數,拋出一個正告。通知你Vue是一個組織函數,須要用new操作符去挪用。這個warn函數不是純真的console.warn,它的完成我們背面的博文會引見。
接下來,把options作為參數挪用_init要領。options不做過量的引見了,就是你挪用new Vue時刻傳入的參數。在深切_init要領之前,我們先把眼光移到index.js文件里

function Vue (options) {
  ...
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

在Vue的組織函數定義以後,有一系列要領會被馬上挪用。這些要領重要用來給Vue函數增加一些原型屬性和要領的。个中就有接下來要引見的Vue.prototyoe._init

Vue.prototype._init

在core/instance/init.js中我們找到了_init的定義。代碼已做了一些中文解釋

  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++
    let startTag, endTag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }
    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    // 有子組件時,options._isComponent才會為true
    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.
      // 優化組件實例,由於動態選項兼并很慢,而且也沒有組件的選項須要特別看待
      // 優化components屬性
      initInternalComponent(vm, options)
    } else {
      // 傳入的options和vue自身的options舉行兼并
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm) // 初始化一些和生命周期相干的內容
    initEvents(vm)  // 初始化事宜相干屬性,當有父組件的要領綁定在子組件時刻,供子組件挪用
    initRender(vm) // 增加slot屬性
    callHook(vm, 'beforeCreate') // 挪用beforeCreate鈎子
    initState(vm) // 初始化數據,舉行雙向綁定 state/props
    initProvide(vm) // resolve provide after data/props 注入provider的值到子組件中
    callHook(vm, 'created') // 挪用created鈎子

    /* istanbul ignore if */
    // 盤算覆蓋率時疏忽以下代碼
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }
    if (vm.$options.el) {
      vm.$mount(vm.$options.el) // 把模板轉換成render函數
    }
  }

我們一一來剖析上述代碼。起首緩存當前的上下文到vm變量中,輕易以後挪用。然後設置_uid屬性。_uid屬性是唯一的。當觸發init要領,新建Vue實例時(當襯着組件時也會觸發)uid都邑遞增。
下面這段代碼主假如用來測試代碼機能的,在這個時刻相當於打了一個”標記點”來測試機能。

let startTag, endTag
    /* istanbul ignore if */
    process.env.NODE_ENV === 'develop'
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
}

對這部份內容感興趣的朋友們能夠點擊我的另一篇文章Performance API檢察。
接下來實行這行代碼vm._isVue = true,Vue的作者對這句話做了解釋。

an flag to avoid this being observed

乍看起來彷佛不太邃曉,彷佛是說為了防備this被observed實例化。那這究竟是什麼意思呢?我們來看observer的代碼。

export function observe (value: any, asRootData: ?boolean): Observer | void {
  ...
  else if (
    observerState.shouldConvert &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
  }
  ...
}

假如傳入值的_isVue為ture時(即傳入的值是Vue實例自身)不會新建observer實例(這裡能夠臨時明白新建observer實例就是讓數據相應式)。
再回到init源碼部份

if (options && options._isComponent) {
    initInternalComponent(vm, options)
} else {
    vm.$options = mergeOptions(
      resolveConstructorOptions(vm.constructor),
        options || {},
        vm
     )
}

當相符第一個前提是,即當前這個Vue實例是組件。則實行initInternalComponent要領。(該要領重要就是為vm.$options增加一些屬性, 背面講到組件的時刻再細緻引見)。當相符第二個前提時,即當前Vue實例不是組件。而是實例化Vue對象時,挪用mergeOptions要領。mergeOptions重要挪用兩個要領,resolveConstructorOptions和mergeOptions。這兩個要領牽扯到了許多知識點,為了我們文章篇幅的斟酌。接下來預備經由過程兩篇博文來引見這兩個要領。下篇博文重要引見resolveConstructorOptions相干的內容,涉及到原型鏈和組織函數以及部份Vue.extend的完成,敬請期待!

    原文作者:淼淼真人
    原文地址: https://segmentfault.com/a/1190000014488094
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞