Build your own React_1 createElement函数

Posted 一只前端小马甲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Build your own React_1 createElement函数相关的知识,希望对你有一定的参考价值。

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

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

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

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

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

步骤一:createElement函数

让我们使用另一个React程序示例。这次我们将用我们自己写的React框架来代替React代码。

const element = (
	<div id="foo">
		<a>bar</a>
		<b />
	</div>
)
const container = document.getElementById("root")
ReactDOM.render(element, container)

首先我们要编写自己的createElement函数,将JSX转化为JS代码,就能看到createElement函数的调用。

const element = React.createElement(
	"div",
	 id: "foo" ,
	React.createElement("a", null, "bar"),
	React.createElement("b")
)
// const container = document.getElementById("root")
// ReactDOM.render(element, container)

在上一步骤中,我们知道React创造的元素实际上是拥有type和props属性的JS对象createElement函数的作用就是返回这个对象

我们对props属性使用扩展运算符,对children属性使用rest参数语法,这种方式得到的children属性是一个数组。

function createElement(type, props, ...children)
	return 
		type,
		props: 
			...props,
			children,
		
	

例如,createElement(“div”)返回:


	type: "div",
	props: 
		children: []
	

createElement(“div”, null, a)返回:


	type: "div",
	props: 
		children: [a]
	

createElement(“div”, null, a, b)返回:


	type: "div",
	props: 
		children: [a, b]
	

children数组中可能有基本数据类型,例如字符串和数字。所以我们将所有的非对象类型都包装一层,生成一个新类型(TEXT_ELEMENT)的对象。
React源码里不会为基本数据类型包装一层,当元素没有children属性时,也不会有空数组。我们这么做是为了简化代码,我们重写的框架更看重简洁的代码而不是高效的性能。

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_ELEMETN",
		props: 
			nodeValue: text,
			children: [],
		
	

现在我们还在使用React的createElement函数,为了替换掉它,我们需要给我们的框架取个名字,类似React的同时,具有教学的(didactic)意义。

const element = React.createElement(
	// "div",
	//  id: "foo" ,
	React.createElement("a", null, "bar")
	React.createElement("b")
)

不妨叫做Didact。

const Didact = 
	createElement,


const element = Didact.createElement(
	// "div",
	//  id: "foo" ,
	Didact.createElement("a", null, "bar"),
	Didact.createElement("b")
)

但是我们又同时想使用JSX语法,如何告诉Babel使用Didact的createElement替代React的createElement函数呢?
只需要添加一个注释,告诉Babel在转化某处JSX时使用我们自己定义的函数。

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

总结

步骤零中,作者解释了React元素的本质是JS对象,以及该JS对象是如何与实际的DOM节点关联起来的。

步骤一则展示了如何将React元素转化JS对象——通过createElement函数。React元素的标签名、props属性以及children属性被作为参数传入名为createElement的函数。前两个参数没有特别之处,children参数被转化为数组,数组元素值可能为基本数据类型或者对象,为了便于后面生成DOM节点的逻辑统一,作者将基本数据类型也“包装”成了对象。createElement函数最终返回一个JS对象,该对象保留了最初React元素中的信息。

上一篇传送门:Build your own React_0 总述
下一篇传送门:Build your own React_2 render函数

以上是关于Build your own React_1 createElement函数的主要内容,如果未能解决你的问题,请参考以下文章

Build your own React_4 理解React纤维

Build your own React_4 理解React纤维

Build your own React_0 总述

Build your own React_0 总述

Build your own React_8 Hooks

Build your own React_7 函数组件