jinzhanye

可替代的JSX

原文链接: blog.bloomca.me

不仅仅是在React(或受JSX启发模板),对于在各种框架中进行模板化,JSX是如今一个非常受欢迎的选择。然而,如果你不喜欢使用JSX,或者你的项目想避免使用它,又或者只是受好奇心驱使,你想尝试如果不用JSX该怎么写React代码。最简单的答案是去阅读官方文档,然而官方文档有点短,以下有几个更多的选择。

免责声明:就我个人而言,我喜欢JSX,在我所有React项目中都使用到它。然而,我稍微研究了这个话题也愿意分享我的发现

什么是JSX

首先,我们需要了解JSX是什么,以便在纯JavaScript中编写正确的代码。JSX是一门特定领域语言,这意味着我们需要使用JSX转变我们的代码以便获得能运行的Javscript代码,不然浏览器理解不了我们的代码。在光明的未来,如果你想在你的目标浏览器中使用 模块化 和其他支持的所需功能,你依然没有能力完全抛弃代码转换,这将是个问题

想要搞懂JSX最终被转换成什么,最好的办法也许是使用 babel repl。你需要点击 presets(应该在面板左侧)还有在那里选择 react,这样 babel 才能正确地完成转换。完成上面步骤后你会在右侧面板实时看到转换得到的JavaScript代码。例如,你可以试着输入下面代码

class A extends React.Component {
    render() {
        return (
            <div className={"class"} onclick={some}>
                {"something"}
                <div>something else</div>
            </div>
        )
    }
}

我得到的代码如下所示

class A extends React.Component {
  render() {
    return React.createElement("div", {
      className: "class",
      onclick: some
    }, "something", React.createElement("div", null, "something else"));
  }
}

你可以看到每对 <%tag%> 结构都被 React.createElement 这个函数代替。第一个参数是react 组件或者是内置的标签字符串(如 div、span),第二个参数跟属性相关,剩余的参数都被看作是孩子节点。

我强烈建议你去做不同的操作生成不同的树,去看看有什么不同。例如看看 React 是怎么渲染 truefalse、数组、组件还有其他等等。即使你只用JSX或只用 babel repl 做格式化把内容搞得漂亮点,这对于你来说也是很有帮助的。

官方文档提供有关JSX更深入的阅读

重命名

虽然我们用以上的方式生成的代码完全有效,并且我们可以用这种方式编写所有的React代码,但这种方法存在一些问题。

第一个问题是它非常冗长。真的很冗长,这是 React.createElement 造成的。所以第一个解决方案是将它先赋值到一个变量,通常叫做 h 表示 hyperscript。这将为你节省大量文本,代码也更具可读性。为了说明这一点,我们重写一下之前的例子

const h = React.createElement;
class A extends React.Component {
  render() {
    return h(
      "div",
      {
        className: "class",
        onclick: some
      },
      "something",
      h("div", null, "something else")
    );
  }
}

Hyperscript

如果你使用 React.createElementh 进行一些操作,你会发现它有几个缺陷。首先,这个函数需要三个参数。因此如果没有属性,你还必须要提供 null 占位,而 className 这个可能最常用的属性,每次都要先写入一个对象再写这个属性。

你可以 react-hyperscript 作为代替方案。它并需要提供空属性作为参数,也允许你使用类似emmet的风格(div#main.content -\> <div>)指定class和id。因此,我们的代码会有一点改善:

class A extends React.Component {
  render() {
    return h("div.class", { onclick: some }, [
      "something",
      h("div", "something else")
    ]);
  }
}

HTM

如果你不反对JSX,但不喜欢强制转译你的代码,这里有个叫 htm 的项目也许适合你。它的目标(看起来)是跟JSX做同样的事,但使用模板语法。这肯定会增加一些额外的开销(这些模版在运行时进行解析),但在你的情况也许是值得的。

它的工作方式是包裹元素函数,在我们这里这个函数就是 React.createElement,但也可以是其他相似的API,然后返回一个函数,这个函数将会解析我们的模版然后准备返回跟 babel 一样的结果,但这些事仅仅在运行时执行。

const html = htm.bind(React.createElement);
class A extends React.Component {
    render() {
        return html`
            <div className=${"class"} onclick=${some}>
                ${"something"}
                <div>something else</div>
            </div>
        `
    }
}

如你所见,它与真正的JSX几乎相同,我们只需要以稍微不同的方式插入变量;但是,这些主要是细节,如果你想在没有任何工具设置的情况下展示如何使用React,这也许很方便。

类Lisp语法

这个想法跟hyperscript相似,但是,这是个值得一看的优雅方法。这也很多类似的帮忙库,所以这是我的主观选择;这也许能给你的项目带来灵感。

ijk 将这种方法付诸实践,你编写模版的时候只需要用到数组,将位置作为参数。这样子的好处是你不用一味编写 h 函数(是的,即使这可能是重复的!)。下面是一个使用例子:

function render(structure) {
  return h('nodeName', 'attributes', 'children')(structure)
}
class A extends React.Component {
  render() {
    return render([
      'div', { className: 'class', onClick, some}, [
        'something',
        ['div', 'something else']
      ]]);
  }
}

总结

本文没有建议你不要使用JSX,也没有探讨JSX是否一个坏主意。你可能在想如果不用JSX你的代码能写成个什么样子,本文的目的只是回答这个问题。