Vueにおけるrenderの流れ

5817 ワード

私の前の2つの文章からVueのデータ応答について話しましたが、どのようにrenderとre-renderがありますか?私が見たのは2.1.10というバージョンで、init.jsinitMixin()の初期化コードはこう書いてあります
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initState(vm)  
    callHook(vm, 'created')
    //                
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }

実はinitRender(vm)関数でレンダリングの初期化作業をしていますが、後で詳しくお話しします.Vueには2箇所定義されているVue.prototype.$mount.一つはweb-runtime.js一つはweb-runtime-with-compile.jsで、ここではVueの2つの構築方式について言及する.独立した構築と実行時に構築します.
スタンドアロンコンストラクションとランタイムコンストラクション
独立した構築と実行時の構築の違いは、前者はテンプレートコンパイルを含み、後者は含まれていないことです.テンプレートコンパイルは、テンプレート文字列をレンダリング関数にコンパイルします.Vueのレンダリングには、文字列テンプレートをrender関数にコンパイルする2つのステップがあります.第2の実行プロセスは、render関数を実行することです.独立したコンストラクションはtemplateからrender関数を実行し、templateオプションをサポートしますが、ブラウザインタフェースに依存するため、サーバ側レンダリングとして使用できません.実行時構築にはコンパイル部分は含まれず、render関数を直接実行するためtemplateオプションはサポートされていませんが、コンパイルプロセスがないため、フレームワークをより軽量化できます.実行時にtemplateオプションを書くことはできませんが、単一ファイルコンポーネントにはテンプレートを書くことができます.通常、コンパイラをパッケージ化することなく、vue-loaderとvueifyでテンプレートをプリコンパイルします.
今戻ってコードを見てweb-runtime-with-compile.js
//   './web-runtime'  Vue.prototype.$mount
const mount = Vue.prototype.$mount

//       Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  //  el dom
  el = el && query(el)
  const options = this.$options
  // resolve template/el and convert to render function
 //    render  template  render
  if (!options.render) {
    let template = options.template
    if (template) {
        template = idToTemplate(template)
      } else if (template.nodeType) {
        template = template.innerHTML
      } else {
        return this
      }
    } else if (el) {
      //   el            template
      template = getOuterHTML(el)
    }
    //       template
    if (template) {
      const { render, staticRenderFns } =compileToFunctions(template, {
        warn: msg => warn(msg, this),
        shouldDecodeNewlines,
        delimiters: options.delimiters
      }, this)
      // compileToFunctions   render this.$options
      options.render = render
    }
  }
  //    './web-runtime'  Vue.prototype.$mount   
  //  './web-runtime'  Vue.prototype.$mount'   lifecycle.js  _mount
  return mount.call(this, el, hydrating)
}


一度分析:まずキャッシュした./web-runtimeつまりランタイム構築で定義したVue.prototype.$mountメソッドを再定義し、そのメソッドを再定義し、renderオプションのある直接キャッシュを呼び出す$mount()renderメソッドがなければtemplateオプションがあるかどうかをチェックし、あればtemplateをrenderにコンパイルし、なければelのコードを文字列に変換し、コンパイルしてrenderを生成します.最終的にはrender関数を生成し、キャッシュされたmount関数を呼び出すことがわかります.次にmount関数を見ます.web-runtime.js
Vue.prototwype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  /**
   * query =>   el          ,
   *       create  div,
   *      ,   document.querySelector(el)
   */
  el = el && inBrowser ? query(el) : undefined
  //   _mount   lifecycle.js   
  return this._mount(el, hydrating)
}

最終的に呼び出されるlifecycle.jsで定義されている_mount()であることがわかりますが、この方法を続けてみましょう
Vue.prototype._mount = function (
    el?: Element | void,
    hydrating?: boolean
  ): Component {
    const vm: Component = this
    //     $el
    vm.$el = el
    //  beforeMount      
    callHook(vm, 'beforeMount')

    vm._watcher = new Watcher(vm, function updateComponent () {
      vm._update(vm._render(), hydrating)
    }, noop)

    hydrating = false
    // manually mounted instance, call mounted on self
    // mounted is called for render-created child components in its inserted hook
    //       mount   mounted      
    if (vm.$vnode == null) {
      vm._isMounted = true
      callHook(vm, 'mounted')
    }
    return vm
  }

ええ...上の注釈ははっきり書いてあると思います.ここではこの一言を重点的に述べる
vm._watcher = new Watcher(vm, function updateComponent () {
      vm._update(vm._render(), hydrating)
}, noop)

これまでのデータ応答部で述べたようにWatcherこのコンストラクション関数.Watcherインスタンス作成時にトリガーされるget()、ここでgetが実行updateComponent ()=>vm._update()=>vm.render()、最初に実行されたのはvm.render()であることがわかる.
Vue.prototype._render = function (): VNode {
    const vm: Component = this

    //  $options  render
    //$options.render compileToFunctions template  el     
    const {
      render,
      staticRenderFns,
      _parentVnode
    } = vm.$options
    vm.$vnode = _parentVnode
    // render self
    let vnode
    try {
      vnode = render.call(vm._renderProxy, vm.$createElement)
      ...
    } catch (e) {
      ...
      vnode = vm._vnode
    }
    return vnode
  }
vm._renderProxy = vm

まずわかりやすいです.render()メソッドは、renderを呼び出すvnodeを返します.render印刷してみるとこんな関数
function anonymous() {
  with(this){
    return _c('div',{attrs:{"id":"app"} },
                    [ _v("
"+_s(sum)+"
")] )} }

まず、この関数の中のthisvmそのものを指していることがわかります._c,_v,_s,sumいずれもvmの方法と属性です.取得this.sumでトリガーdefinePropery()get().よし、これでrenderデータ応答に関連付けることができます.new Watcher(updateComponent())を実行すると依存を収集し、依存の値が変化するとwatcherのupdateComponentをトリガーして依存を再収集する.実はtemplateがrenderに変換されると,中間は先に生成された抽象構文ツリー(AST)が静的ノードを抽出し,その後renderに変換する.さらにvnodeのpatchアルゴリズムもあり,この文章ではまだ述べていない.もし后ろのコードがまだ読めたら、私はまた书きます....