Skip to content

渲染过程解析 ​

渲染管线 ​

_render()                           _update()
  │                                    │
  ├── 执行 render 函数                 ├── 首次渲染 → 创建 DOM
  │   └── h("div", ...)                │   └── createElm()
  │   └── $createElement()             │
  │                                    ├── 更新 → 对比 VNode
  └── 返回 VNode 树                    │   └── patch(oldVNode, newVNode)
                                       │
                                       └── 最终更新真实 DOM

_render 源码 ​

js
// src/core/instance/render.js

Vue.prototype._render = function () {
  const vm = this
  const { render, _parentVnode } = vm.$options

  // 设置当前实例(用于 resolveSlots)
  vm.$vnode = _parentVnode

  let vnode
  try {
    currentRenderingInstance = vm
    // 执行 render 函数,返回 VNode 树
    vnode = render.call(vm._renderProxy, vm.$createElement)
  } catch (e) {
    handleError(e, vm, "render")
  } finally {
    currentRenderingInstance = null
  }

  // 根 VNode 只有一个(多个根节点用 fragment 包装)
  vnode.parent = _parentVnode
  return vnode
}

_update 源码 ​

js
// src/core/instance/lifecycle.js

Vue.prototype._update = function (vnode) {
  const vm = this
  const prevEl = vm.$el
  const prevVnode = vm._vnode // 上一次的 VNode

  vm._vnode = vnode // 缓存本次 VNode

  if (!prevVnode) {
    // 首次渲染:直接创建真实 DOM
    vm.$el = vm.__patch__(vm.$el, vnode)
  } else {
    // 更新:对比新旧 VNode,最小化 DOM 操作
    vm.$el = vm.__patch__(prevVnode, vnode)
  }
}

$createElement (h 函数) ​

js
// src/core/vdom/create-element.js

export function createElement(context, tag, data, children) {
  let vnode
  if (typeof tag === "string") {
    // 原生标签:div、span...
    vnode = new VNode(tag, data, children, undefined, undefined, context)
  } else if (isDef((tag.componentOptions || tag).Ctor)) {
    // 组件:创建组件 VNode
    vnode = createComponent(tag, data, context, children)
  }
  return vnode
}

从 template 到 DOM 的完整链路 ​

template: <div class="app">{{ msg }}</div>
    │
    │ compile (构建时或运行时)
    ▼
render: function() {
    return _c("div", { class: "app" }, [_v(_s(msg))])
}
    │
    │ _render() 执行
    ▼
VNode: { tag: "div", children: [{ text: "hello" }], ... }
    │
    │ _update(vnode) → __patch__()
    ▼
DOM: <div class="app">hello</div>

_c 就是 $createElement 的简写,是编译后的 render 函数中对 h 函数的调用。

基于 Vue 2.7 & Vue 3.4 源码