Vue源碼解析(1)-模版渲染

首先在init.js裏調用$mount函數
把el #app獲取相應dom傳遞過去vue

Vue.prototype._init = function (options) {
   ...
   if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }   
}

entry-runtime-with-compiler.js裏在Vue原型上定義$mount方法node

Vue.prototype.$mount = function (
  el,
  hydrating
) { 
       let template = options.template
       template = idToTemplate(template)
       const { render, staticRenderFns } = compileToFunctions(template , {
        shouldDecodeNewlines, // 對瀏覽器的怪癖作兼容,布爾值。
        shouldDecodeNewlinesForHref, // 對瀏覽器的怪癖作兼容,布爾值。
        delimiters: options.delimiters, //vue透傳
        comments: options.comments //vue透傳
      }, this)
 }

圖片描述

template(64)和 render(72), staticRenderFns(73) 對應圖中所示react

render是渲染函數,staticrenderfns是靜態樹的渲染函數web

//render
function anonymous(
) {
with(this){return _c('div',[_c('h1',{staticStyle:{"color":"red"}},[_v("我是選項模板3")]),_v(" "),_c('p',[_v(_s(number))]),_v(" "),_c('p',[_v(_s(message))]),_v(" "),_m(0)])}
}  
//staticrenderfns
function anonymous(
) {
with(this){return _c('div',[_c('div',[_v("\n          1\n          "),_c('div',[_v("11")]),_v(" "),_c('div',[_v("12")])]),_v(" "),_c('div',[_v("2")]),_v(" "),_c('div',[_v("3")]),_v(" "),_c('div',[_v("4")]),_v(" "),_c('div',[_v("5")])])}
}
//template
<template id="demo">
    <div>
      <h1 style="color:red">我是選項模板3</h1>
      <p>{{number}}</p>
      <p>{{message}}</p>
      <div>
        <div>
          1
          <div>11</div>  
          <div>12</div>  
        </div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
      </div>
    </div>
  </template>

children多的放進了staticrenderfns 其他的放進了render 至於爲何這麼作,傳送門
從ast到render過程segmentfault

compileToFunctions來源
platform文件下 compiler文件 index.js文件瀏覽器

const { compile, compileToFunctions } = createCompiler(baseOptions)

createCompiler來源
core文件下 compiler文件 index.js文件app

export const createCompiler = createCompilerCreator(function baseCompile (
  template,
  options
) {
  // 使用 parse 函數將模板解析爲 AST
  const ast = parse(template.trim(), options)
 
  // ast對象進行優化,找出ast對象中全部靜態子樹
  if (options.optimize !== false) {
    optimize(ast, options)
  }
  // 根據給定的AST生成最終的目標平臺的代碼
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

createCompilerCreator來源
creact-compiler.js下dom

export function createCompilerCreator (baseCompile) {
  return function createCompiler (baseOptions) {
    function compile (
      template,
      options
    ) {
      const finalOptions = Object.create(baseOptions)
    // 執行createCompilerCreator裏傳來的函數 生生ast等
      const compiled = baseCompile(template, finalOptions)
      compiled.errors = errors
      compiled.tips = tips
      return compiled
    }

    return {
      compile,
      compileToFunctions: createCompileToFunctionFn(compile)
    }
  }
}

compiled以下即函數

return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
compiled.errors = errors
compiled.tips = tips

圖片描述

總之,在$mount原型函數裏面$mount
給 vue.options 掛載了 render和staticRenderFns
options.render = render
options.staticRenderFns = staticRenderFnsoop

打印 options

圖片描述

掛在後須要渲染

lifecycle.js

mountComponent(){
   callHook(vm, 'beforeMount')
   updateComponent = () => {
      vm._update(vm._render(), hydrating)
   }
   // 把更新組件加入依賴
   new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
}

vm._render()生成傳說中的VNode,即虛擬dom

vm._render()執行函數結果

圖片描述

vm._update方法的實現

Vue.prototype._update = function (vnode, hydrating) {
   // 判斷vnode是否初始化過
    if (!prevVnode) {
      // initial render
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
      // updates
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
  }

經過__patch__更新

在 platform/web/runtime/index.js文件裏執行了mountComponent方法

import { mountComponent } from 'core/instance/lifecycle'


Vue.prototype.$mount = function (
  el,
  hydrating
) {
  el = el && inBrowser ? query(el) : undefined
  // 調用mountComponent方法
  return mountComponent(this, el, hydrating)
}

最終效果圖
圖片描述

相關文章
相關標籤/搜索