JSX是什么鬼(WTF-is-JSX)
JSX实际上很简单:只需要花1分钟阅读这个,你会明白有关这个有趣的替代模板的一切。
替换标题:“融入JSX”
Pragma
您可以声明每个文件或每个函数来告诉您的transpiler
(例如:Babel
)每个节点在运行时应调用的函数的名称(请参阅“Transpilation”)。
在下面的例子中,我们说的是“ h()
为每个节点注入一个函数”:
/** @jsx h */
Transpilation
如果你还没有使用解析器,你应该是尝试一下。使用ES6 / ES2015 编写,调试,测试和运行JavaScript时更为高效。Babel
是最受欢迎和推荐的透析器,所以我会假设你正在使用它。
除了将ES6 / ES7 +语法转换为 ES5 以外,Babel还包括对JSX 开箱即用的转换支持。您不需要添加或更改任何内容以使用此功能。
通过查看一个非常简单的例子来了解它的工作原理是最简单的:
转换前 :( 你写的代码)
/** @jsx h */
let foo = <div id="foo">Hello!</div>;
转换后 :(你运行的代码)
var foo = h('div', {id:"foo"}, 'Hello!');
你可能看第二个代码片段的时候,认为使用函数构建UI不会那么糟糕
这就是为什么我坐上了 JSX
的车:如果它从地球上消失了,用手写的输出依然会很舒服。
JSX只是一种已经相当不错的语法的糖。
人们甚至把它用于整个项目:hyperscript
我们来构建一个JSX渲染器
首先,我们需要定义转换代码后调用的 h()
函数。
你调用任何你想要的,我使用h()
,因为这种类型的“构建器”功能的原始想法被称为hyperscript (“hypertext“ + “javascript”).。
function h(nodeName, attributes, ...args) {
let children = args.length ? [].concat(...args) : null;
return { nodeName, attributes, children };
}
好的,这看起来很简单(ps: 只处理了 children)
不熟悉ES6 / ES2015?
- 这…在参数列表是一个rest param。它将“其余”的参数收集到数组中。
- 该concat(…args)位是一个扩展运算符:它需要该数组并将其扩展为参数concat()。这里使用的concat()是将子节点推入数组。
现在我们有这些嵌套的JSON对象我们的h()
函数吐出来,所以我们最终得到一个这样的“树”:
{
nodeName: "div",
attributes: {
"id": "foo"
},
children: ["Hello!"]
}
所以我们只需要一个接受该格式并抛出实际DOM节点的函数:(即沉染vdom)
function render(vnode) {
// 字符串只转换成 文本节点:
if (vnode.split) return document.createTextNode(vnode);
// 使用 VDOM 的 nodeName 创建一个DOM 标签
let n = document.createElement(vnode.nodeName);
// 拷贝所有属性到新节点上
let a = vnode.attributes || {};
Object.keys(a).forEach( k => n.setAttribute(k, a[k]) );
// 渲染并添加子节点
(vnode.children || []).forEach( c => n.appendChild(render(c)) );
return n;
}
:) 不难理解这是如何工作的。 如果有帮助,您可以将“虚拟DOM”视为一个非常简单的配置,用于构建给定的DOM结构。
虚拟DOM的好处是它非常轻巧。小对象引用其他小对象,一个由易于优化的应用程序逻辑构成的结构。
这也意味着它不受任何渲染逻辑或缓慢的DOM方法的束缚。
使用JSX
我们知道JSX
被转换成h()
函数调用。
那些函数调用创建一个简单的“虚拟”DOM树。
我们可以使用该render()
函数来制作一个匹配的“真实”DOM树。
这就是这样的:
// JSX -> VDOM:
let vdom = <div id="foo">Hello!</div>;
// VDOM -> DOM:
let dom = render(vdom);
// add the tree to :
document.body.appendChild(dom);
Partials,迭代与逻辑:没有新的语法
我们拥有所有的JavaScript语法,而不是模板语言引入的有限概念。(PS:这是JSX胜过模板语言的地方)
“Partials”是由无逻辑/有限逻辑模板引擎引入的概念,用于在不同的上下文中重复使用视图块。
迭代似乎是每个新的模板语言都重新发明的东西(我和任何人一样有罪)。使用JSX,没有新的语法来学习:迭代您在JavaScript程序中的其他任何地方。您选择最适合给定任务迭代式的:[].forEach()
,[].map()
,for
和while
循环等
逻辑,像迭代,是模板语言喜欢重新发明的东西。一方面,无逻辑模板在将逻辑嵌入到视图方法非常弱:有限的结构,如\{\{ \#if value}}
将逻辑推入控制器层,鼓励膨胀。这规避了构建语言来描述更复杂的逻辑,避免可预见性和安全隐患。
在另一方面,使用代码生成(一种从粗略到不可原谅的技术)的引擎通常具有执行逻辑或甚至迭代任意JavaScript表达式的能力。这是一个很好的理由,不惜一切代价避免这种情况:您的代码被从原始位置(也许是一个模块,闭包或标记内)中剥离出来,并对“别的地方”评估。这对我来说是不可预测的或足够安全的。
JSX允许所有 JavaScript的语言功能,而不依赖于在构建步骤中生成奇怪的代码,没有eval()和扩展。
// 将数组中的字符串以列表的形式展示
let items = ['foo', 'bar', 'baz'];
// 创建列表li
function item(text) {
return <li>{text}</li>;
}
// 一个使用迭代和复用的视图
let list = render(
<ul>
{ items.map(item) }
</ul>
);
render()
返回一个DOM节点(在上述情况下是返回
- 列表),所以我们只需要把它放到DOM中:
document.body.appendChild(list);
把它放在一起
以下是小型虚拟DOM渲染器和使用它的视图的完整源码。 具有一些样式的CodePen可在下面获得。
同样,我也写了一个例子:在任何地方使用JSX渲染DOM
const ITEMS = 'hello there people'.split(' ');
// turn an Array into list items:
let list = items => items.map( p => <li> {p} </li> );
// view with a call out ("partial") to generate a list from an Array:
let vdom = (
<div id="foo">
<p>Look, a simple JSX DOM renderer!</p>
<ul>{ list(ITEMS) }</ul>
</div>
);
// render() converts our "virtual DOM" (see below) to a real DOM tree:
let dom = render(vdom);
// append the new nodes somewhere:
document.body.appendChild(dom);
// Remember that "virtual DOM"? It's just JSON - each "VNode" is an object with 3 properties.
let json = JSON.stringify(vdom, null, ' ');
// The whole process (JSX -> VDOM -> DOM) in one step:
document.body.appendChild(
render( <pre id="vdom">{ json }</pre> )
);