12.1 渲染组件
渲染器的
patch原本只认「标签 / 文本 / 注释 / 片段」,把vnode.type是对象的虚拟节点交给mountComponent,组件才算真正被渲染出来。
一、组件的用户接口
- 一个组件就是一个选项对象,至少包含
render函数,其返回值必须是虚拟 DOM; - 描述组件的虚拟节点:
vnode.type直接就是该选项对象。
const MyComponent = {
name: 'MyComponent', // 组件名称,可选
render() {
// 返回虚拟 DOM
return { type: 'div', children: '我是文本内容' }
}
}
// 用来描述组件的 VNode,type 属性值为组件选项对象
const CompVNode = { type: MyComponent }
renderer.render(CompVNode, document.querySelector('#app'))
二、patch 增加组件分支
渲染器用 vnode.type 区分节点类型,因此要新增一个 typeof type === 'object' 的分支:
function patch(n1, n2, container, anchor) {
// ...
if (typeof type === 'string') {
// 普通标签元素
} else if (type === Text) {
// 文本节点
} else if (type === Comment) {
// 注释节点
} else if (type === Fragment) {
// 片段
} else if (typeof type === 'object') {
// n2.type 是对象 → 描述的是组件
if (!n1) {
mountComponent(n2, container, anchor) // 挂载组件
} else {
patchComponent(n1, n2, container) // 更新组件(本节尚未实现)
}
}
}
对象分支要放在最后:字符串、
Text、Comment、Fragment都是更具体的判断。
三、mountComponent:完成组件的挂载
function mountComponent(vnode, container, anchor) {
// vnode.type 就是组件的选项对象
const componentOptions = vnode.type
const { render } = componentOptions
// 执行渲染函数,取得组件要渲染的内容,即子树 subTree
const subTree = render()
// 组件渲染的内容仍是普通虚拟 DOM,交给 patch 继续处理
patch(null, subTree, container, anchor)
}
组件渲染函数返回的虚拟 DOM 称为子树(subTree); 组件虚拟节点自身不产生真实 DOM(没有
vnode.el),它渲染出的 DOM 由subTree决定。
四、这一版还缺什么
| 能力 | 现状 | 补在哪一节 |
|---|---|---|
| 组件自身状态 | 无,render 里只能用静态数据 | 12.2 |
| 自更新 | 无,状态变化不会重新渲染 | 12.2 |
| 组件实例 | 无,分不清挂载与更新 | 12.3 |
| 生命周期 | 无 | 12.3 / 12.8 |
| props | 无 | 12.4 |
| setup | 无 | 12.5 |
| 自定义事件 | 无 | 12.6 |
| 插槽 | 无 | 12.7 |
本节已经调用
patchComponent,但函数体要到 12.4 讲被动更新时才补上。 组件 vnode 没有vnode.el,所以渲染器里的unmount此时只覆盖了Fragment与普通元素,组件卸载在本章实践中尚未实现。
相关
- 12.2 组件状态与自更新:给组件加上 data 状态,并让状态变化驱动重新渲染