首先在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) }
最終效果圖