review

首先让我们回顾一下一些基本概念。如果你已经对 React、JSX 和 DOM 元素的工作原理有了很好的了解,你可以跳过这一步。

我们将使用这个 React 应用程序,只有三行代码。第一行定义了一个React元素,第二行从 DOM 中获取一个节点,最后一行是将 React 元素渲染到容器中。

const element = <h1 title="foo">Hello</h1>
const container = document.getElementById("root")
ReactDOM.render(element, container)

上述代码是用 JSX 进行定义的,它甚至不是一段有效的 JavaScript 代码,所以接下来,首先要做的就是用普通的 JavaScript 代码去代替它。

1. 定义元素#

const element = <h1 title="foo">Hello</h1>

JSX 可以通过 Babel 等转译成 JS 代码。这种转换通常很简单:调用 React.createElement 替换标签内的代码,将标签名称、属性和子元素作为参数进行传递。

const element = React.createElement(
"h1",
{ title: "foo" },
"Hello"
)

React.createElement 的主要功能是根据参数创建一个对象。所以,在此我们可以用它的输出代替函数调用。

const element = {
type: "h1",
props: {
title: "foo",
children: "Hello",
},
}

这是一个拥有两个属性(type、props)的元素对象:

type 是一个字符串,指定我们要创建的 DOM 节点的类型,当你想创建一个HTML元素时,它是你传递给 document.createElementtagNametype 也可以是一个函数,但是这部分内容会放在第7步进行探讨。

props 是另一个对象,它有所有来自 JSX 属性的键和值。它还有一个特殊的属性:children。children 在这里是一个字符串,但它通常是一个有更多元素的数组。这就是为什么元素也是树。

2 渲染元素#

ReactDOM.render(element, container)

render 是 React 改变 DOM 的方式。那么,如何用普通 JavaScript 替换它呢?

2.1 创建 DOM 节点#

首先根据元素类型 element.type 创建 DOM 节点,并将相关属性进行传递。

const node = document.createElement(element.type)
node["title"] = element.props.title

2.2 创建子节点#

根据元素 children 属性创建元素子节点。此处,是一个字符串作为子节点,即创建一个文本节点即可。

const text = document.createTextNode("")
text["nodeValue"] = element.props.children

2.3 链接节点#

最后,将相应的子节点加入到父节点中,并将根节点加入到 container

node.appendChild(text)
container.appendChild(node)

3 完整代码对比#

const element = <h1 title="foo">Hello</h1>
const container = document.getElementById("root")
ReactDOM.render(element, container)

转换成:

const element = {
type: "h1",
props: {
title: "foo",
children: "Hello",
},
}
const container = document.getElementById("root")
const node = document.createElement(element.type)
node["title"] = element.props.title
const text = document.createTextNode("")
text["nodeValue"] = element.props.children
node.appendChild(text)
container.appendChild(node)