Skip to content

Commit

Permalink
Various perf and size improvements (#3704)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
JoviDeCroock authored Sep 3, 2022
1 parent aa8d57b commit 53621d9
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 117 deletions.
17 changes: 2 additions & 15 deletions src/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,10 @@ import options from './options';
import { createVNode, Fragment } from './create-element';
import { patch } from './diff/patch';
import { DIRTY_BIT, FORCE_UPDATE, MODE_UNMOUNTING } from './constants';
import { getParentContext, getParentDom } from './tree';
import { getParentDom } from './tree';

export let ENABLE_CLASSES = false;

/**
* The render queue
* @type {import('./internal').RendererState}
*/
export const rendererState = {
_parentDom: null,
_context: {},
_commitQueue: []
};

/**
* Base Component class. Provides `setState()` and `forceUpdate()`, which
* trigger rendering
Expand Down Expand Up @@ -111,10 +101,7 @@ function rerender(internal) {
0
);

rendererState._context = getParentContext(internal);
rendererState._commitQueue = [];
rendererState._parentDom = getParentDom(internal);
patch(internal, vnode);
patch(internal, vnode, getParentDom(internal));
commitRoot(internal);
}
}
Expand Down
10 changes: 2 additions & 8 deletions src/create-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import options from './options';
import { mount } from './diff/mount';
import { patch } from './diff/patch';
import { createInternal } from './tree';
import { rendererState } from './component';

/**
*
Expand All @@ -30,13 +29,8 @@ export function createRoot(parentDom) {
firstChild =
/** @type {import('./internal').PreactElement} */ (parentDom.firstChild);

rendererState._context = {};
// List of effects that need to be called after diffing:
rendererState._commitQueue = [];
rendererState._parentDom = parentDom;

if (rootInternal) {
patch(rootInternal, vnode);
patch(rootInternal, vnode, parentDom);
} else {
rootInternal = createInternal(vnode);

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

rootInternal._context = {};

mount(rootInternal, vnode, firstChild);
mount(rootInternal, vnode, parentDom, firstChild);
}

// Flush all queued effects
Expand Down
23 changes: 8 additions & 15 deletions src/diff/children.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import { mount } from './mount';
import { patch } from './patch';
import { unmount } from './unmount';
import { createInternal, getDomSibling } from '../tree';
import { rendererState } from '../component';

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

Expand Down Expand Up @@ -75,6 +75,7 @@ export function patchChildren(internal, children) {
mount(
childInternal,
childVNode,
parentDom,
getDomSibling(internal, skewedIndex)
);
}
Expand All @@ -85,14 +86,10 @@ export function patchChildren(internal, children) {
(MODE_HYDRATE | MODE_SUSPENDED)
) {
// We are resuming the hydration of a VNode
mount(
childInternal,
childVNode,
childInternal._dom
);
mount(childInternal, childVNode, parentDom, childInternal._dom);
} else {
// Morph the old element into the new one, but don't append it to the dom yet
patch(childInternal, childVNode);
patch(childInternal, childVNode, parentDom);
}

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

// Perform insert of new dom
if (childInternal.flags & TYPE_DOM) {
rendererState._parentDom.insertBefore(
parentDom.insertBefore(
childInternal._dom,
getDomSibling(internal, skewedIndex)
);
Expand Down Expand Up @@ -136,13 +133,9 @@ export function patchChildren(internal, children) {

let nextSibling = getDomSibling(internal, skewedIndex + 1);
if (childInternal.flags & TYPE_DOM) {
rendererState._parentDom.insertBefore(childInternal._dom, nextSibling);
parentDom.insertBefore(childInternal._dom, nextSibling);
} else {
insertComponentDom(
childInternal,
nextSibling,
rendererState._parentDom
);
insertComponentDom(childInternal, nextSibling, parentDom);
}
}

Expand Down
19 changes: 12 additions & 7 deletions src/diff/commit.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import { rendererState } from '../component';
import options from '../options';

/**
* A list of components with effects that need to be run at the end of the current render pass.
* @type {import('../internal').CommitQueue}
*/
export let commitQueue = [];

/**
* @param {import('../internal').Internal} rootInternal
*/
export function commitRoot(rootInternal) {
let commitQueue = [].concat(rendererState._commitQueue);
rendererState._commitQueue = [];
let currentQueue = commitQueue;
commitQueue = [];

if (options._commit) options._commit(rootInternal, commitQueue);
if (options._commit) options._commit(rootInternal, currentQueue);

commitQueue.some(internal => {
currentQueue.some(internal => {
try {
// @ts-ignore Reuse the root variable here so the type changes
commitQueue = internal._commitCallbacks.length;
currentQueue = internal._commitCallbacks.length;
// @ts-ignore See above ts-ignore comment
while (commitQueue--) {
while (currentQueue--) {
internal._commitCallbacks.shift()();
}
} catch (e) {
Expand Down
69 changes: 34 additions & 35 deletions src/diff/mount.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ import {
} from '../constants';
import { normalizeToVNode, Fragment } from '../create-element';
import { setProperty } from './props';
import { createInternal } from '../tree';
import { createInternal, getParentContext } from '../tree';
import options from '../options';
import { ENABLE_CLASSES, rendererState } from '../component';
import { ENABLE_CLASSES } from '../component';
import { commitQueue } from './commit';
/**
* Diff two virtual nodes and apply proper changes to the DOM
* @param {import('../internal').Internal} internal The Internal node to mount
* @param {import('../internal').VNode | string} newVNode The new virtual node
* @param {import('../internal').PreactElement} parentDom The element into which this subtree is rendered
* @param {import('../internal').PreactNode} startDom
* @returns {import('../internal').PreactNode | null} pointer to the next DOM node to be hydrated (or null)
*/
export function mount(internal, newVNode, startDom) {
export function mount(internal, newVNode, parentDom, startDom) {
if (options._diff) options._diff(internal, newVNode);

/** @type {import('../internal').PreactNode} */
Expand All @@ -36,29 +38,30 @@ export function mount(internal, newVNode, startDom) {
// Root nodes signal that an attempt to render into a specific DOM node on
// the page. Root nodes can occur anywhere in the tree and not just at the
// top.
let prevParentDom = rendererState._parentDom;
if (internal.flags & TYPE_ROOT) {
rendererState._parentDom = newVNode.props._parentDom;

// Note: this is likely always true because we are inside mount()
if (rendererState._parentDom !== prevParentDom) {
prevStartDom = startDom;
startDom = null;
}
if (
internal.flags & TYPE_ROOT &&
newVNode.props._parentDom !== parentDom
) {
parentDom = newVNode.props._parentDom;
prevStartDom = startDom;
startDom = null;
}

let prevContext = rendererState._context;

nextDomSibling = mountComponent(internal, startDom);
const renderResult = mountComponent(internal, startDom);
if (renderResult === startDom) {
nextDomSibling = startDom;
} else {
nextDomSibling = mountChildren(
internal,
renderResult,
parentDom,
startDom
);
}

if (internal._commitCallbacks.length) {
rendererState._commitQueue.push(internal);
commitQueue.push(internal);
}

rendererState._parentDom = prevParentDom;
// In the event this subtree creates a new context for its children, restore
// the previous context for its siblings
rendererState._context = prevContext;
} else {
// @TODO: we could just assign this as internal.dom here
let hydrateDom =
Expand Down Expand Up @@ -192,14 +195,12 @@ function mountElement(internal, dom) {
dom.innerHTML = newHtml.__html;
}
} else if (newChildren != null) {
const prevParentDom = rendererState._parentDom;
rendererState._parentDom = dom;
mountChildren(
internal,
Array.isArray(newChildren) ? newChildren : [newChildren],
dom,
isNew ? null : dom.firstChild
);
rendererState._parentDom = prevParentDom;
}

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

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

newDom = childInternal._dom;

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

if (childInternal.ref) {
Expand Down Expand Up @@ -297,13 +299,14 @@ function mountComponent(internal, startDom) {

// Necessary for createContext api. Setting this property will pass
// the context value as `this.context` just for this component.
let context = getParentContext(internal);
let tmp = type.contextType;
let provider = tmp && rendererState._context[tmp._id];
let provider = tmp && context[tmp._id];
let componentContext = tmp
? provider
? provider.props.value
: tmp._defaultValue
: rendererState._context;
: context;

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

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

if (c.getChildContext != null) {
rendererState._context = internal._context = Object.assign(
{},
rendererState._context,
c.getChildContext()
);
internal._context = Object.assign({}, context, c.getChildContext());
}

if (renderResult == null) {
Expand All @@ -401,5 +400,5 @@ function mountComponent(internal, startDom) {
renderResult = [renderResult];
}

return mountChildren(internal, renderResult, startDom);
return renderResult;
}
Loading

0 comments on commit 53621d9

Please sign in to comment.