HTM - JSX 的替代品?還是另一種選擇?
ofollow,noindex">原文連結
htm
全稱是 Hyperscript Tagged Markup,是一款與 JSX 語法類似的東西,相比 JSX 它最大的優點是:
- 不需要編譯器
- 直接在現代瀏覽器中執行,只要你的瀏覽器支援Tagged templates 就行
所以,你可以直接在 react、preact 或者 omi 中使用並且直接在瀏覽器中執行,不需要任何編譯。它利用了Tagged templates 和瀏覽器自帶的 HTML parser。
極小的尺寸
- 直接在瀏覽器中使用只有 700 位元組,1KB 都不到
- 在 preact 中使用只有 500 位元組
- 如果使用babel-plugin-htm 只需要 0 位元組
語法 - 像 JSX 也像 lit-html
htm 是受 lit-html 啟發,但是包含了 JSX 裡的這些特性:
-
剩餘擴充套件:
<div ...${props}>
-
標籤自關閉:
<div />
-
動態標籤名:
<${tagName}>
(tagName
是元素的引用) -
布林屬性:
<div draggable />
對 JSX 的改進
htm
確確實實地基於 JSX 之上做了大量改進,比如下面這些特性是 JSX 所沒有的:
- 不需要編譯器,直接在瀏覽器中執行
-
HTML 的可選分號的方式:
<div class=foo>
-
HTML 的自關閉:
<img src=${url}>
-
可選的關閉標籤:
<section><h1>this is the whole template!
-
元件關閉標籤:
<${Footer}>footer content<//>
-
支援 HTML 註釋:
<div><!-- don't delete this! --></div>
- 安裝lit-html VSCode extension 語法高亮
專案狀態
HTM最初的目標是在Preact周圍建立一個包裝器,使用它在瀏覽器中不受干擾。我想使用虛擬DOM,但我不想用構建工具,直接使用ES模組。
這意味著要放棄JSX,最接近的替代方案是 [Tagged_templates]。所以,我寫了這個庫來修補兩者之間的差異。事實證明,該技術是框架無關的,因此它應該與大多數虛擬 DOM 庫一起工作。
安裝
htm
釋出到了 npm, 也可以訪問unpkg.com 的 CDN:
npm i htm 複製程式碼
從unpkg獲取:
import htm from 'https://unpkg.com/htm?module' const html = htm.bind(React.createElement); 複製程式碼
// just want htm + preact in a single file? there's a highly-optimized version of that: import { html, render } from 'https://unpkg.com/htm/preact/standalone.mjs' 複製程式碼
使用指南
既然htm
是一個通用的庫,我們需要告訴它怎麼“編譯”我們的模板。
目標應該是形式h(tag, props, ...children)
(hyperscript
)
, 的函式,並且可以返回任何東西。
// 這是我們的 h 函式。現在,它只返回一個描述物件。 function h(tag, props, ...children) { return { tag, props, children }; } 複製程式碼
為了使用那個h
函式,我們需要通過繫結htm
到我們的h
函式來建立我們自己的 HTML 標籤函式:
import htm from 'htm'; const html = htm.bind(h); 複製程式碼
現在我們有一個html
模板標籤,可以用來生成上面建立的格式的物件,比如:
import htm from 'htm'; function h(tag, props, ...children) { return { tag, props, children }; } const html = htm.bind(h); console.log( html`<h1 id=hello>Hello world!</h1>` ); // { //tag: 'h1', //props: { id: 'hello' }, //children: ['Hello world!'] // } 複製程式碼
舉個例子
好奇地想看看這一切是什麼樣子的?這是一個工作應用程式!
它是單個HTML檔案,沒有構建或工具。你可以用Nano編輯它。
<!DOCTYPE html> <html lang="en"> <title>htm Demo</title> <script type="module"> import { html, Component, render } from 'https://unpkg.com/htm/preact/standalone.mjs'; class App extends Component { addTodo() { const { todos = [] } = this.state; this.setState({ todos: todos.concat(`Item ${todos.length}`) }); } render({ page }, { todos = [] }) { return html` <div class="app"> <${Header} name="ToDo's (${page})" /> <ul> ${todos.map(todo => html` <li>${todo}</li> `)} </ul> <button onClick=${() => this.addTodo()}>Add Todo</button> <${Footer}>footer content here<//> </div> `; } } const Header = ({ name }) => html`<h1>${name} List</h1>` const Footer = props => html`<footer ...${props} />` render(html`<${App} page="All" />`, document.body); </script> </html> 複製程式碼
這是一個Preact 線上版本.
那真是太好了?注意,只有一個匯入-這裡我們只使用了import
與 Preact 整合,因為它更容易匯入和更小。
同樣的示例在沒有預構建版本的情況下執行良好,只需使用兩個匯入:
import { h, Component, render } from 'preact'; import htm from 'htm'; const html = htm.bind(h); render(html`<${App} page="All" />`, document.body); 複製程式碼
其他使用方式
因為htm
被設計成滿足JSX的相同需求,所以您可以使用JSX的任何地方使用它。
** 使用vhtml 生成 HTML:**
import htm from 'htm'; import vhtml from 'vhtml'; const html = htm.bind(vhtml); console.log( html`<h1 id=hello>Hello world!</h1>` ); // '<h1 id="hello">Hello world!</h1>' 複製程式碼
Webpack configuration viajsxobj :(details here )
import htm from 'htm'; import jsxobj from 'jsxobj'; const html = htm.bind(jsxobj); console.log(html` <webpack watch mode=production> <entry path="src/index.js" /> </webpack> `); // { //watch: true, //mode: 'production', //entry: { //path: 'src/index.js' //} // } 複製程式碼
omi-html
在 omi 中使用htm
Usage of omi-html
import { define, render, WeElement } from 'omi' import 'omi-html' define('my-counter', class extends WeElement { static observe = true data = { count: 1 } sub = () => { this.data.count-- } add = () => { this.data.count++ } render() { return html` <div> <button onClick=${this.sub}>-</button> <span>${this.data.count}</span> <button onClick=${this.add}>+</button> </div>` } }) render(html`<my-counter />`, 'body') 複製程式碼
直接執行在瀏覽器
<script src="https://unpkg.com/omi"></script> <script src="https://unpkg.com/omi-html"></script> <script> const { define, WeElement, render } = Omi define('my-counter', class extends WeElement { install() { this.constructor.observe = true this.data.count = 1 this.sub = this.sub.bind(this) this.add = this.add.bind(this) } sub() { this.data.count-- } add() { this.data.count++ } render() { return html` <div> <button onClick=${this.sub}>-</button> <span>${this.data.count}</span> <button onClick=${this.add}>+</button> </div> `} }) render(html`<my-counter />`, 'body') </script> 複製程式碼