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函数