翻译自这里:https://engineering.hexacta.com/didact-element-creation-and-jsx-d05171c55c56

Didact: Element creation and JSX

JSX

上一节我们介绍了Didact Elements,使用了一种很麻烦的方法来代表要渲染的DOM。这一节我们将介绍如何使用JSX来创建Didact Elements。

下面是一个Didact Elements的原生对象表示:

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" } }]
}
}
]
}
};

有了JSX这个语法糖之后我们就可以使用下面这个方法来创建和上面一样的元素:

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>
);

如果你对JSX不熟悉你可以能会想上面这段代码是个无效的JS对象—–没错,你想的是对的。为了让浏览器能解析JSX,我们需要使用预处理器(比如babel,想对JSX有更多了解的可以看这里)来将JSX转换一下。例如babel会将上面的JSX转成下面这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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"
)
);

剩下我们要做的就是添加一个createElement方法来让Didact支持JSX,其他的工作就可以交给预处理器了。createElement方法的第一个参数是元素的类型type,第二个参数是元素的props对象,剩下的其他参数就是children了。createElement方法会返回带有type属性和props属性的对象,props属性值也是一个对象,该对象含有第二个参数的所有属性,第二个参数之后的其他参数会放在一个数组中,并作为该对象的children属性值。来实现一下createElement方法:

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}
}

上面的createElement在不碰到文本元素时都能很好的工作。遇到文本元素时,文本内容会以字符串形式在第二个参数之后传递给createElement。又因为我们之前定义了文本元素也需要有typeprops属性,所以我们会将刚传进来的字符串转成一个文本元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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});
}

上面代码排除了子元素为nullundefinedfalse的情况。这些情况都是没必要渲染的,所以也不需要添加到props.children上。

Summary

这一节我们依然没有添加主功能到Didact上,但现在我们已经开始使用JSX来创建元素了,这大幅提升了开发体验。我已经将上节和本节的代码在codepen上更新了。代码头部的注释/** @jsx crerateElement8/告诉了babel去使用createElement来转义JSX。

你也可以在这里查看代码

下一节我们将往Didact中加入虚拟DOM和用来支持更新操作的一致性校验算法。

下一节