Skip to content

Commit 53621d9

Browse files
authored
Various perf and size improvements (#3704)
* test switch for props * remove _parentDom from rendererState * align mount and patch component closer * revert switch * optimize commitQueue * remove rendererState * make root logic more concicse
1 parent aa8d57b commit 53621d9

File tree

7 files changed

+77
-117
lines changed

7 files changed

+77
-117
lines changed

src/component.js

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,10 @@ import options from './options';
33
import { createVNode, Fragment } from './create-element';
44
import { patch } from './diff/patch';
55
import { DIRTY_BIT, FORCE_UPDATE, MODE_UNMOUNTING } from './constants';
6-
import { getParentContext, getParentDom } from './tree';
6+
import { getParentDom } from './tree';
77

88
export let ENABLE_CLASSES = false;
99

10-
/**
11-
* The render queue
12-
* @type {import('./internal').RendererState}
13-
*/
14-
export const rendererState = {
15-
_parentDom: null,
16-
_context: {},
17-
_commitQueue: []
18-
};
19-
2010
/**
2111
* Base Component class. Provides `setState()` and `forceUpdate()`, which
2212
* trigger rendering
@@ -111,10 +101,7 @@ function rerender(internal) {
111101
0
112102
);
113103

114-
rendererState._context = getParentContext(internal);
115-
rendererState._commitQueue = [];
116-
rendererState._parentDom = getParentDom(internal);
117-
patch(internal, vnode);
104+
patch(internal, vnode, getParentDom(internal));
118105
commitRoot(internal);
119106
}
120107
}

src/create-root.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import options from './options';
1010
import { mount } from './diff/mount';
1111
import { patch } from './diff/patch';
1212
import { createInternal } from './tree';
13-
import { rendererState } from './component';
1413

1514
/**
1615
*
@@ -30,13 +29,8 @@ export function createRoot(parentDom) {
3029
firstChild =
3130
/** @type {import('./internal').PreactElement} */ (parentDom.firstChild);
3231

33-
rendererState._context = {};
34-
// List of effects that need to be called after diffing:
35-
rendererState._commitQueue = [];
36-
rendererState._parentDom = parentDom;
37-
3832
if (rootInternal) {
39-
patch(rootInternal, vnode);
33+
patch(rootInternal, vnode, parentDom);
4034
} else {
4135
rootInternal = createInternal(vnode);
4236

@@ -55,7 +49,7 @@ export function createRoot(parentDom) {
5549

5650
rootInternal._context = {};
5751

58-
mount(rootInternal, vnode, firstChild);
52+
mount(rootInternal, vnode, parentDom, firstChild);
5953
}
6054

6155
// Flush all queued effects

src/diff/children.js

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import { mount } from './mount';
1212
import { patch } from './patch';
1313
import { unmount } from './unmount';
1414
import { createInternal, getDomSibling } from '../tree';
15-
import { rendererState } from '../component';
1615

1716
/**
1817
* Update an internal with new children.
1918
* @param {import('../internal').Internal} internal The internal whose children should be patched
2019
* @param {import('../internal').ComponentChild[]} children The new children, represented as VNodes
20+
* @param {import('../internal').PreactElement} parentDom The element into which this subtree is rendered
2121
*/
22-
export function patchChildren(internal, children) {
22+
export function patchChildren(internal, children, parentDom) {
2323
let oldChildren =
2424
(internal._children && internal._children.slice()) || EMPTY_ARR;
2525

@@ -75,6 +75,7 @@ export function patchChildren(internal, children) {
7575
mount(
7676
childInternal,
7777
childVNode,
78+
parentDom,
7879
getDomSibling(internal, skewedIndex)
7980
);
8081
}
@@ -85,14 +86,10 @@ export function patchChildren(internal, children) {
8586
(MODE_HYDRATE | MODE_SUSPENDED)
8687
) {
8788
// We are resuming the hydration of a VNode
88-
mount(
89-
childInternal,
90-
childVNode,
91-
childInternal._dom
92-
);
89+
mount(childInternal, childVNode, parentDom, childInternal._dom);
9390
} else {
9491
// Morph the old element into the new one, but don't append it to the dom yet
95-
patch(childInternal, childVNode);
92+
patch(childInternal, childVNode, parentDom);
9693
}
9794

9895
go: if (mountingChild) {
@@ -102,7 +99,7 @@ export function patchChildren(internal, children) {
10299

103100
// Perform insert of new dom
104101
if (childInternal.flags & TYPE_DOM) {
105-
rendererState._parentDom.insertBefore(
102+
parentDom.insertBefore(
106103
childInternal._dom,
107104
getDomSibling(internal, skewedIndex)
108105
);
@@ -136,13 +133,9 @@ export function patchChildren(internal, children) {
136133

137134
let nextSibling = getDomSibling(internal, skewedIndex + 1);
138135
if (childInternal.flags & TYPE_DOM) {
139-
rendererState._parentDom.insertBefore(childInternal._dom, nextSibling);
136+
parentDom.insertBefore(childInternal._dom, nextSibling);
140137
} else {
141-
insertComponentDom(
142-
childInternal,
143-
nextSibling,
144-
rendererState._parentDom
145-
);
138+
insertComponentDom(childInternal, nextSibling, parentDom);
146139
}
147140
}
148141

src/diff/commit.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
1-
import { rendererState } from '../component';
21
import options from '../options';
32

3+
/**
4+
* A list of components with effects that need to be run at the end of the current render pass.
5+
* @type {import('../internal').CommitQueue}
6+
*/
7+
export let commitQueue = [];
8+
49
/**
510
* @param {import('../internal').Internal} rootInternal
611
*/
712
export function commitRoot(rootInternal) {
8-
let commitQueue = [].concat(rendererState._commitQueue);
9-
rendererState._commitQueue = [];
13+
let currentQueue = commitQueue;
14+
commitQueue = [];
1015

11-
if (options._commit) options._commit(rootInternal, commitQueue);
16+
if (options._commit) options._commit(rootInternal, currentQueue);
1217

13-
commitQueue.some(internal => {
18+
currentQueue.some(internal => {
1419
try {
1520
// @ts-ignore Reuse the root variable here so the type changes
16-
commitQueue = internal._commitCallbacks.length;
21+
currentQueue = internal._commitCallbacks.length;
1722
// @ts-ignore See above ts-ignore comment
18-
while (commitQueue--) {
23+
while (currentQueue--) {
1924
internal._commitCallbacks.shift()();
2025
}
2126
} catch (e) {

src/diff/mount.js

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ import {
1515
} from '../constants';
1616
import { normalizeToVNode, Fragment } from '../create-element';
1717
import { setProperty } from './props';
18-
import { createInternal } from '../tree';
18+
import { createInternal, getParentContext } from '../tree';
1919
import options from '../options';
20-
import { ENABLE_CLASSES, rendererState } from '../component';
20+
import { ENABLE_CLASSES } from '../component';
21+
import { commitQueue } from './commit';
2122
/**
2223
* Diff two virtual nodes and apply proper changes to the DOM
2324
* @param {import('../internal').Internal} internal The Internal node to mount
2425
* @param {import('../internal').VNode | string} newVNode The new virtual node
26+
* @param {import('../internal').PreactElement} parentDom The element into which this subtree is rendered
2527
* @param {import('../internal').PreactNode} startDom
2628
* @returns {import('../internal').PreactNode | null} pointer to the next DOM node to be hydrated (or null)
2729
*/
28-
export function mount(internal, newVNode, startDom) {
30+
export function mount(internal, newVNode, parentDom, startDom) {
2931
if (options._diff) options._diff(internal, newVNode);
3032

3133
/** @type {import('../internal').PreactNode} */
@@ -36,29 +38,30 @@ export function mount(internal, newVNode, startDom) {
3638
// Root nodes signal that an attempt to render into a specific DOM node on
3739
// the page. Root nodes can occur anywhere in the tree and not just at the
3840
// top.
39-
let prevParentDom = rendererState._parentDom;
40-
if (internal.flags & TYPE_ROOT) {
41-
rendererState._parentDom = newVNode.props._parentDom;
42-
43-
// Note: this is likely always true because we are inside mount()
44-
if (rendererState._parentDom !== prevParentDom) {
45-
prevStartDom = startDom;
46-
startDom = null;
47-
}
41+
if (
42+
internal.flags & TYPE_ROOT &&
43+
newVNode.props._parentDom !== parentDom
44+
) {
45+
parentDom = newVNode.props._parentDom;
46+
prevStartDom = startDom;
47+
startDom = null;
4848
}
4949

50-
let prevContext = rendererState._context;
51-
52-
nextDomSibling = mountComponent(internal, startDom);
50+
const renderResult = mountComponent(internal, startDom);
51+
if (renderResult === startDom) {
52+
nextDomSibling = startDom;
53+
} else {
54+
nextDomSibling = mountChildren(
55+
internal,
56+
renderResult,
57+
parentDom,
58+
startDom
59+
);
60+
}
5361

5462
if (internal._commitCallbacks.length) {
55-
rendererState._commitQueue.push(internal);
63+
commitQueue.push(internal);
5664
}
57-
58-
rendererState._parentDom = prevParentDom;
59-
// In the event this subtree creates a new context for its children, restore
60-
// the previous context for its siblings
61-
rendererState._context = prevContext;
6265
} else {
6366
// @TODO: we could just assign this as internal.dom here
6467
let hydrateDom =
@@ -192,14 +195,12 @@ function mountElement(internal, dom) {
192195
dom.innerHTML = newHtml.__html;
193196
}
194197
} else if (newChildren != null) {
195-
const prevParentDom = rendererState._parentDom;
196-
rendererState._parentDom = dom;
197198
mountChildren(
198199
internal,
199200
Array.isArray(newChildren) ? newChildren : [newChildren],
201+
dom,
200202
isNew ? null : dom.firstChild
201203
);
202-
rendererState._parentDom = prevParentDom;
203204
}
204205

205206
// (as above, don't diff props during hydration)
@@ -216,9 +217,10 @@ function mountElement(internal, dom) {
216217
* Mount all children of an Internal
217218
* @param {import('../internal').Internal} internal The parent Internal of the given children
218219
* @param {import('../internal').ComponentChild[]} children
220+
* @param {import('../internal').PreactElement} parentDom The element into which this subtree is rendered
219221
* @param {import('../internal').PreactNode} startDom
220222
*/
221-
export function mountChildren(internal, children, startDom) {
223+
export function mountChildren(internal, children, parentDom, startDom) {
222224
let internalChildren = (internal._children = []),
223225
i,
224226
childVNode,
@@ -240,7 +242,7 @@ export function mountChildren(internal, children, startDom) {
240242
internalChildren[i] = childInternal;
241243

242244
// Morph the old element into the new one, but don't append it to the dom yet
243-
mountedNextChild = mount(childInternal, childVNode, startDom);
245+
mountedNextChild = mount(childInternal, childVNode, parentDom, startDom);
244246

245247
newDom = childInternal._dom;
246248

@@ -253,7 +255,7 @@ export function mountChildren(internal, children, startDom) {
253255
// The DOM the diff should begin with is now startDom (since we inserted
254256
// newDom before startDom) so ignore mountedNextChild and continue with
255257
// startDom
256-
rendererState._parentDom.insertBefore(newDom, startDom);
258+
parentDom.insertBefore(newDom, startDom);
257259
}
258260

259261
if (childInternal.ref) {
@@ -297,13 +299,14 @@ function mountComponent(internal, startDom) {
297299

298300
// Necessary for createContext api. Setting this property will pass
299301
// the context value as `this.context` just for this component.
302+
let context = getParentContext(internal);
300303
let tmp = type.contextType;
301-
let provider = tmp && rendererState._context[tmp._id];
304+
let provider = tmp && context[tmp._id];
302305
let componentContext = tmp
303306
? provider
304307
? provider.props.value
305308
: tmp._defaultValue
306-
: rendererState._context;
309+
: context;
307310

308311
if (provider) provider._subs.add(internal);
309312

@@ -379,11 +382,7 @@ function mountComponent(internal, startDom) {
379382
c.state = c._nextState;
380383

381384
if (c.getChildContext != null) {
382-
rendererState._context = internal._context = Object.assign(
383-
{},
384-
rendererState._context,
385-
c.getChildContext()
386-
);
385+
internal._context = Object.assign({}, context, c.getChildContext());
387386
}
388387

389388
if (renderResult == null) {
@@ -401,5 +400,5 @@ function mountComponent(internal, startDom) {
401400
renderResult = [renderResult];
402401
}
403402

404-
return mountChildren(internal, renderResult, startDom);
403+
return renderResult;
405404
}

0 commit comments

Comments
 (0)