Preact | Omi |
---|---|
原文連接javascript
htm
全稱是 Hyperscript Tagged Markup,是一款與 JSX 語法相似的東西,相比 JSX 它最大的優勢是:html
因此,你能夠直接在 react、preact 或者 omi 中使用而且直接在瀏覽器中運行,不須要任何編譯。它利用了 Tagged templates 和瀏覽器自帶的 HTML parser。java
htm 是受 lit-html 啓發,可是包含了 JSX 裏的這些特性:react
<div ...${props}>
<div />
<${tagName}>
( tagName
是元素的引用)<div draggable />
htm
確確實實地基於 JSX 之上作了大量改進,好比下面這些特性是 JSX 所沒有的:webpack
<div class=foo>
<img src=${url}>
<section><h1>this is the whole template!
<${Footer}>footer content<//>
<div><!-- don't delete this! --></div>
HTM最初的目標是在Preact周圍建立一個包裝器,使用它在瀏覽器中不受干擾。我想使用虛擬DOM,但我不想用構建工具,直接使用ES模塊。git
這意味着要放棄JSX,最接近的替代方案是 [Tagged_templates]。因此,我寫了這個庫來修補二者之間的差別。事實證實,該技術是框架無關的,所以它應該與大多數虛擬 DOM 庫一塊兒工做。github
htm
發佈到了 npm, 也能夠訪問 unpkg.com 的 CDN:web
npm i htm
複製代碼
從unpkg獲取:npm
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 via jsxobj: (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 中使用 htm
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>
複製代碼