demo

<div id="app">  {{ message }}</div>
var app = new Vue({  el: '#app',  data: {    message: 'Hello Vue!'  }})

简要流程图

源码详细流程

src/core/index.js

这里主要是导出真正的Vue函数,初始化全局API

import Vue from './instance/index'initGlobalAPI(Vue)export default Vue

src/core/instance/index.js

实例化的时候调用this._init方法

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

src/core/instance/init.js

这一块主要是初始化一系列的属性和方法。然后调用$mount方法挂载el元素。

Vue.prototype._init = function (options?: Object) {     ...     vm.$options = mergeOptions(        resolveConstructorOptions(vm.constructor),        options || {},        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')    ...        if (vm.$options.el) {      // 挂载el属性      vm.$mount(vm.$options.el)    }}

src/platforms/web/entry-runtime-with-compiler.js

两个点,调用compileToFunctions函数生成render函数备用, 调用mount函数开始执行真正的挂载流程。

// 这个mount指向下面这个文件的Vue.prototype.$mountconst mount = Vue.prototype.$mount// 在这里主要是使用compileToFunctions来编译模板,生成render函数,以供后用。// 主体的mount函数还是使用原来的$mount方法Vue.prototype.$mount = function (  el?: string | Element,  hydrating?: boolean): Component {    el = el && query(el)    ...    if (!options.render) {        ...      template = getOuterHTML(el)        // 调用compileToFunctions函数,得到render, staticRenderFns      const { render, staticRenderFns } = compileToFunctions(template, {        outputSourceRange: process.env.NODE_ENV !== 'production',        shouldDecodeNewlines,        shouldDecodeNewlinesForHref,        delimiters: options.delimiters,        comments: options.comments      }, this)      // 更新options属性      options.render = render      options.staticRenderFns = staticRenderFns            }        // 调用原本的mount方法    return mount.call(this, el, hydrating)}// 对外导出的 Vue(这个vue指向下面这个文件)export default Vue

src/platforms/web/runtime/index.js

一个点,执行mountComponent开始挂载组件

import { mountComponent } from 'core/instance/lifecycle'// install platform patch functionVue.prototype.__patch__ = inBrowser ? patch : noop// public mount methodVue.prototype.$mount = function (  el?: string | Element,  hydrating?: boolean): Component {  el = el && inBrowser ? query(el) : undefined  return mountComponent(this, el, hydrating)}export default Vue

src/core/instance/lifecycle.js

然后会执行到mountComponent函数。在实例化Watcher时,会执行vm._render(), vm._update()方法,来看这两个重要方法

export function mountComponent (  vm: Component,  el: ?Element,  hydrating?: boolean): Component {    ...    let updateComponent    ...    updateComponent = () => {      vm._update(vm._render(), hydrating)    }        new Watcher(vm, updateComponent, noop, {        before () {          if (vm._isMounted && !vm._isDestroyed) {            callHook(vm, 'beforeUpdate')          }        }     }, true /* isRenderWatcher */)}

src/core/instance/render.js

这里的$options.render就是第一步调用compileToFunctions的模板编译后的render函数。 所以这一步理解为返回模板对应的vnode

Vue.prototype._render = function (): VNode {    ...    const { render, _parentVnode } = vm.$options    ...    vnode = render.call(vm._renderProxy, vm.$createElement)    ...    return vnode}
  1. src/core/instance/lifecycle.js中的_update方法(这一步的作用是把VNode渲染成真实的DOM)。这一步主要调用 vm.__patch__方法
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {    ...    if (!prevVnode) {      // initial render      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)    } else {      // updates      vm.$el = vm.__patch__(prevVnode, vnode)    }    ...}

src/platforms/web/runtime/index.js

// install platform patch functionVue.prototype.__patch__ = inBrowser ? patch : noop

src/platforms/web/runtime/patch.js

export const patch: Function = createPatchFunction({ nodeOps, modules })

src/core/vdom/patch.js

这个函数超级复杂。作用是依赖vnode递归创建了一个完整的DOM树并插到Body上。

export function createPatchFunction (backend) {    ...        return function patch (oldVnode, vnode, hydrating, removeOnly)        }}

简要流程:

template => vnode tree => DOM tree => 将DOM tree插到body下

核心的两个方法

_render负责将template转为vnode tree, _update负责将vnode tree转为DOM tree, 并且插到body下

参考链接

https://ustbhuangyi.github.io...