banner
这篇文章是 制作你自己的React系列的其中一篇

#JSX

上次我们介绍了Didact Elements,它是用来描述我们想要渲染到DOM的元素的数据结构。在这篇文章中,我们将会用JSX来简化DIdact Elements的创建。

JSX是一种语法糖:
比起:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const element = {
type: "div",
props: {
id: "container",
children: [
{ type: "input", props: { value: "foo", type: "text" } },
{
type: "a",
props: {
href: "/bar",
children: [{ type: "TEXT ELEMENT", props: { nodeValue: "bar" } }]
}
},
{
type: "span",
props: {
onClick: e => alert("Hi"),
children: [{ type: "TEXT ELEMENT", props: { nodeValue: "click me" } }]
}
}
]
}
};

element-without-jsx.js hosted with ❤ by GitHub

我们可以这样写:

1
2
3
4
5
6
7
const element = (
<div id="container">
<input value="foo" type="text" />
<a href="/bar">bar</a>
<span onClick={e => alert("Hi")}>click me</span>
</div>
);

element-with-jsx.jsx hosted with ❤ by GitHub

如果你不熟悉JSX你可能会觉得上面的代码是无效的。对,他确实是无效的,无法在浏览器中运行,为了让浏览器能解析,代码需要预编译(比如babel),转换为有效的JS(了解更多JSX请阅读 Jason Miller这篇文章。举个例子,babel把上面的JSX编译成下面的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

const element = createElement(
"div",
{ id: "container" },
createElement("input", { value: "foo", type: "text" }),
createElement(
"a",
{ href: "/bar" },
"bar"
),
createElement(
"span",
{ onClick: e => alert("Hi") },
"click me"
)
);

transpiled.js hosted with ❤ by GitHub

为了只支持JSX,我们需要添加到Didact中的是createElement函数,剩下的工作由预处理器完成。函数的第一个参数是元素的type,第二个是参数是有props属性的对象,剩下的其他参数都是childrencreateElement需要创建一个props对象,把所有的config赋值给它。把第二个以后的参数都设置为它的children,返回一个包含typeprops的对象。把下面的函数放入代码:

1
2
3
4
5
6
function createElement(type, config, ...args) {
const props = Object.assign({}, config);
const hasChildren = args.length > 0;
props.children = hasChildren ? [].concat(...args) : [];
return { type, props };
}

element.js hosted with ❤ by GitHub

除了无法处理文本元素之外这个函数没什么问题。文本元素作为字符串传递给createElement函数,DIdact需要文本元素有type和props这两个属性,就像其他元素一样。所以我们要将一个字符串封装为符合结构的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const TEXT_ELEMENT = "TEXT ELEMENT";

function createElement(type, config, ...args) {
const props = Object.assign({}, config);
const hasChildren = args.length > 0;
const rawChildren = hasChildren ? [].concat(...args) : [];
props.children = rawChildren
.filter(c => c != null && c !== false)
.map(c => c instanceof Object ? c : createTextElement(c));
return { type, props };
}

function createTextElement(value) {
return createElement(TEXT_ELEMENT, { nodeValue: value });
}

element.js hosted with ❤ by GitHub

我还筛选了除了nullundefinedfalse的参数,我们不需要渲染这些所以没必要把他们加到props.children里。

#总结
在这篇文章中我们没有给Didact增加任何实际的功能,但是我们改进了开发者体验,因为可以用JSX来定义元素而不是手写了,我已经更新了codepen上的代码
请注意 codepen自动使用了babel编译JSX,文件开头的/** @jsx createElement */告诉babel要这样做。
你也可以在github上查看我的commit.

在下一章我将介绍Didact的虚拟DOM和diff算法来支持DOM更新。