hydro-js
A lightweight (~3.9K compressed) reactive UI library via template literal tags.
Installation
$ npm install hydro-js
import { render, html } from 'hydro-js';
Alternatively you can use a CDN
<script type="module">
import { render, html } from "https://unpkg.com/hydro-js";
</script>
Examples
Concept
There are multiple things this library can do. The first thing is generating HTML from strings. This is mostly done by the Range Web API
. There are already ways to do that, like Element.insertAdjacentHTML()
, but this has some drawbacks, as it does not create Table Elements, like <colgroup>
, <tbody>
, <tfoot>
, <thead>
and <tr>
. Furthermore, the html function deals with inline events, objects, Handlebars / {{ Mustache }} etc. Using this function will feel like writing JSX without a build step.
The render function is used for mounting and unmounting Elements to/from the DOM and for executing lifecycle hooks. Optionally, it can diff both HTML Trees and reuse Elements (optionally too). This is not using a virtual DOM.
The functions calls for render
and DOM Updates are queued and worked on during a browser's idle periods.
In order to make the DOM reactive, ES6 Proxy
objects are being used to map data against an array of DOM Elements. Whenever the setter is being called, the Proxy will check the mapping and update the DOM granularly. No re-renders are needed!
Almost all intern maps are using WeakMap
with DOM Elements or Proxy objects as keys and thus memory is cleared efficiently.
Documentation
html
args: string
returns: DocumentFragment | Element | Text
Takes a string and transforms it to HTML. Used for internal bookkeeping too.
render
args:
- new Element (
DocumentFragment | Element | Text | reactive Proxy
) - old Element (
Element | string
) - ? shouldSchedule:
boolean
(default: true)
returns: function
that unmounts the new Element
Accepts the return value of html
and replaces it with old Element. If it is a string, it will be resolved with querySelector
.
setGlobalSchedule
args: boolean
Will enable/disable the schedule logic for render
and DOM Updates. Intern value defaults to true
.
setReuseElements
args: boolean
Will enable/disable the reuse of Elements in the diffing phase of render
. Intern value defaults to true
.
setInsertDiffing
args: boolean
If enabled, it will insert the new DOM Tree to the DOM before diffing. This will asssure that reused Elements will not lose their state (e.g. <video>
in Chrome. Intern value defaults to false
.
onRender
args:
function
- elem (
DocumentFragment | Element | Text
) - ...args for passed
function
Calls the passed in function
with ...args
, after the Element is being inserted by render
;
onCleanup
args:
function
- elem (
DocumentFragment | Element | Text
) - ...args for passed
function
Calls the passed in function
with ...args
, after the Element is being diffed out by render
or removed by unmount
;
reactive
args: value: any
returns: unique Proxy
Returns a Proxy object that can be used within html
. The Proxy is a wrapping a function that can set the value. If the setter is called with a function then the argument of the passed in function will be provided as the current value for the Proxy.
The actual value will be set on the hydro Proxy, but this Proxy will hide the complexity.
observe
args:
ReturnType<typeof reactive>
function
Calls the function whenenver the value of reactive changes. This is only one layer deep but chaining properties on reactive Proxys will return a Proxy too. Observing a prop of an object will look like: observe(person.name, ...)
getValue
args: ReturnType<typeof reactive>
returns: currently set value
Returns the value inside the the Proxy. getValue is needed because reactive Proxy does not have access to the value.
ternary
args:
- condition:
function | ReturnType<typeof reactive>
- trueVal:
any
- falseVal:
any
- ? proxy:
ReturnType<typeof reactive>
returns: ReturnType<typeof reactive>
In order to track a ternary in a template literal, this function has to be used. The proxy parameter is optional if the first parameter is a reactive Proxy. Otherwise, a function is being executed, whenever the Proxy value changes, which will update the DOM to either the trueVal or the falseVal.
unset
args: ReturnType
Deletes the Proxy object and removes Observer. This is important to keep memory low.
hydro
The actual Proxy in the library. This cannot be used with getValue
, observe
, ternary
or unset
but it offers the same functionality in a different manner.
properties:
- isProxy:
boolean
(default: true)
- asyncUpdate:
boolean
, (default: true, derived from globalSchedule)
- observe:
function
, args: string
as key
- getObservers:
function
, returns: map with all observer
- unobserve:
function
, args: string | undefined
, unobserve key or all
emit
args:
- event:
string
- data:
any
- who:
EventTarget
- options:
object
(default: { bubbles: true }
)
Emits an event from the EventTarget who. This event bubbles by default.
$
Shortcut for querySelector
.
$$
Shortcut for querySelectorAll
.
internals
An object with internal data / functions for testing or deeper dives for developers. This only includes a compare
function for DOM Elements at the moment.
Roadmap
• Add GitHub Actions for Publishing
• Snowpack Starter Project with Prerendering
• Add Broadcast channel or alike to sync updates over multiple tabs
• Experiment with Code in Worker
• Experiment if the above code can be replaced by wasm
• Refactor some code parts, once WeakRef is supported in Safari