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