函数组件在React中是一个非常重要的概念,他可以帮助我们精简代码结构,根据功能拆分不同的组件并加以复用。
但上一篇的逻辑中,我们只对普通标签和文本做了处理,并没有函数组件相关的逻辑。在这一篇的文章中我们要分析函数组件并实现他的渲染。
针对函数组件生成虚拟dom的处理
通过以下示例可以看出,函数组件本质上是一个纯函数,其核心特征是通过执行函数来生成对应的DOM结构,因此,在生成dom的相关逻辑中,可以针对此特性进行判断。
const Counter = ({ num }) => {
return (
<div>Counter: {num}</div>
)
}
// 执行 Counter 后 可以得到里面dom结构
函数组件相当于是一个盒子,执行函数组件就是开盒的过程,开盒完成后就可以获取到对应的dom,但是函数组件会留在dom树中,所以要考虑到函数组件没有真实dom,并不能挂载的情况。
相关逻辑优化
在虚拟DOM的创建过程中,考虑到函数组件的传参,所以在执行函数组件的时候要将props传进去。
因此在 createElement 函数中做一下兼容。
const createElement = (type, props, ...children) => {
return {
type,
props: {
...props,
children: children.map(child => {
// 针对传参进行兼容,扩展基础类型支持
const isPrimitive = ['string', 'number'].includes(typeof child)
return isPrimitive ? createTextEl(child) : child
})
}
}
}
同时在上一篇创建链表 perfromFiberOfUnit 的逻辑中也要针对函数组件进行处理。
function performFiberOfUnit(fiber) {
// 判断当前dom是不是函数组件,不是才执行创建dom方法
const isFunctionComponent = typeof fiber.type === 'function'
// 仅处理原生DOM组件
if(!isFunctionComponrnt){
if(!fiber.dom){
// 1.创建dom节点
// ...
// 2.设置props
// ...
}
}
// 因为执行函数组件将直接得到dom,所以需要手动包裹成数组,并将对应的props传进去
const children = isFunctionComponrnt
? [fiber.type(fiber.props)]
: fiber.props.children
// 转化成链表,做好指针关系
// ...
}
考虑到函数组件执行后其中可能没有dom结构的情况
function commitWork(work){
// ...
// 兼容function component的情况。
// 因为在function component的dom节点是null,所以要循环往上查找真实的dom并挂载
while(!parentWork.dom){
parentWork = parentWork.parent
}
// ...
}