9.render helper

render helper

什么是 render helper

在 编译器 中我们说到,编译器最后会生成渲染函数。组件更新时,执行渲染函数,就可以得到组件的 vnode。

渲染函数之所以能生成 vnode 是通过其中的 _c、_l、_v、_s 等方法实现的,在 编译后 的代码中我们能看到很多类似函数的调用

  • 普通的节点被编译成了可执行 _c 函数
  • v-for 节点被编译成了可执行的 _l 函数

作用总结:在 Vue 实例上挂载一些运行时的工具方法,这些方法用在编译器生成的渲染函数中,用于生成组件的 VNode。

入口

Vue 实例的入口文件(2-Vue初始化过程),包括 Vue 构造函数的定义、各个实例方法的初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Vue 的构造函数
function Vue (options) {
// 调用 Vue.prototype._init 方法,该方法是在 initMixin 中定义的
this._init(options)
}

// 定义 Vue.prototype._init 方法
initMixin(Vue)
/**
* 定义:
* Vue.prototype.$data
* Vue.prototype.$props
* Vue.prototype.$set
* Vue.prototype.$delete
* Vue.prototype.$watch
*/
stateMixin(Vue)
/**
* 定义 事件相关的 方法:
* Vue.prototype.$on
* Vue.prototype.$once
* Vue.prototype.$off
* Vue.prototype.$emit
*/
eventsMixin(Vue)
/**
* 定义:
* Vue.prototype._update
* Vue.prototype.$forceUpdate
* Vue.prototype.$destroy
*/
lifecycleMixin(Vue)
/**
* 执行 installRenderHelpers,在 Vue.prototype 对象上安装运行时便利程序
*
* 定义:
* Vue.prototype.$nextTick
* Vue.prototype._render
*/
renderMixin(Vue)
1
2
3
4
5
6
7
export function renderMixin (Vue: Class<Component>) {
// install runtime convenience helpers
// 在组件实例上挂载一些运行时需要用到的工具方法
installRenderHelpers(Vue.prototype)

// ...
}

初始化时调用了 renderMixin,renderMixin 中调用了 Install RenderHelpers 挂载一些工具方法,也就是 _o _c 等等

installRenderHelpers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 在实例上挂载简写的渲染工具函数,这些都是运行时代码
* 这些工具函数在编译器生成的渲染函数中被使用到了
* @param {*} target Vue 实例
*/
export function installRenderHelpers(target: any) {
/**
* v-once 指令的运行时帮助程序,为 VNode 加上打上静态标记
* 有点多余,因为含有 v-once 指令的节点都被当作静态节点处理了,所以也不会走这儿
*/
target._o = markOnce
// 将值转换为数字
target._n = toNumber
/**
* 将值转换为字符串形式,普通值 => String(val),对象 => JSON.stringify(val)
*/
target._s = toString
/**
* 运行时渲染 v-for 列表的帮助函数,循环遍历 val 值,依次为每一项执行 render 方法生成 VNode,最终返回一个 VNode 数组
*/
target._l = renderList
target._t = renderSlot
/**
* 判断两个值是否相等
*/
target._q = looseEqual
/**
* 相当于 indexOf 方法
*/
target._i = looseIndexOf
/**
* 运行时负责生成静态树的 VNode 的帮助程序,完成了以下两件事
* 1、执行 staticRenderFns 数组中指定下标的渲染函数,生成静态树的 VNode 并缓存,下次在渲染时从缓存中直接读取(isInFor 必须为 true)
* 2、为静态树的 VNode 打静态标记
*/
target._m = renderStatic
target._f = resolveFilter
target._k = checkKeyCodes
target._b = bindObjectProps
/**
* 为文本节点创建 VNode
*/
target._v = createTextVNode
/**
* 为空节点创建 VNode
*/
target._e = createEmptyVNode
}

面试题

一个组件是如何变成 VNode?

  • 组件实例初始化,最后执行 $mount 进入挂载阶段
  • 如果是只包含运行时的 vue.js,则直接进入挂载阶段,因为这时候的组件已经变成了渲染函数,编译过程通过模块打包器 + vue-loader + vue-template-compiler 完成的
  • 如果没有使用预编译,则必须使用全量的 vue.js
  • 挂载时如果发现组件配置项上没有 render 选项,则进入编译阶段
  • 将模版字符串编译成 AST 语法树,其实就是一个普通的 JS 对象
  • 然后优化 AST,遍历 AST 对象,标记每一个节点是否为静态静态;然后再进一步标记出静态根节点,在组件后续更新时会跳过这些静态节点的更新,以提高性能
  • 接下来从 AST 生成渲染函数,生成的渲染函数有两部分组成:
    • 负责生成动态节点 VNode 的 render 函数
    • 还有一个 staticRenderFns 数组,里面每一个元素都是一个生成静态节点 VNode 的函数,这些函数会作为 render 函数的组成部分,负责生成静态节点的 VNode
  • 接下来将渲染函数放到组件的配置对象上,进入挂载阶段,即执行 mountComponent 方法
  • 最终负责渲染组件和更新组件的是一个叫 updateComponent 方法,该方法每次执行前首先需要执行 vm._render 函数,该函数负责执行编译器生成的 render,得到组件的 VNode
  • 将一个组件生成 VNode 的具体工作是由 render 函数中的 _c、_o、_l、_m 等方法完成的,这些方法都被挂载到 Vue 实例上面,负责在运行时生成组件 VNode

下面说的有点多,其实记住一句就可以了,设置组件配置信息,然后通过 new VNode(组件信息) 生成组件的 VNode

  • _c,负责生成组件或 HTML 元素的 VNode,_c 是所有 render helper 方法中最复杂,也是最核心的一个方法,其它的 _xx 都是它的组成部分
    • 接收标签、属性 JSON 字符串、子节点数组、节点规范化类型作为参数
    • 如果标签是平台保留标签或者一个未知的元素,则直接 new VNode(标签信息) 得到 VNode
    • 如果标签是一个组件,则执行 createComponent 方法生成 VNode
      • 函数式组件执行自己的 render 函数生成 VNode
      • 普通组件则实例化一个 VNode,并且在在 data.hook 对象上设置 4 个方法,在组件的 patch 阶段会被调用,从而进入子组件的实例化、挂载阶段,然后进行编译生成渲染函数,直至完成渲染
      • 当然生成 VNode 之前会进行一些配置处理比如:
        • 子组件选项合并,合并全局配置项到组件配置项上
        • 处理自定义组件的 v-model
        • 处理组件的 props,提取组件的 props 数据,以组件的 props 配置中的属性为 key,父组件中对应的数据为 value 生成一个 propsData 对象;当组件更新时生成新的 VNode,又会进行这一步,这就是 props 响应式的原理
        • 处理其它数据,比如监听器
        • 安装内置的 init、prepatch、insert、destroy 钩子到 data.hooks 对象上,组件 patch 阶段会用到这些钩子方法
  • _l,运行时渲染 v-for 列表的帮助函数,循环遍历 val 值,依次为每一项执行 render 方法生成 VNode,最终返回一个 VNode 数组
  • _m,负责生成静态节点的 VNode,即执行 staticRenderFns 数组中指定下标的函数

参考

https://juejin.cn/post/6963048982079602696


9.render helper
http://example.com/2022/10/26/9-render-helper/
Author
John Doe
Posted on
October 26, 2022
Licensed under