preact
Version:
Fast 4kb React-compatible Virtual DOM library.
78 lines (67 loc) • 2.65 kB
JavaScript
import { EMPTY_OBJ, MODE_HYDRATE, NULL } from './constants';
import { commitRoot, diff } from './diff/index';
import { createVNode, Fragment } from './create-element';
import options from './options';
import { slice } from './util';
/**
* Render a Preact virtual node into a DOM element
* @param {import('./internal').ComponentChild} vnode The virtual node to render
* @param {import('./internal').PreactElement} parentDom The DOM element to render into
*/
export function render(vnode, parentDom) {
if (options._root) options._root(vnode, parentDom);
// https://github.com/preactjs/preact/issues/3794
// https://github.com/preactjs/preact/issues/5118
if (parentDom.nodeType == 9) {
parentDom = parentDom.documentElement;
}
// @ts-expect-error
let isHydrating = vnode && vnode._flags & MODE_HYDRATE;
// To be able to support calling `render()` multiple times on the same
// DOM node, we need to obtain a reference to the previous tree. We do
// this by assigning a new `_children` property to DOM nodes which points
// to the last rendered tree. By default this property is not present, which
// means that we are mounting a new tree for the first time.
let oldVNode = isHydrating ? NULL : parentDom._children;
parentDom._children = createVNode(Fragment, { children: [vnode] });
// List of effects that need to be called after diffing.
let commitQueue = [],
refQueue = [];
diff(
parentDom,
// Determine the new vnode tree and store it on the DOM element on
// our custom `_children` property.
parentDom._children,
oldVNode || EMPTY_OBJ,
EMPTY_OBJ,
parentDom.namespaceURI,
oldVNode
? NULL
: parentDom.firstChild
? slice.call(parentDom.childNodes)
: NULL,
commitQueue,
oldVNode ? oldVNode._dom : parentDom.firstChild,
// @ts-expect-error we are doing a bit-wise operation so it's either 0 or true
isHydrating,
refQueue
);
// Flush all queued effects
commitRoot(commitQueue, parentDom._children, refQueue);
// The live children are tracked on _children after diffing.
parentDom._children.props.children = NULL;
}
/**
* Update an existing DOM element with data from a Preact virtual node
* @param {import('./internal').ComponentChild} vnode The virtual node to render
* @param {import('./internal').PreactElement} parentDom The DOM element to update
*/
export function hydrate(vnode, parentDom) {
// Only objects can carry the flag. Primitives (null, strings, numbers,
// booleans) render into the existing DOM the same way hydrating would.
if (vnode && typeof vnode == 'object') {
// @ts-expect-error
vnode._flags |= MODE_HYDRATE;
}
render(vnode, parentDom);
}