Skip to content

实现Fragment

本节课代码地址(请参考课程获取) 为了提高组件结构灵活性,需要实现Fragment ,具体来说,需要区分几种情况:

1. Fragment包裹其他组件

jsx
<>
<div></div>

<div></div>

</>
// 对应DOM

<div></div>

<div></div>

这种情况的JSX转换结果

jsxs(Fragment, {

children: [

jsx("div", {}),

jsx("div", {})

]
});

typeFragmentReactElement ,对单一节点的Diff 需要考虑Fragment 的情况。

2. Fragment与其他组件同级

jsx
<ul>
<>
<li>1</li>

<li>2</li>

</>

<li>3</li>

<li>4</li>

</ul>
// 对应DOM

<ul>

<li>1</li>

<li>2</li>

<li>3</li>

<li>4</li>

</ul>

这种情况的JSX转换结果

jsxs('ul', {

children: [

jsxs(Fragment, {

children: [

jsx('li', {

children: '1'

}),

jsx('li', {

children: '2'

})

]

}),

jsx('li', {

children: '3'

}),

jsx('li', {

children: '4'

})

]

});

children 为数组类型,则进入reconcileChildrenArray 方法,数组中的某一项为 Fragment ,所以需要增加「type为Fragment的ReactElement的判断」,同时 beginWork 中需要增加Fragment 类型的判断。

3. 数组形式的Fragment

jsx
// arr = [<li>c</li>, <li>d</li>]
<ul>

<li>a</li>

<li>b</li>

{arr}

</ul>
// 对应DOM

<ul>

<li>a</li>

<li>b</li>

<li>c</li>

<li>d</li>

</ul>

这种情况的JSX转换结果

jsxs('ul', {

children: [

jsx('li', {

children: 'a'

}),

jsx('li', {

children: 'b'

}),

arr

]

});

children 为数组类型,则进入reconcileChildrenArray 方法,数组中的某一项为数组, 所以需要增加「reconcileChildrenArray中数组类型的判断」。

Fragment对ChildDeletion的影响

ChildDeletion 删除DOM 的逻辑:

找到子树的根Host节点 找到子树对应的父级Host节点 从父级Host节点中删除子树根Host节点 考虑删除p节点的情况:

jsx
<div>

<p>xxx</p>

</div>

考虑删除Fragment 后,子树的根Host 节点可能存在多个:

jsx
<div>
<>

<p>xxx</p>

<p>yyy</p>

</>

</div>

对React的影响

React 包需要导出Fragment ,用于JSX 转换引入Fragment 类型 详见fix: react 导出Fragment 纠错 当嵌套数组类型JSX (比如这个Demo)时,由于我们实现的源码中updateFromMap 方法中 如下代码没有考虑传入的element 可能为数组形式:

const keyToUse = element.key !== null ? element.key : index;

导致element 为数组形式时keyToUseundefined ,进而导致Fragment 不能复用,造 成bug 。 为了解决这个问题,这种情况下可以使用index 作为key ,修改如下:

function getElementKeyToUse(element: any, index?: number): Key {

if (

Array.isArray(element) ||

typeof element === 'string' ||

typeof element === 'number'
) {

return index;

}

return element.key !== null ? element.key : index;

}

详见fix: fragment array没有key

用心学习,用代码说话 💻