1 | import { EMPTY_OBJ } from './constants';
|
2 | import { commitRoot, diff } from './diff/index';
|
3 | import { createElement, Fragment } from './create-element';
|
4 | import options from './options';
|
5 | import { 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 | */
|
15 | export 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 | */
|
73 | export function hydrate(vnode, parentDom) {
|
74 | render(vnode, parentDom, hydrate);
|
75 | }
|