## HTM - JSX 的替代品?还是另一种选择? | **[Preact](https://github.com/developit/preact)** | **[Omi](https://github.com/Tencent/omi)** | | ------------------------------- | ----------------------------------- | | hyperscript tagged markup demo| hyperscript tagged markup demo | [`htm`](https://github.com/developit/htm) 全称是 Hyperscript Tagged Markup,是一款与 JSX 语法类似的东西,相比 JSX 它最大的优点是: * 不需要编译器 * 直接在现代浏览器中运行,只要你的浏览器支持 [Tagged templates] 就行 所以,你可以直接在 react、preact 或者 omi 中使用并且直接在浏览器中运行,不需要任何编译。它利用了 [Tagged templates] 和浏览器自带的 HTML parser。 ## 极小的尺寸 * 直接在浏览器中使用只有 700 字节,1KB 都不到 * 在 preact 中使用只有 500 字节 * 如果使用 [babel-plugin-htm](https://www.npmjs.com/package/babel-plugin-htm) 只需要 0 字节 ## 语法 - 像 JSX 也像 lit-html htm 是受 lit-html 启发,但是包含了 JSX 里的这些特性: - 剩余扩展: `
` - 标签自关闭: `
` - 动态标签名: `<${tagName}>` _( `tagName` 是元素的引用)_ - 布尔属性: `
` ## 对 JSX 的改进 `htm` 确确实实地基于 JSX 之上做了大量改进,比如下面这些特性是 JSX 所没有的: - **不需要编译器,直接在浏览器中运行** - HTML 的可选分号的方式: `
` - HTML 的自关闭: `` - 可选的关闭标签: `

this is the whole template!` - 组件关闭标签: `<${Footer}>footer content` - 支持 HTML 注释: `
` - 安装 [lit-html VSCode extension] 语法高亮 ## 项目状态 HTM最初的目标是在Preact周围创建一个包装器,使用它在浏览器中不受干扰。我想使用虚拟DOM,但我不想用构建工具,直接使用ES模块。 这意味着要放弃JSX,最接近的替代方案是 [Tagged templates]。所以,我写了这个库来修补两者之间的差异。事实证明,该技术是框架无关的,因此它应该与大多数虚拟 DOM 库一起工作。 ## 安装 `htm` 发布到了 npm, 也可以访问 unpkg.com 的 CDN: ```js npm i htm ``` **从unpkg获取:** ```js import htm from 'https://unpkg.com/htm?module' const html = htm.bind(React.createElement); ``` ```js // 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])_, 的函数,并且可以返回任何东西。 ```js // 这是我们的 h 函数。现在,它只返回一个描述对象。 function h(tag, props, ...children) { return { tag, props, children }; } ``` 为了使用那个 `h` 函数,我们需要通过绑定`htm`到我们的`h`函数来创建我们自己的 HTML 标签函数: ```js import htm from 'htm'; const html = htm.bind(h); ``` 现在我们有一个`html`模板标签,可以用来生成上面创建的格式的对象,比如: ```js import htm from 'htm'; function h(tag, props, ...children) { return { tag, props, children }; } const html = htm.bind(h); console.log( html`

Hello world!

` ); // { // tag: 'h1', // props: { id: 'hello' }, // children: ['Hello world!'] // } ``` ## 举个例子 好奇地想看看这一切是什么样子的?这是一个工作应用程序! 它是单个HTML文件,没有构建或工具。你可以用Nano编辑它。 ```html htm Demo ``` **这是一个[Preact 线上版本](https://htm-demo-preact.glitch.me/).** 那真是太好了?注意,只有一个导入-这里我们只使用了 `import` 与 Preact 集成,因为它更容易导入和更小。 同样的示例在没有预构建版本的情况下运行良好,只需使用两个导入: ```js 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:** ```js import htm from 'htm'; import vhtml from 'vhtml'; const html = htm.bind(vhtml); console.log( html`

Hello world!

` ); // '

Hello world!

' ``` **Webpack configuration via [jsxobj]:** ([details here](https://webpack.js.org/configuration/configuration-languages/#babel-and-jsx)) ```js import htm from 'htm'; import jsxobj from 'jsxobj'; const html = htm.bind(jsxobj); console.log(html` `); // { // watch: true, // mode: 'production', // entry: { // path: 'src/index.js' // } // } ``` # omi-html 在 omi 中使用 [htm](https://github.com/developit/htm) [→ 在线例子](https://tencent.github.io/omi/assets/omi-html.html) ## Usage of omi-html ```js 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`
${this.data.count}
` } }) render(html``, 'body') ``` ## 直接运行在浏览器 ```html ``` [Tagged Templates]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates [lit-html]: https://github.com/Polymer/lit-html [babel-plugin-htm]: https://www.npmjs.com/package/babel-plugin-htm [lit-html VSCode extension]: https://marketplace.visualstudio.com/items?itemName=bierner.lit-html [vhtml]: https://github.com/developit/vhtml [jsxobj]: https://github.com/developit/jsxobj [hyperscript]: https://github.com/hyperhype/hyperscript