Nho (nhα» | small in Vietnamese) is a tiny library designed for easy Web Component development.
-
Writing a Web Component using vanilla JavaScript can be such tedious. Alternatively, popular libraries can be overkill and overweighted (4KB+) for creating small components like a
"Buy now" buttonor acart listing drawer -
Nhosimplifies the process by staying lightweight, removing unnecessary APIs, and using a simple DOM diffing algorithm
1.2KBgzipped (1210 bytesforesmand1443 bytesforumd)- Simple API inspired from
Vue 100%test coverage
- In order to stay small,
Nhoskips few advanced features found in popular front-end frameworks likekey,Fragments,memo. The DOM diffing algorithm is somewhat basic, but it is fast enough for small projects. If your components become too complex, consider other options
First, run
npm install nho
The package is published on
npm, so other package managers (e.g.yarn,pnpm,bun) still work
then
import { Nho } from 'nho';
class MyCounterChild extends Nho {}First, add script to the html file
<script src="https://unpkg.com/nho"></script>then, add script to the html file
<script>
let Nho = nho.Nho;
class MyCounterChild extends Nho {}
</script>/* main.js */
/* declare global style. Styles will be injected to all Nho Elements */
Nho.style = `
.box {
background: blue;
color: yellow;
}
`
class MyCounterChild extends Nho {
render(h) {
/* bind value from props */
return h`<div>Child: ${this.props.count}</div>`
}
}
class MyCounter extends Nho {
setup() {
/* this method runs before mount */
/* create component state using "this.reactive", state must be an object */
this.state = this.reactive({ count: 1 });
/* only use ref for storing DOM reference */
this.pRef = this.ref();
/* effect */
this.effect(
// effect value: fn -> value
() => this.state.count,
// effect callback: fn(old value, new value)
(oldValue, newValue) => {
console.log(oldValue, newValue)
}
)
}
onMounted() {
/* this method runs after mount */
console.log('Mounted');
}
onUpdated() {
/* this method runs after each update. */
console.log('Updated');
/* P tag ref */
console.log('P Ref', this.pRef?.current);
}
onUnmounted() {
/* this method runs before unmount */
console.log('Before unmount');
}
addCount() {
/* update state by redeclaring its key-value. Avoid updating the whole state. */
this.state.count += 1;
}
render(h) {
/* this method is used to render */
/*
JSX template alike
- Must have only 1 root element
- Bind state / event using value in literal string
- Pass state to child element using props with 'p:' prefix
*/
return h`
<div class="box">
<p ref=${this.pRef}>Name: ${this.state.count}</p>
<button onclick=${this.addCount}>Add count</button>
<my-counter-child p:count=${this.state.count + 5}></my-counter-child>
</div>
`
}
}
customElements.define("my-counter", MyCounter);
customElements.define("my-counter-child", MyCounterChild);/* index.html */
<my-counter></my-counter>- Install dependencies:
bun install - Build the library bundles:
bun run build - Build/watch the example app:
bun run dev(outputs toexample/dist, openexample/index.html) - Serve the example folder:
bun run serve(default http://localhost:3000) - Run tests:
bun test
- Avoid using these below properties inside Nho Component since they are reversed Nho's properties
setup, onMounted, onUnmounted, onUpdated, effect, ref, reactive, render, style
any property that starts with `_`
- It's better to dive into the code, but here is a diagram about how
Nhoworks
flowchart LR
subgraph Main
direction LR
A[State change] --> B[Proxy set trap]
B --> C[Batched via requestAnimationFrame]
C --> D[Render template]
D --> E[Parse to DOM]
E --> F[Diff & patch current DOM]
F --> G[Bind props, events, refs]
G --> H[Run lifecycle + effects]
end
subgraph Cache[Cache + bind]
direction TB
N1[/Collect p: props and on* handlers while rendering/]
N2[/Attach cached handlers and refs/]
N1 --> N2
end
D -. cache .-> N1
N2 -. apply .-> G
R["Diff steps (order):<br>1. Trim extra children<br>2. Compare each new child by index<br>3. Clone if missing<br>4. Replace if tag/text differs<br>5. Sync attributes in place"]
F -. uses .-> R