3.2 初识渲染器
渲染器的作用就是把虚拟 DOM 渲染为真实 DOM。
一、渲染器的实现思路
| 步骤 | 做法 |
|---|---|
| 创建元素 | 用 vnode.tag 作为标签名调用 document.createElement |
| 添加属性与事件 | 遍历 vnode.props,以 on 开头的 key 用 addEventListener 绑定 |
| 处理 children | 字符串 → 文本子节点;数组 → 递归调用渲染器 |
二、虚拟 DOM
const vnode = {
tag: 'div', // 标签名称
props: {
onClick: () => alert('hello')
},
children: 'click me' // 子节点
}
三、最简渲染器实现
function renderer(vnode, container) {
// 1. 使用 vnode.tag 作为标签名称创建 DOM 元素
const el = document.createElement(vnode.tag)
// 2. 遍历 vnode.props,将属性、事件添加到 DOM 元素
for (const key in vnode.props) {
if (/^on/.test(key)) {
// 以 on 开头即为事件,如 onClick
el.addEventListener(
key.substr(2).toLowerCase(), // 事件名称 onClick -> click
vnode.props[key] // 事件处理函数
)
}
}
// 3. 处理 children
if (typeof vnode.children === 'string') {
// 字符串 → 文本子节点
el.appendChild(document.createTextNode(vnode.children))
} else if (Array.isArray(vnode.children)) {
// 数组 → 递归渲染子节点,以当前元素 el 作为挂载点
vnode.children.forEach(child => renderer(child, el))
}
// 4. 将元素添加到挂载点下
container.appendChild(el)
}
renderer(vnode, document.body) // body 作为挂载点
事件名转换靠
on前缀:去掉前两个字符再转小写,onClick→click。el必须最后才挂到container上,否则递归时子节点会先于父节点出现在页面上。 递归时挂载点换成当前元素el,而不是继续用container,这样层级结构才正确。
四、真实渲染器的三个入口
上面只是教学用的最简版本。真正的渲染器对外暴露三个入口,第 7 章会展开:
| 入口 | 职责 |
|---|---|
render | 接收 vnode 与新内容,决定是初次挂载还是更新 |
mount | 首次渲染,创建 DOM 并插入容器 |
patch | 更新,比较新旧 vnode 后只改动变化的部分 |
相关
- 3.1 声明式地描述 UI:虚拟 DOM 是什么、怎么描述
- 3.3 组件的本质:vnode.tag 不是字符串时怎么办