Build your own React_2 render函数

前端工程师的要求越来越高,仅懂得“三大马车”和调用框架API,已经远不能满足岗位的能力要求。因此增强自身的底层能力,了解框架的内部原理非常重要。本系列文章,翻译自Rodrigo Pombo的《Build your own React》一文,同时每篇文章最后,都会加入自己的理解,一方面记录自己初探React框架原理的过程,另一方面也是想与各位大牛多多交流,以出真知。

我们打算从零开始重写一个React框架,在遵循源码架构的基础上,省略了一些优化和非核心功能代码。

假设你阅读过我之前的文章《build your own React》,那篇文章是基于React 16.8版本,当时还不能使用hooks来替代class。

你可以在Didact仓库找到那篇文章和对应的代码。这里还有个相同主题的视频,跟文章的内容有些区别的,但是可以参考观看。

从零开始重写React框架,我们需要遵循以下步骤:

步骤二:render 函数

接下来,我们来实现一个自己的ReactDOM.render函数。目前只考虑向DOM中添加节点的情况,以后再考虑更新和删除。

function render(element, container){
	// TODO create dom nodes
}

const Didact = {
	// createElement,
	render,
}

/** @jsx Didact.createElement */
// const element = (
//	<div id="foo">
//		<a>bar</a>
//		<b />
//	</div>
// )

// const container = document.getElementById("root")
Didact.render(element, container)

我们根据传入的标签名称生成一个DOM节点,之后将节点添加至容器中。

function render(element, container){
	const dom = document.createElement(element.type)

	container.appendChild(dom)
}

对于子元素递归采用相同的处理方式。

function render(element, container){
	// const dom = document.createElement(element.type)
	
	element.props.children.forEach(child=>
		render(child, dom)
	)

	// container.appendChild(dom)
}

对于文字类型元素的处理方式是:如果元素type属性为TEXT_ELEMENT,我们创建一个文字节点,而不是普通节点。

// function render(element, container){
	const dom = element.type ==="TEXT_ELEMENT"
		? document.createTextNode("")
		: document.createElement(element.type)

	// element.props.children.forEach(child=>
	//	render(child, dom)
	// )

	// container.appendChild(dom)
//}

最后一件事情就是将React元素的props属性赋给生成的dom节点。

// function render(element, container){
	// const dom = element.type === "TEXT_ELEMENT"
	//	? document.createTextNode("")
	//	: document.createElement(element.type)

	const isProperty = key => key !== "children"
	Object.keys(element.props)
		.filter(isProperty)
		.forEach(name=> {
		dom[name] = element.props[name]
	})

	// element.props.children.forEach(child=>
// }

这就是render函数的所有内容,我们现在拥有了将JSX渲染为DOM的库。

function createElement(type, props, ...children){
	return {
		type,
		props: {
			...props,
			children: children.map(child=>
				typeof child === 'object'
				? child
				: createTextElement(child)
			)
		},
	}
}

function createTextElement(text){
	return {
		type: "TEXT_ELEMENT",
		props: {
			nodeValue: text,
			children: []
		}
	}
}

function render(element, container){
	const dom = 
	element.type === "TEXT_ELEMENT"
	? document.createTextNode("")
	: document.createElement(element.type)

	const isProperty = key => key !== "children"
	Object.keys(element.props)
	.filter(isProperty)
	.forEach(name=>{
		dom[name] = element.props[name]
	})

	element.props.children.forEach(child=>
		render(child, dom)
	)

	container.appendChild(dom)
}

const Didact = {
	createElement,
	render
}

/** @jsx Didact.createElement */
const element = (
	<div id="foo">
		<a>Bar</a>
		<b />
	</div>
)
const container = document.getElementById("root")
Didact.render(element, container)

总结

步骤一中,作者详解介绍了通过createElement函数将React元素转化JS对象,该对象是React元素变为实际DOM节点的中间形态。

步骤二则展示了,如何将这个中间形态转化为DOM节点——通过render函数。步骤一得到的JS对象、根DOM节点被作为参数传出名为render的函数。render函数首先将JS递归转化为DOM树,每个节点可能是文字类型也可能是普通DOM节点,最后将生成的DOM树添加至根DOM节点上。至此,我们已经能清楚的知道一个React元素是如何被转化为实际的DOM节点。

上一篇传送门:Build your own React_1 createElement函数
下一篇传送门:Build your own React_3 并发模式

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值