UNPKG

2.49 kBJavaScriptView Raw
1import { EMPTY_OBJ } from './constants';
2import { commitRoot, diff } from './diff/index';
3import { createElement, Fragment } from './create-element';
4import options from './options';
5import { slice } from './util';
6
7/**
8 * Render a Preact virtual node into a DOM element
9 * @param {import('./internal').ComponentChild} vnode The virtual node to render
10 * @param {import('./internal').PreactElement} parentDom The DOM element to
11 * render into
12 * @param {import('./internal').PreactElement | object} [replaceNode] Optional: Attempt to re-use an
13 * existing DOM tree rooted at `replaceNode`
14 */
15export function render(vnode, parentDom, replaceNode) {
16 if (options._root) options._root(vnode, parentDom);
17
18 // We abuse the `replaceNode` parameter in `hydrate()` to signal if we are in
19 // hydration mode or not by passing the `hydrate` function instead of a DOM
20 // element..
21 let isHydrating = typeof replaceNode === 'function';
22
23 // To be able to support calling `render()` multiple times on the same
24 // DOM node, we need to obtain a reference to the previous tree. We do
25 // this by assigning a new `_children` property to DOM nodes which points
26 // to the last rendered tree. By default this property is not present, which
27 // means that we are mounting a new tree for the first time.
28 let oldVNode = isHydrating
29 ? null
30 : (replaceNode && replaceNode._children) || parentDom._children;
31
32 vnode = (
33 (!isHydrating && replaceNode) ||
34 parentDom
35 )._children = createElement(Fragment, null, [vnode]);
36
37 // List of effects that need to be called after diffing.
38 let commitQueue = [];
39 diff(
40 parentDom,
41 // Determine the new vnode tree and store it on the DOM element on
42 // our custom `_children` property.
43 vnode,
44 oldVNode || EMPTY_OBJ,
45 EMPTY_OBJ,
46 parentDom.ownerSVGElement !== undefined,
47 !isHydrating && replaceNode
48 ? [replaceNode]
49 : oldVNode
50 ? null
51 : parentDom.firstChild
52 ? slice.call(parentDom.childNodes)
53 : null,
54 commitQueue,
55 !isHydrating && replaceNode
56 ? replaceNode
57 : oldVNode
58 ? oldVNode._dom
59 : parentDom.firstChild,
60 isHydrating
61 );
62
63 // Flush all queued effects
64 commitRoot(commitQueue, vnode);
65}
66
67/**
68 * Update an existing DOM element with data from a Preact virtual node
69 * @param {import('./internal').ComponentChild} vnode The virtual node to render
70 * @param {import('./internal').PreactElement} parentDom The DOM element to
71 * update
72 */
73export function hydrate(vnode, parentDom) {
74 render(vnode, parentDom, hydrate);
75}