0.2.0 • Published 2 years ago
@stickts/stick v0.2.0
Stick
This is a POC implementation of a rendering library with following goals:
- Simple API with as few layers of indirection as possible. One should (relatively 😟) easily be able to grasp how the thing works
- Fine-grained DOM updates, without intermediate layers like VirtualDOM
- Decent perceptible performance and low memory footprint
- Embrace the platform. Particularly WebComponents and default devtools, which already exist in browsers
Example
import { element, Inlet, inlet, intoInlet } from '@stickts/stick'
import { O, from, fromEvent, map } from '@stickts/stick/o'
// element() creates a WebComponent, custom HTML tag, that can be used as a regular HTML element.
const Counter = element<{
init: number,
count$: Inlet<number>
}>('x-counter', (props) => {
// This function is called only once, when element is constructed.
// "inlets" are special kind of observables that can be used to consume values produced elsewere.
// These ones consume events and can be passed as event handlers to the template
const inc$ = inlet<MouseEvent>()
const dec$ = inlet<MouseEvent>()
const count = pipe(
merge(
from(0),
map(1)(inc$),
map(-1)(dec$)
),
scan((counter, change) => counter + change, props.init),
)
// This function connects `count` observable with inlet `props.count$`
// This way observing `props.count$` actually leads to observing `count`
intoInlet(props.count$, count)
// Render the DOM. JSX here actually renders DOM nodes and remembers places, that need dynamic updates.
return <>
<button onClick={inc$}>inc</button> <button onClick={dec$}>dec</button>
</>
})
// Root component of this example
// Insert it as <x-app></x-app> in DOM somewhere, and it will render itself
element('x-app', () => {
const count$ = inlet<number>()
return <main>
<h1>A somewhat lacking example</h1>
<p>
<Counter init={9000} count$={count$}/>
Current count is: {count$}
</p>
</main>
})
Caveats
match
andrepeat
directives, render into<s-container>
element. This helps to easily and cheaply swap rendered content and cache it. The container hasdisplay: contents
so it's not represented in the render tree. The problem is that this interferes with flex/grid layouts and CSS selectors.- By default
observable
can be observed only once. If you need multiple observers, usebroadcast
. This is done to improve performance of piped observables. Although this might change in the future.
TODO
- Refine API
- Cover the API with tests
- Proper styling support
- Shadow DOM
- RxJS and Mostjs interop
- docs
- SSR