学习呦
14 10月 2025 10 min read

渲染

Written by 米豆

渲染器

简单来说,渲染器的工作就是把 vnode 根据类型变成真实 dom,并挂载、更新到页面上,或者进行卸载删除等操作
渲染器是工作在运行时的

流程

通过h()生成对应的 vNode,vNode 其实就是对象,里面的type属性可以区分类型,比如对应的真实 dom 类型,或者自定义组件类型
vNode 对象要交给渲染器render,渲染器内部实现了一系列的方法,包括插入、删除、更新等操作
下面是精简过的源码部分

const rendererOptions = /*#__PURE__*/ extend({ patchProp }, nodeOps)

export const render = ((...args) => {
let renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions)
// 调用的 render 就是下面baseCreateRenderer返回的{render}
renderer.render(...args)
}) as RootRenderFunction<Element | ShadowRoot>

export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement,
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}

// 创建基本的渲染器
// 渲染器中包含各种 vnode 的操作方法,和 dom 操作一致,包括插入、删除等等
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?: typeof createHydrationFunctions,
): any {
// ....上面有很多代码,例如 unmount、patch 等函数的实现

const render: RootRenderFunction = (vnode, container, namespace) => {
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
patch(
container._vnode || null,
vnode,
container,
null,
null,
null,
namespace,
)
}
container._vnode = vnode
}

return {
render
// ....
}
}

h函数

可以使用js来创建vue的虚拟节点(VNode或常说的VDOM),也就是常说的h函数

createVNode()、h()

h函数重载方法很多,只拿出了用的多的两个作为例子

// vue3
// packages/runtime-core/src/h.ts

// 一般标签
export function h<K extends keyof HTMLElementTagNameMap>(
type: K,
props?: (RawProps & HTMLElementEventHandler) | null,
children?: RawChildren | RawSlots,
): VNode

// 组件
export function h<P>(
type: Component<P>,
props?: (RawProps & P) | null,
children?: RawChildren | RawSlots,
): VNode

调用

// <h1 msg="hello">
// <span>子节点内容</span>
// </h1>

const vnode = h(
'h1',
{ msg: 'hello' }, // 相当于 v-bind:msg="hello"
[ h('span', '子节点内容') ] // 子节点
)

h函数是createVNode()的一个别名

原理

原理也很简单,就是返回一个VNode类型的对象

// 极度简化代码
function createVNode(
type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
props: (Data & VNodeProps) | null = null,
children: unknown = null,
): VNode {
const vnode = {
// ... 一堆属性
} as VNode

return vnode
}

使用

完整的渲染函数创建一个VNode的代码如下

// vue2
// 这里创建了一个anchored-heading组件,组件要求有一个名为render的函数,来返回组件模版,props属性则为这个组件接收的props
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level,
[
createElement('a', {
attrs: {
name: headingId,
href: '#' + headingId
}
}, this.$slots.default)
]
)
},
props: {
level: {
type: Number,
required: true
}
}
})

eg2. 创建一个带有作用域插槽的子组件

// vue2
render: function (createElement) {
// `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
return createElement('div', [
createElement('child', {
// 在数据对象中传递 `scopedSlots`
// 格式为 { name: props => VNode | Array<VNode> }
scopedSlots: {
default: function (props) {
return createElement('span', props.text)
}
}
})
])
}
↑