携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情
我最早知道Fragment 是在react,他表示一个虚拟的根节点,让我们写组件的时候不需要每个组件都必须有根节点。在vue2中并没有实现这个功能,在vue3中终于完整的实现了这个功能,甚至某种情况下,我觉得比react更好用。
在vue2时代,如果我们想做这样一个列表,你会发现ListItem这个组件报错了,它只能有一个根节点,于是你不得不新声明一个数组,然后用v-for去展示。
但是在vue3中,却可以直接这样写,原因就是因为vue3实现了Fragment。Fragment中文译为碎片,片段。其实我们可以把这理解成为一个虚拟的根节点,里面可以包含任意的DOM结构。在原生js中也有类似的API,document.createDocumentFragment。
我们现在尝试实现一下这个Fragment自定义节点。
const Fragment = Symbol()
const vnode = {
type:Fragment,
children:[
{type:'li',children:'1'},
{type:'li',children:'2'},
{type:'li',children:'3'}
]
}
当我们渲染Fragment时,只需要把渲染所有的children就可以了.
const patch = (oldNode,newNode,container)=>{
/*省略其他逻辑*/
if(newNode.type === Fragment){
if(oldNode){
patchChildren(oldNode,newNode,container)
} else {
newNode.children?.forEach(node=>patch(null,newNode,container))
}
}
}
这里要注意一下,当旧node存在的时候,就有可能之前就是一个Fragment,因此我们需要patchChildren,对子元素进行diff,如果不存在旧node,那只需要递归挂载就行了。
其实渲染Fragment比渲染节点更简单,因为我们无需管什么事件,什么属性,Fragment只能有一个children字段。
当然别忘了,卸载的时候也要判断Fragment,然后递归卸载。
到这里,就是vue3的Fragment了,但是我很好奇的是,vue2不能这样支持Fragment吗?
在我看来,vue2和3的虚拟DOM上的实现应该差不多,那么唯一的问题的就是出在渲染器了,vue2在渲染的时候必须有一个根节点,就好比我们patch的时候,必须传入oldNode一样,他不支持传入一个null去渲染,有空的话我会自己寻找答案,当然如果有大佬肯在评论区里告诉我一下更好。