[譯] JSX 的替代方案

玉兒Qi發表於2019-03-07

如今,JSX 已經是一個非常受歡迎的框架模版了,它的應用也不僅僅侷限於 React(或其他 JSX 衍生模版)。但是,如果你並不喜歡它,或者有某些想要避免使用它的專案,或者只是好奇不使用 JSX 該如何書寫 React 程式碼的時候,該怎麼辦呢?最簡單的方法就是去閱讀官方文件,但是,官方文件很簡短,而在本篇文章中我們為您提供了更多的選擇。

免責宣告:個人來說,我很喜歡 JSX,在我所有的 React 專案中我都使用了它。但是,我對本文主題重新進行了調研,並且希望和你分享我的所見。

什麼是 JSX

首先,我們需要明白什麼是 JSX,這樣我們才能在純 JavaScript 中編寫對應的程式碼。JSX 是一種特定域程式語言,意味著我們需要將 JSX 程式碼轉碼,以便得到常規的 JavaScript,否則瀏覽器將無法解析程式碼。展望前景光明的未來,如果你想要使用 modules,並且所有需要的功能都能被目標瀏覽器支援,你仍然不能完全丟棄轉碼這一步,這可能是一個問題。

也許理解 JSX 將會被解析成什麼最好的方法就是使用 babel repl 實際操作一次。你需要點選左側皮膚的 presets 並且選擇 react,這樣解析器才能正確的解析程式碼。這之後,你就能在右側實時看到編譯生成的 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 元件或者內建標籤名字串(比如 divspan),第二個引數則是元件屬性,其他的引數則都被視作元件的子元素。

我強烈推薦你使用不同結構的元件樹反覆嘗試,來觀察 React 如何渲染值為 truefalse、陣列、或者元件等的屬性:即使你只嘗試使用 JSX 和一些其他內容的程式碼,它也很有幫助。

如果你想深入學習 JSX,可以參考官方文件

重新命名

由於編譯結果是固定的,我們其實也可以將所有的 React 程式碼直接以這種形式寫出,但是其實這種方式存在一些問題。

第一個問題就是非常繁瑣。真的相當繁瑣,而罪魁禍首就是 React.createElement。所以這個問題的解決方案就是將它簡寫為一個變數,按照 hyperscript 的方式命名為 h。這種方式能節省很多程式碼量,並且可讀性也更強。下面我們來重寫上面的程式碼,以便說明:

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.createElement 或者 h 你都已經嘗試過了,你就可以看出它們都存在一些缺點。首先,函式需要三個引數,所以在沒有屬性的情況下,你還是必須傳遞 null 作為引數,同時,className 作為一個很常用的屬性,在每次使用的時候都需要新建一個物件。

作為一個替代方案,你可以使用 react-hyperscript 庫。它不需要你提供空屬性,並且允許你用點號的方式定義 class 和 id(div#main.content -> <div id="main" class="content">)。這樣,你的程式碼能優化為:

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(是的,有時候總寫 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 有什麼不好。但是你可能會好奇如果不用它,你要怎麼寫程式碼,還有你的程式碼可能會是什麼樣子,本文的目的就只是回答了這個問題。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章