An alternative to HTM-JSX? Or the other option?
Preact | Omi |
---|---|
The original link
HTM is Hyperscript Tagged Markup, which is a similar syntax to JSX. Compared to JSX, its biggest advantages are:
- No compiler is required
- Run directly in a modern browser, as long as your browser supports Tagged templates
So, you can use it directly in React, Preact, or OMI and run it directly in the browser without any compilation. It makes use of Tagged templates and the HTML Parser that comes with the browser.
Minuscule size
- Using it directly in the browser is only 700 bytes, less than 1KB
- Only 500 bytes are used in Preact
- If you use babel-plugin-htm you only need 0 bytes
Syntax – like JSX and like lit-HTML
HTM is inspired by lit-HTML, but includes these features found in JSX:
- Residual extensions:
<div ... ${props}>
- Label self-closing:
<div />
- Dynamic tag name:
<${tagName}>
(tagName
Is a reference to an element) - Boolean properties:
<div draggable />
Improvements to JSX
HTM does make a number of improvements over JSX, such as the following features that JSX does not have:
- You don’t need a compiler. It runs directly in the browser
- HTML optional semicolons:
<div class=foo>
- HTML self-closing:
<img src=${url}>
- Optional close TAB:
<section><h1>this is the whole template!
- Component closing label:
<${Footer}>footer content<//>
- Support for HTML comments:
<div><! -- don't delete this! --></div>
- Install lit- HTML VSCode extension syntax highlighting
Project status
The original goal of HTM was to create a wrapper around Preact that would be used in the browser without interference. I want to use the virtual DOM, but I don’t want to use the build tool and just use the ES module.
This means abandoning JSX, and the closest alternative is [Tagged_templates]. So, I wrote this library to fix the difference. It turns out that this technique is framework-independent, so it should work with most virtual DOM libraries.
The installation
HTM is published to NPM, you can also access the CDN of unpkg.com:
npm i htm
Copy the code
Obtained from unpkg:
import htm from 'https://unpkg.com/htm?module'
const html = htm.bind(React.createElement);
Copy the code
// 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'
Copy the code
Use guide
Since HTM is a generic library, we need to tell it how to “compile” our templates.
The goal should be of the form H (tag, props… Children), and can return anything.
// This is our function h. Now, it just returns a description object.
function h(tag, props, ... children) {
return { tag, props, children };
}
Copy the code
To use that h function, we need to create our own HTML tag function by binding HTM to our H function:
import htm from 'htm';
const html = htm.bind(h);
Copy the code
We now have an HTML template tag that can be used to generate objects in the format created above, such as:
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!']
// }
Copy the code
For example
Curious to see what it’s all like? This is a working application!
It is a single HTML file with no build or tools. You can edit it with the Nano.
<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=The ${() = >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>
Copy the code
This is aPreact online version.
That would be great, wouldn’t it? Note that there is only one import – here we only use import to integrate with Preact because it is easier to import and smaller.
The same example works fine without a pre-built version, using only two imports:
import { h, Component, render } from 'preact';
import htm from 'htm';
const html = htm.bind(h);
render(html` <${App} page="All" />`.document.body);
Copy the code
Other usage
Because HTM is designed to meet the same requirements of JSX, you can use it anywhere you use JSX.
** Use VHTML to generate HTML:**
import htm from 'htm';
import vhtml from 'vhtml';
const html = htm.bind(vhtml);
console.log( html`Hello world!
` );
// 'Hello world!
'
Copy the code
Webpack configuration via jsxobj: (details here)
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'
/ /}
// }
Copy the code
omi-html
Use HTM in OMI
→ Online examples
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 = (a)= > {
this.data.count--
}
add = (a)= > {
this.data.count++
}
render() {
return html`
<div>
<button onClick=The ${this.sub}>-</button>
<span>The ${this.data.count}</span>
<button onClick=The ${this.add}>+</button>
</div>`
}
})
render(html`<my-counter />`.'body')
Copy the code
Runs directly in the browser
<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=The ${this.sub}>-</button>
<span>The ${this.data.count}</span>
<button onClick=The ${this.add}>+</button>
</div>
`}
})
render(html`<my-counter />`.'body')
</script>
Copy the code
Star & Fork
- omi-html