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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值