由下面的例子可以看出,函数式组件实际上是一个函数,执行此函数可以获得一个dom
const Counter = ({num}) => {
return (
<div>Counter: {num}</div>
)
}
因此,在生成dom的相关逻辑中,可以针对此特性进行判断。
要考虑到函数组件的传参,所以在执行函数组件的时候要降props传进去。在创建dom的地方做一下兼容
const createElement = (type, props, ...children) => {
return {
type,
props: {
...props,
children: children.map(child => {
// 针对传参进行兼容
const isTextNode = typeof child === 'string' || typeof child === 'number'
return isTextNode ? createTextEl(child) : child
})
}
}
}
函数组件生成dom
function perfromFiberOfUnit(fiber){
// 判断当前dom是不是函数组件,不是才执行创建dom方法
const isFunctionComponrnt = typeof fiber.type === 'function'
if(!isFunctionComponrnt){
if(!fiber.dom){
// 1.创建dom节点
// ...
// 2.设置props
// ...
}
}
// 因为执行函数组件将直接得到dom,所以需要手动包裹成数组,并将对应的props传进去
const children = isFunctionComponrnt ? [fiber.type(fiber.props)] : fiber.props.children
// 转化成链表,做好指针关系
// ...
}
这样就基本上完成了对function component的实现。
函数组件相当于是一个盒子,执行函数组件就是开盒的过程,开盒完成后就可以获取到对应的dom,但是函数组件会留在dom树中,所以要考虑到函数组件没有真实dom,并不能挂载的情况。
因此有一些前置优化的地方来做个提醒:
function commitWork(work){
// ...
// 兼容function component的情况。
// 因为在function component的dom节点是null,所以要循环往上查找真实的dom并挂载
while(!parentWork.dom){
parentWork = parentWork.parent
}
// ...
}