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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值