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

Build your own React_4 理解React纤维

Build your own React_4 理解React纤维