vue3 源码学习,实现一个 mini-vue(十):构建 renderer 渲染器之 Text Comment 节点的渲染
前言
原文来自 我的个人博客
在之前的章节中我们实现了 ELEMENT 节点的挂载与更新,以及 class、style、事件 属性等等的挂载与更新。
本章我们就在来实现一下 Text、Comment 节点的渲染
1. TEXT 节点的挂载与更新
首先创建测试实例:
<script>
const { h, render, Text } = Vue
const vnode = h(Text, 'hello world')
// 挂载
render(vnode, document.querySelector('#app'))
// 延迟两秒,生成新的 vnode,进行更新操作
setTimeout(() => {
const vnode2 = h(Text, '你好,世界')
render(vnode2, document.querySelector('#app'))
}, 2000)
</script>
1.1 源码阅读
我们知道,对于节点的打补丁操作是从 packages/runtime-core/src/renderer.ts 中的 render 函数开始的,所以我们可以直接在这里进行 debugger:

- 第一次进入
render,执行挂载操作,可以看到render方法会执行patch方法:

- 在
patch方法中 会进行switch(type)判断,因为此时我们是TEXT节点 所以会进入processText方法。同时我们也看到了下面Comment会进入processCommentNode方法Fragment会进入processFragment方法。我们先进入processText方法:

- 在
processText方法中,因为n1 == null,所以会执行hostInsert方法,但是在hostInsert中的第一个参数会先调用hostCreateText,所以执行顺序是先执行hostCreateText在执行hostInsert:

-
可以看到这两个方法都是在源码
/packages/runtime-dom/src/nodeOps.ts文件中,之前我们也说过,vue为了浏览器兼容性,特定将操作dom的操作都放在了runtime-dom的文件夹下了,好我们再来看着两个方法,第一个hostCreateText方法很简单就是执行了createTextNode生成Text节点,第二个hostInsert方法则执行了insertBefore,该方法我们之前实现过,就不在多说了。 -
至此 挂载 操作完成
-
延迟两秒,第二次进入
render方法,执行 更新操作 -
第二次依然会 进入
renderpatchprocessText方法,我们直接从processText方法调试:

- 在
processText中,n1.children = 'hello world',n2.children = '你好世界',if条件成立,所以会进入到hostSetText:

-
而
hostSetText执行了node.nodeValue = text修改了value -
至此,更新操作完成。
总结:
由以上代码可知:
- 对于
Text节点的 挂载和更新,整体是非常简单的: - 挂载:通过
doc.createTextNode(text)生成节点,在通过insertBefore插入 - 更新:通过
node.nodeValue直接指定即可。
1.2 代码实现
明确好了 Text 的源码逻辑之后,那么接下来我们就实现一下对应的代码:
- 在
packages/runtime-core/src/renderer.ts中,增加processText方法:
/**
* Text 的打补丁操作
*/
const processText = (oldVNode, newVNode, container, anchor) => {
// 不存在旧的节点,则为 挂载 操作
if (oldVNode == null) {
// 生成节点
newVNode.el = hostCreateText(newVNode.children as string)
// 挂载
hostInsert(newVNode.el, container, anchor)
}
// 存在旧的节点,则为 更新 操作
else {
const el = (newVNode.el = oldVNode.el!)
if (newVNode.children !== oldVNode.children) {
hostSetText(el, newVNode.children as string)
}
}
}
- 为
RendererOptions增加createText与setText方法:
/**
* 渲染器配置对象
*/
export interface RendererOptions {
...
/**
* 创建 Text 节点
*/
createText(text: string)
/**
* 设置 text
*/
setText(node, text): void
}
- 为
options增加解析:
/**
* 解构 options,获取所有的兼容性方法
*/
const {
...
createText: hostCreateText,
setText: hostSetText
} = options
- 在
patch方法中,处理Text节点:
case Text:
// Text
processText(oldVNode, newVNode, container, anchor)
break
- 在
packages/runtime-dom/src/nodeOps.ts增加createText和setText方法:
/**
* 创建 Text 节点
*/
createText: text => doc.createTextNode(text),
/**
* 设置 text
*/
setText: (node, text) => {
node.nodeValue = text
}
代码完成。
创建测试实例 packages/vue/examples/runtime/render-text.html:
<script>
const { h, render, Text } = Vue
const vnode = h(Text, 'hello world')
// 挂载
render(vnode, document.querySelector('#app'))
// 延迟两秒,生成新的 vnode,进行更新操作
setTimeout(() => {
const vnode2 = h(Text, '你好,世界')
render(vnode2, document.querySelector('#app'))
}, 2000)
</script>
测试挂载和更新成功
2. COMMENT 节点的挂载和更新
完成了 Text 节点的挂载、更新之后,那么下面我们来看 Comment 节点的挂载操作。
这里要注意的是:vue 不支持动态的 comment,即没有更新操作
其实对于 Comment 而言,它的整体流程和 Text 非常类似。我们在 1.1 源码阅读的第 2 步中就已经说过了,它会进入 processCommentNode 方法,下面我们创建一个测试实例直接进入这个方法
<script>
const { h, render, Comment } = Vue
const vnode = h(Comment, 'hello world')
// 挂载
render(vnode, document.querySelector('#app'))
</script>
2.1 源码阅读
- 进入
processCommentNode方法:

-
从上图可以看出挂载操作会进入到
hostInsert,而hostInsert实际上就是执行的createComment。另外也可以看到在else中的注释以及代码,说明vue确实不支持动态的comment,而是直接替换了el -
至此,挂载成功
总结:
由以上代码可知:
- 对于
Comment而言,只存在 挂载 操作 - 整体的处理非常简单:
- 通过
doc.createComment创建Comment节点 - 通过
hostInsert完成挂载
- 通过
2.2 代码实现
- 在
packages/runtime-core/src/renderer.ts中:
export interface RendererOptions {
...
/**
* 设置 text
*/
createComment(text: string)
}
const {
...
createComment: hostCreateComment
} = options
/**
* Comment 的打补丁操作
*/
const processCommentNode = (oldVNode, newVNode, container, anchor) => {
if (oldVNode == null) {
// 生成节点
newVNode.el = hostCreateComment((newVNode.children as string) || '')
// 挂载
hostInsert(newVNode.el, container, anchor)
} else {
// 无更新
newVNode.el = oldVNode.el
}
}
// patch 方法中 switch 逻辑
case Comment:
// Comment
processCommentNode(oldVNode, newVNode, container, anchor)
break
- 在
packages/runtime-dom/src/nodeOps.ts中,增加createComment方法:
/**
* 创建 Comment 节点
*/
createComment: (text) => doc.createComment(text)
代码完成。
创建测试实例 packages/vue/examples/runtime/render-comment.html:
<script>
const { h, render, Text } = Vue
const vnode = h(Text, 'hello world')
// 挂载
render(vnode, document.querySelector('#app'))
// 延迟两秒,生成新的 vnode,进行更新操作
setTimeout(() => {
const vnode2 = h(Text, '你好,世界')
render(vnode2, document.querySelector('#app'))
}, 2000)
</script>
测试成功
转载自:https://juejin.cn/post/7185819975376961597