webvm

0.0.4 • Public • Published

WebVM

npm npm

WebVM is a common simplified API for DOM/Browser interaction that's usable from many different WASM or standard web based projects. Providing both react-like diffing objects for DOM efficient updates, with polyfills and wrappers for existing Web objects for better usage, interaction and experience.

WebVM is a project geared towards that interaction layer between different for projects in WebAssembly, Zig, Golang or Rust.

It targets to provide the following features:

  • Efficient DOM diffing methods.
  • RequestAnimationFrame Loop management.
  • Dead Simple Event using live events.
  • DOMMount objects (Manages DOM Node update and patching)
  • Promise Polyfills
  • Fetch Polyfills

Building

To build WebVM typescript sources into javascript using the commonjs module system, simply execute:

npm run ts-build

WebVM provides two commands with npm script that will use browserify to create a single bundle file suitable for use in a browser, using any of the two commands will allow you dropped said file into the web-browser with a <script> tag.

"dist-unminified""npx browserify --debug src/webvm.js -o dist/webvm.js",
"dist-minified""npx browserify -p tinyify ./src/webvm.js -o ./dist/webvm.min.js",

Simply run the desired command to build and you will find built script in the ./dist directory.

npm run dist-unminified

Or

npm run dist-minified

MountNode

MountNode is the core of the WebVM DOM management features, it sits on top of a static DOM node already present within the loaded html page as specified by you and uses that node as it's mount point for rendering all new elements, patching existing content and updating them.

With MountNode, DOM nodes id attribute become very important, DOM node algorithm tries to take advantage of the fact that all nodes should have a unique attribute and consistently uses that during it's patching/updating process of the DOM, if no id attribute is found, then a positional fallback algorithm is used when nodes type (e.g div, h) are the same in same position in new virtual DOM tree.

MountNode currently uses two well defined means of DOM updates:

HTML nodes or strings

The HTML nodes or strings which will be converted into DOM nodes is the general virtual DOM approach, a popular cases where incoming updates/changes comes as a completely rendered html which will need to be updated with the existing displayed DOM. This requires that all nodes to be updated and those which we do not wish to remove should be within the new incoming html.

This is required because internally, DOMMount algorithm after patching the DOM will remove all un-recognize DOM elements within the static node that where neither seen in new update, hence a complete html is necessary to ensure unchanged nodes do not get removed from the page.

JSON DOM Fragments

The JSON DOM fragment are to provide a virtual DOM representation which can be used in a similar manner to the html nodes rendering and also as individual fragment updates where through internal used references, parts of the DOM can be updated by just listening out specific parts which changed. The JSON fragments all can represent a complete html tree and a part of the tree, which gives them their power and usefulness, with the added advantage we need not waste effort rendering html again as they will be converted directly into DOM nodes for insertion and updates.

When a JSON DOM fragment represent a full DOM structure, then it's treated like it's HTML nodes/string counterpart where a we treat them as describe the total change to occur within the static nodes children, where nodes described in the JSON node fragments are updated if changed and others unchanged are left behind, whilst others not seen are removed and cleaned out of the DOM.

But wen a JSON DOM fragment represent individual changes where their order is not respected, then we can actively update different areas of the DOM easily, removing un-needed parts whilst updating only parts that have changed. It provides a somewhat streaming change list to be applied to the DOM, where we only consider direct points of change instead of the whole rendered state of the last update. This becomes useful when you only wish to batch certain updates from the server to the client or stream different, non-linear changes in light, small batches when content size matters.

DOMMount provides two methods for it's two approaches:

Patch

Patch applies both HTML nodes/strings and JSON dom fragments as complete description of the latest state of the virtual dom compared to it's last update. Therefore the full force of the general algorithm as described above is applied, where nodes described in the update are changed and updated as needed and others not in new rendering are total removed from the DOM tree within the static node.

Stream

Stream applies only JSON strings containing a list of JSON nodes or a array of JSON nodes that describe non-linear changes to be applied to different parts of the DOM tree within the static node.

DOMMount also handles events differently compared to other fragments, it uses a live events (made popular by JQuery) where the static node becomes the receiver of all event registration, where we expect all events that occur within it's children to bubble or propagate up the tree, where we catch, then retrieve the DOM node itself which received the event then notify the provided callback about the event and the node targeted.

To make this work, DOMMount utilizes an events attributes on all nodes that describe to the event manager that a node is interested in such events that occur on it, so that if an event occurs on a node and the node has no interest then the event is ignored.

The events are described in the following format: {eventName}-{preventDefaultFlag}{stopPropagationFlag}.

The {eventName} tells which event is targeted, such as a 'click', 'mouseup', etc.

The {preventDefaultFlag} is a flag containing either 1 or 0, to indicate if we wish to prevent the default behaviour for such an event in the case of <a>, <form> tags which have default behaviours in respect to their contents and attributes.

The {stopPropagation} is used to indicate the prevent of an event propagating outside it's static node tree. As live events depends solely on event bubbling, the propagation stopping only happens when the static node receives the event and not at the target where it occurs as we need to respond and notifier callback on the occurrence of said event.

Below is a demonstration of the use of the DOMMount for a DOM tree with a div#app static node.

<html>
    <head></head>
    <body>
        <div id='app'></div>
    </body>
