Build your own React_7 函数组件
Posted 一只前端小马甲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Build your own React_7 函数组件相关的知识,希望对你有一定的参考价值。
前端工程师的要求越来越高,仅懂得“三大马车”和调用框架API,已经远不能满足岗位的能力要求。因此增强自身的底层能力,了解框架的内部原理非常重要。本系列文章,翻译自Rodrigo Pombo的《Build your own React》一文,同时每篇文章最后,都会加入自己的理解,一方面记录自己初探React框架原理的过程,另一方面也是想与各位大牛多多交流,以出真知。
我们打算从零开始重写一个React框架,在遵循源码架构的基础上,省略了一些优化和非核心功能代码。
假设你阅读过我之前的文章《build your own React》,那篇文章是基于React 16.8版本,当时还不能使用hooks来替代class。
你可以在Didact仓库找到那篇文章和对应的代码。这里还有个相同主题的视频,跟文章的内容有些区别的,但是可以参考观看。
从零开始重写React框架,我们需要遵循以下步骤:
步骤七:函数组件
接下来我们需要做的是支持函数组件。
首先我们把最初的示例修改一下。我们将它变成一个函数组件,它会返回h1元素。
const Didact = {
createElement,
render,
}
/** @jsx Didact.createElement */
function App(props){
return <h1>Hi {props.name}</h1>
}
const element = <App name="foo" />
const container = document.getElementById("root")
Didact.render(element, container)
然后我们将函数组件的JSX转化为js,就会变成下面这样:
function App(props){
return Didact.createElement(
"h1",
null,
"Hi ",
props.name
)
}
const element = Didact.createElement(App, {
name: "foo",
})
函数组件有两个不同的地方:
- 函数组件生成的fiber没有没有对应DOM节点
- children节点由函数直接返回,而不是通过props属性获取
// requestIdleCallback(workLoop)
//}
//requestIdleCallback(workLoop)
function performUnitOfWork(fiber){
if(!fiber.dom){
fiber.dom = createDom(fiber)
}
const elements = fiber.props.children
reconcileChildren(fiber, elements)
// if(fiber.child){
// return fiber.child
// }
// let nextFiber = fiber
// while(nextFiber){
//}
我们判断纤维对应元素是源自函数,对于函数组件的纤维我们有不同的更新函数。
在updateHostComponent中我们保持跟原来一样。
function performUnitOfWork(fiber){
const isFunctionComponent =
fiber.type instanceof Function
if(isFunctionComponent){
updateFunctionComponent(fiber)
}else{
updateHostComponent(fiber)
}
// if(fiber.child){
// return fiber.child
// }
// let nextFiber = fiber
// while(nextFiber){
// if(nextFiber.sibling){
// return nextFiber.sibling
// }
// }
// nextFiber = nextFiber.parent
}
function updateFunctionComponent(fiber){
// TODO
}
function updateHostComponent(fiber){
if(!fiber.dom){
fiber.dom = createDom(fiber)
}
reconcileChildren(fiber, fiber.props.children)
}
//function reconcileChildren(wipFiber, elements){
// let index = 0
在updateFunctionComponent中,我们运行函数,获得子节点。
在我们的例子中,fiber.type是App函数,当我们执行它会得到h1元素。
然后,一旦我们获得了子元素,调解工作就跟之前一样了,我们不需要再修改任何东西。
//function updateFunctionComponent(fiber){
const children = [fiber.type(fiber.props)]
reconcileChildren(fiber, children)}
//}
//function updateHostComponent(fiber){
// if(!fiber.dom){
// fiber.dom = createDom(fiber)
// }
// reconcileChildren(fiber, fiber.props.children
//}
我们还要修改的是commitWork函数。
function commitWork(fiber){
if(!fiber){
return
}
const domParent = fiber.parent.dom
if(
fiber.effectTag === "PLACEMENT" &&
fiber.dom != null
){
domParent.appendChild(fiber.dom)
}else if(
fiber.effectTag === "UPDATE" &&
fiber.dom != null
){
updateDom(
fiber.dom,
fiber.alternate.props,
fiber.props
)
}else if(fiber.effectTag === "DELETION"){
domParent.removeChild(fiber.dom)
}
commitWork(fiber.child)
commitWork(fiber.sibling)
}
//function render(element, container){
对于没有DOM节点的纤维,我们需要修改两个地方。
首先,为了确定函数组件的父DOM节点,我们需要在fiber数中向上找,直到找到一个有DOM节点的纤维。
//if(!fiber){
// return
//}
let domParentFiber = fiber.parent
while(!domParentFiber.dom){
domParentFiber = domParentFiber.parent
}
const domParent = domParentFiber.dom
//if(
// fiber.effectTag === "PLACEMENT" &&
// fiber.dom != null
//){
domParent.appendChild(fiber.dom)
//}else if(
// fiber.effectTag === "UPDATE" &&
// fiber.dom != null
){
其次,当需要移除一个节点是,我们需要向下找直到找到一个具有DOM节点的子元素。
// )
// }else if(fiber.effectTag === "DELETION"){
commitDeletion(fiber, domParent)
// }
// commitWork(fiber.child)
// commitWork(fiber.sibling)
//}
function commitDeletion(fiber, domParent){
if(fiber.dom){
domParent.removeChild(fiber.dom)
}else{
commitDeletion(fiber.child, domParent)
}
}
//function render(element, container){
// wipRoot = {
总结
步骤六中,作者对之前只向DOM中添加节点做了补充,将新老节点的dom属性、props属性进行对比,然后对其更新和删除。
步骤七则介绍了函数组件的更新和删除的方法,函数组件没有对应的DOM属性,且子节点只能通过函数返回,因此我们需要对performUnitOfWork函数以及对应的commitWork函数做些修改。
上一篇传送门:Build your own React_6 调解器
下一篇传送门:Build your own React_8 Hooks
以上是关于Build your own React_7 函数组件的主要内容,如果未能解决你的问题,请参考以下文章
Build your own React_2 render函数
Build your own React_2 render函数
Build your own React_1 createElement函数
Build your own React_1 createElement函数