私はまだVue-Vueのインスタンス化を理解していません.

7294 ワード

Vueの定義
トピックに直進し、Vue/core/instance/index.jsに定義される.
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

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)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

これがVueの定義で、それは実は1つの構造関数で、ここで特筆すべきは、VueES6 Classの文法を使用していないで、Vueの構造関数のprototypeを拡張することを通じて、javascriptの原型の設計を十分に利用してモジュール化を実現して、下の多くのmixinVueの機能を拡張することを見ることができます.このようなコード設計は、読み取りとメンテナンスに非常に便利です.
以下の多くの__Mixin(Vue)は、実際に例示化されたオブジェクトがこれらの機能を使用できるように、Vue.prototypeに方法を追加することである.
この文章は主にVueの実例化過程を述べる.
すべてのすべてはnew Vue(options)から始まり、実際には上に見たthis._init(options)を呼び出した._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
    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
      )
    }
    /* 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)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, '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)
    }
  }
  • は、vmを現在の実行コンテキストとして定義し、すなわち、現在のnewからのインスタンスまたはそのサブコンポーネントを指す.
  • startTag endTag性能に関連し、window.performance打点により性能を試験した.
  •  // a flag to avoid this being observed
    vm._isVue = true
  • に記載されているように、Vueのすべてのインスタンスは_isVueとしてマークされ、その役割は、インスタンスがsetまたはdelであることを回避することである.ポイントを引く!!!プロジェクトでVue.set(this, key, value) Vue.del(this, key, value)を直接使用することはできません.そうしないと、エラーが発生します.
  •   if (options && options._isComponent) {
          initInternalComponent(vm, options)
        } else {
          vm.$options = mergeOptions(
            resolveConstructorOptions(vm.constructor),
            options || {},
            vm
          )
        }
  • Vueのマージポリシーは遅いので、内部コンポーネントを登録するときに特別な処理をする必要がないので、内部コンポーネントを直接初期化します.内部コンポーネントの識別は_isComponentで、内部コンポーネントを作成する最初のステップでは、内部コンポーネントのoption._isComponenttrueに設定します.次のコードを見てください:
  • export function createComponentInstanceForVnode (vnode: any, parent: any,): Component {
      const options: InternalComponentOptions = {
        _isComponent: true,
        _parentVnode: vnode,
        parent
      }
      // ...
      return new vnode.componentOptions.Ctor(options)
    }

    ここで、グローバルに登録されたコンポーネントは、Vue.options.componentsに登録されているが、サブコンポーネントの作成プロセスは、vm.constructor.optionsを自分の$optionsに拡張し、グローバルに登録されたコンポーネントが任意のコンポーネントで使用できる理由である.
  • ,mergeOptionsという記事がある時にすでに言及していますが、initGlobalAPIはその時に定義されています.Vue.optionsresolveConstructorOptionsの場合、new Vueに直接戻り、サブクラスが拡張されていれば、再び複雑な合併を行った後、新しいVue.optionsに戻ります.最後に、パラメータを新しいoptionsとマージします.
  • optionsライフサイクルに関連する属性を初期化する限り、この方法は比較的簡単です.
  • initLifecycle
  • export function initEvents (vm: Component) {
      vm._events = Object.create(null)
      vm._hasHookEvent = false
      // init parent attached events
      const listeners = vm.$options._parentListeners
      if (listeners) {
        updateComponentListeners(vm, listeners)
      }
    }
    
    export function updateComponentListeners (
      vm: Component,
      listeners: Object,
      oldListeners: ?Object
    ) {
      target = vm
      updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)
      target = undefined
    }
    // add   
    function add (event, fn) {
      target.$on(event, fn)
    }

    親登録イベントが取得され、ある場合はサブコンポーネントに接続されます.initEventsのカスタムコンポーネントを使用すると、サブコンポーネント内でv-onのイベントを介して親コンポーネントに通知されます.このシーンは、親コンポーネントイベントが現在のコンポーネントに定義されている問題を理解するのに役立ちます.ここでの具体的な手順は、コンポーネント内で$emitを使用してイベントが登録されている場合、v-on、すなわちaddによってイベントが$onにロードされ、サブコンポーネントvm._eventsのときにコールバックが完了することである.このうち$emitイベントは、イベントが実行された後、onceを呼び出して登録時間を削除する.
  • $offここでもいくつかの属性に対する付与値であり、その中で重要なのは:
  •   vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
      // normalization is always applied for the public version, used in
      // user-written render functions.
      vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
    initRenderが作成され、レンダリングの詳細については説明します.
  • vnode callHook(vm, 'beforeCreate') initState
  • callHook(vm, 'created')およびcallHook(vm, 'beforeCreate')は、多くの説明を必要とせずに、callHook(vm, 'created')のライフサイクル関数を呼び出す.VueにはinitStatepropsなどが初期化されており、ここではdataのコア部分であり、データハイジャックによって応答式原理が実現されている.Vueが応答するときの原理は詳細に説明されます.実際には、Vueの場合、beforeCreateを使用してインスタンスのデータを取得できない理由が理解されていますが、thisで取得できるのは、createdの場合、beforeCreateがまだinitStateになっていないことです.
  • inject/provideはコンポーネント通信を実現する新しい方式であり、親子、子孫などの通信方式を実現することができる.

  • まとめ
    この文章は主にnew Vueの過程を述べて、異なる機能が異なる関数に分割されて実行されることを見ることができて、筋道の論理ははっきりしていて、主線は明確で、初期化の最後に、elの属性があれば、vm.$mountの方法を呼び出してマウントして、マウントの目的はテンプレートを最終的なDOMにレンダリングすることです.