JSX是什么鬼(WTF-is-JSX)

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()forwhile循环等

逻辑,像迭代,是模板语言喜欢重新发明的东西。一方面,无逻辑模板在将逻辑嵌入到视图方法非常弱:有限的结构,如\{\{ \#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> )
);

Codepen演示

0%