</html>
const mounted = new WebVM.dom.DOMMount(document, '#app', (event, element) => {
    // MountNode uses live events, which means event registered are registered to the mount node
    // DOM node, which lets us watch for bubbling events where we catch and get the actual element
    // which received the event to allow you respond. This makes it easy to change DOM nodes without
    // loosing registered events.
    // Only DOM nodes which signify their interest in a event with the events attributes will
    // be considered to be sent her for alerting.
});
 
const newContent = `
    <div id="rack-table">
        <div id="rack-item-1">
                <span>Wrecker</span>
        </div>
        <div id="rack-item-2">
                <span>Wrecker</span>
        </div>
        <div id="rack-item-3">
                <span>Wrecker</span>
        </div>
        <div id="rack-item-4">
                <span>Wrecker</span>
        </div>
    </div>
`;
 
mounted.patch(newContent);

JSON change updates/streaming are quite similar, see example:

const mounted = new WebVM.dom.DOMMount(document, '#app', (event, element) => {
    // MountNode uses live events, which means event registered are registered to the mount node
    // DOM node, which lets us watch for bubbling events where we catch and get the actual element
    // which received the event to allow you respond. This makes it easy to change DOM nodes without
    // loosing registered events.
    // Only DOM nodes which signify their interest in a event with the events attributes will
    // be considered to be sent her for alerting.
});
 
const newContent = `
    <div id="rack-table">
        <div id="rack-item-1">
                <span>Wrecker</span>
        </div>
        <div id="rack-item-2">
                <span>Wrecker</span>
        </div>
        <div id="rack-item-3">
                <span>Wrecker</span>
        </div>
        <div id="rack-item-4">
                <span>Wrecker</span>
        </div>
    </div>
`;
 
mounted.patch(newContent);
 
const renderedContent = `
        <div id="rack-item-2" events="rackup-00" _ref="/rack-table/rack-item2">
                <span>Wrecker Walk away</span>
    </div>
`;
 
// WebVM.ToJSONNode provides necessary method to transform a DOM node description into a 
// JSON fragment object.
const patchContent = patch.ToJSONNode(renderedContent, false, null);
mounted.streamList(patchContent);

JSON Fragments are supposed to match giving format:

JSONNode {
  id: string; // id defines the dom id attribute for giving node.
  tid: string; // tid gives the unique id dom node.
  atid: string; // atid tells the ancestor-id which was the reconciliation process. It tells us what the id of old node.
  ref: string; // ref defines a reference value provided to match nodes from rendering and rendered.
  type: number; // type tells us which type number representing of node e.g 3, 8, 11.
  typeName: string; // typeName tells us which type of node e.g document-fragment, text-node
  name: string; // name tells the name of giving node e.g div, span.
  content: string; // content represents text content if this is a content.
  namespace: string; // namespace tells the DOM namespace for giving node.
  removed: boolean; // removed tells us if this represents a removal JSONNode instruction.
  attrs: Array<JSONAttr>; // attrs defines the array of node attributes.
  events: Array<JSONEvent>; // events defines the event map for giving attribute.
  children: Array<JSONNode>; // children contains child node descriptions for giving root.
}

Example

See demo of DOM patching taking place in Index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebVM Sample Load Page</title>
    <style>
        body{
            padding: 10px;
            width: 960px;
            margin: 0 auto;
        }
 
        h1#counter{
            text-align: center;
            font-size: 15em;
        }
    </style> 
</head>
<body>
    <div id="app"></div>
    <script src="webvm.js" type="text/javascript"></script> 
    <script type="text/javascript">
        var counter = 0;
        var mount = new WebVM.mount.DOMMount(window.document, "#app");
 
        setInterval(function patchDOM() {
          counter++;
          mount.patch(`
            <h1 id="counter">${counter}</h1>
          `);
        }, 1000);
    </script> 
</body>
</html>

Patch Demo

Structure

All typescript work is being done in the lib directory, which then have their equivalent javascript versions compiled into the src directory. This allows anyone to pickup these files individually as they see fit.

With the build scripts, a single version can be generated into the dist directly for the browser.

.
├── dist
│   ├── index.html
│   ├── webvm.js
│   └── webvm.min.js
├── inits
│   ├── setup.js
│   └── setup.js
├── jest.config.js
├── lib
│   ├── anime.ts
│   ├── doc.ts
│   ├── dom.ts
│   ├── extensions.ts
│   ├── fetch.ts
│   ├── http.ts
│   ├── interpolations.ts
│   ├── mount.ts
│   ├── patch.ts
│   ├── promise.ts
│   ├── raf-polyfill.ts
│   ├── tween.ts
│   ├── utils.ts
│   ├── websocket.ts
│   └── webvm.ts
├── package.json
├── package-lock.json
├── README.md
├── src
│   ├── anime.js
│   ├── anime.js.map
│   ├── doc.js
│   ├── doc.js.map
|   |-- ....< more js files >
├── tests
│   └── webvm.test.ts
├── tsconfig.json
└── tslint.json
 
5 directories, 66 files

Readme

Keywords

none

Package Sidebar

Install

npm i webvm

Weekly Downloads

0

Version

0.0.4

License

MIT

Unpacked Size

651 kB

Total Files

66

Last publish

Collaborators

  • influx6