Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various perf and size improvements #3704

Merged
merged 7 commits into from
Sep 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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