《Vuejs设计与实现》8.11 Fragment

362 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情

我最早知道Fragment 是在react,他表示一个虚拟的根节点,让我们写组件的时候不需要每个组件都必须有根节点。在vue2中并没有实现这个功能,在vue3中终于完整的实现了这个功能,甚至某种情况下,我觉得比react更好用。

在vue2时代,如果我们想做这样一个列表,你会发现ListItem这个组件报错了,它只能有一个根节点,于是你不得不新声明一个数组,然后用v-for去展示。

image.png

但是在vue3中,却可以直接这样写,原因就是因为vue3实现了FragmentFragment中文译为碎片,片段。其实我们可以把这理解成为一个虚拟的根节点,里面可以包含任意的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去渲染,有空的话我会自己寻找答案,当然如果有大佬肯在评论区里告诉我一下更好。