From 3cb829bb24f114bc1e004555073e31c58930a7b4 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Wed, 20 Aug 2025 18:53:28 +0200 Subject: [PATCH 1/9] Save bytes by inlining ref unmount checks --- src/diff/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/diff/index.js b/src/diff/index.js index 7cffa6d7ea..622b8e3137 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -662,13 +662,11 @@ function diffElementNodes( export function applyRef(ref, value, vnode) { try { if (typeof ref == 'function') { - let hasRefUnmount = typeof ref._unmount == 'function'; - if (hasRefUnmount) { - // @ts-ignore TS doesn't like moving narrowing checks into variables + if (typeof ref._unmount == 'function') { ref._unmount(); } - if (!hasRefUnmount || value != NULL) { + if (typeof ref._unmount != 'function' || value != NULL) { // Store the cleanup function on the function // instance object itself to avoid shape // transitioning vnode From 3963618cc72b39ecddc7fceccc09f1c73600f93e Mon Sep 17 00:00:00 2001 From: jdecroock Date: Wed, 20 Aug 2025 18:56:38 +0200 Subject: [PATCH 2/9] Inline unmount check --- src/diff/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/diff/index.js b/src/diff/index.js index 622b8e3137..79ac6e9df6 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -689,10 +689,8 @@ export function unmount(vnode, parentVNode, skipRemove) { let r; if (options.unmount) options.unmount(vnode); - if ((r = vnode.ref)) { - if (!r.current || r.current == vnode._dom) { - applyRef(r, NULL, parentVNode); - } + if ((r = vnode.ref) && (!r.current || r.current == vnode._dom)) { + applyRef(r, NULL, parentVNode); } if ((r = vnode._component) != NULL) { From f4db1f37f8c00170c5ee9b76528874af00c4dcbb Mon Sep 17 00:00:00 2001 From: jdecroock Date: Wed, 20 Aug 2025 18:59:13 +0200 Subject: [PATCH 3/9] Hoist excessDomChildren check --- src/diff/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/diff/index.js b/src/diff/index.js index 79ac6e9df6..66c4c6ec39 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -490,7 +490,8 @@ function diffElementNodes( else if (nodeType == 'math') namespace = MATH_NAMESPACE; else if (!namespace) namespace = XHTML_NAMESPACE; - if (excessDomChildren != NULL) { + let hasExcessDomChildren = excessDomChildren != NULL; + if (hasExcessDomChildren) { for (i = 0; i < excessDomChildren.length; i++) { value = excessDomChildren[i]; @@ -541,7 +542,7 @@ function diffElementNodes( // If we are in a situation where we are not hydrating but are using // existing DOM (e.g. replaceNode) we should read the existing DOM // attributes to diff them - if (!isHydrating && excessDomChildren != NULL) { + if (!isHydrating && hasExcessDomChildren) { oldProps = {}; for (i = 0; i < dom.attributes.length; i++) { value = dom.attributes[i]; @@ -620,7 +621,7 @@ function diffElementNodes( ); // Remove children that are not part of any vnode. - if (excessDomChildren != NULL) { + if (hasExcessDomChildren) { for (i = excessDomChildren.length; i--; ) { removeNode(excessDomChildren[i]); } From e82cc0ea776fdccec7688eded49686e3178ad7a5 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Wed, 20 Aug 2025 19:01:29 +0200 Subject: [PATCH 4/9] Reduce var alloc --- src/diff/index.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/diff/index.js b/src/diff/index.js index 66c4c6ec39..07cb863228 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -274,15 +274,14 @@ export function diff( let isTopLevelFragment = tmp != NULL && tmp.type === Fragment && tmp.key == NULL; - let renderResult = tmp; if (isTopLevelFragment) { - renderResult = cloneNode(tmp.props.children); + tmp = cloneNode(tmp.props.children); } oldDom = diffChildren( parentDom, - isArray(renderResult) ? renderResult : [renderResult], + isArray(tmp) ? tmp : [tmp], newVNode, oldVNode, globalContext, @@ -490,8 +489,7 @@ function diffElementNodes( else if (nodeType == 'math') namespace = MATH_NAMESPACE; else if (!namespace) namespace = XHTML_NAMESPACE; - let hasExcessDomChildren = excessDomChildren != NULL; - if (hasExcessDomChildren) { + if (excessDomChildren != NULL) { for (i = 0; i < excessDomChildren.length; i++) { value = excessDomChildren[i]; @@ -542,7 +540,7 @@ function diffElementNodes( // If we are in a situation where we are not hydrating but are using // existing DOM (e.g. replaceNode) we should read the existing DOM // attributes to diff them - if (!isHydrating && hasExcessDomChildren) { + if (!isHydrating && excessDomChildren != NULL) { oldProps = {}; for (i = 0; i < dom.attributes.length; i++) { value = dom.attributes[i]; @@ -621,7 +619,7 @@ function diffElementNodes( ); // Remove children that are not part of any vnode. - if (hasExcessDomChildren) { + if (excessDomChildren != NULL) { for (i = excessDomChildren.length; i--; ) { removeNode(excessDomChildren[i]); } From 4777d1d93894438e8dc5fd78a3d629e253024868 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Wed, 20 Aug 2025 19:04:16 +0200 Subject: [PATCH 5/9] Remove need for clearProcessingException --- src/diff/index.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/diff/index.js b/src/diff/index.js index 07cb863228..d076ba1286 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -88,8 +88,12 @@ export function diff( outer: if (typeof newType == 'function') { try { - let c, isNew, oldProps, oldState, snapshot, clearProcessingException; - let newProps = newVNode.props; + let c, + isNew, + oldProps, + oldState, + snapshot, + newProps = newVNode.props; const isClassComponent = 'prototype' in newType && newType.prototype.render; @@ -108,7 +112,6 @@ export function diff( c = newVNode._component = oldVNode._component; if (c._bits & COMPONENT_PENDING_ERROR) { c._bits |= COMPONENT_PROCESSING_EXCEPTION; - clearProcessingException = true; } } else { // Instantiate the new component @@ -301,7 +304,7 @@ export function diff( commitQueue.push(c); } - if (clearProcessingException) { + if (c._bits & COMPONENT_PENDING_ERROR) { c._bits &= ~(COMPONENT_PROCESSING_EXCEPTION | COMPONENT_PENDING_ERROR); } } catch (e) { From cc4816297d89978a72e6b2c16893f88fcc6f68f6 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Wed, 20 Aug 2025 19:07:07 +0200 Subject: [PATCH 6/9] Remove need for isNew --- src/diff/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/diff/index.js b/src/diff/index.js index d076ba1286..122f7cff33 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -89,7 +89,6 @@ export function diff( outer: if (typeof newType == 'function') { try { let c, - isNew, oldProps, oldState, snapshot, @@ -134,7 +133,6 @@ export function diff( if (!c.state) c.state = {}; c.context = componentContext; c._globalContext = globalContext; - isNew = true; c._bits |= COMPONENT_DIRTY; c._renderCallbacks = []; c._stateCallbacks = []; @@ -161,7 +159,7 @@ export function diff( c._vnode = newVNode; // Invoke pre-render lifecycle methods - if (isNew) { + if (!oldVNode._component) { if ( isClassComponent && newType.getDerivedStateFromProps == NULL && @@ -271,7 +269,11 @@ export function diff( globalContext = assign({}, globalContext, c.getChildContext()); } - if (isClassComponent && !isNew && c.getSnapshotBeforeUpdate != NULL) { + if ( + isClassComponent && + oldVNode._component && + c.getSnapshotBeforeUpdate != NULL + ) { snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState); } From eb3be96f1cc1dc67ba2c8f1b5e7b927fa5844160 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Wed, 20 Aug 2025 19:11:06 +0200 Subject: [PATCH 7/9] Save bytes by making shouldPlace a number --- src/diff/children.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/diff/children.js b/src/diff/children.js index 6739994197..8ec936f750 100644 --- a/src/diff/children.js +++ b/src/diff/children.js @@ -128,7 +128,7 @@ export function diffChildren( firstChildDom = newDom; } - let shouldPlace = !!(childVNode._flags & INSERT_VNODE); + let shouldPlace = childVNode._flags & INSERT_VNODE; if (shouldPlace || oldVNode._children === childVNode._children) { oldDom = insert(childVNode, oldDom, parentDom, shouldPlace); } else if (typeof childVNode.type == 'function' && result !== UNDEFINED) { @@ -251,9 +251,7 @@ function constructNewChildrenArray( // Here, we define isMounting for the purposes of the skew diffing // algorithm. Nodes that are unsuspending are considered mounting and we detect // this by checking if oldVNode._original == null - const isMounting = oldVNode == NULL || oldVNode._original == NULL; - - if (isMounting) { + if (oldVNode == NULL || oldVNode._original == NULL) { if (matchingIndex == -1) { // When the array of children is growing we need to decrease the skew // as we are adding a new element to the array. @@ -340,7 +338,7 @@ function constructNewChildrenArray( * @param {VNode} parentVNode * @param {PreactElement} oldDom * @param {PreactElement} parentDom - * @param {boolean} shouldPlace + * @param {number} shouldPlace * @returns {PreactElement} */ function insert(parentVNode, oldDom, parentDom, shouldPlace) { From 4f89718fb4bf7085de9ce7d2fce16b1c966dae50 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Wed, 20 Aug 2025 19:15:21 +0200 Subject: [PATCH 8/9] Use else to save some bytees --- src/component.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/component.js b/src/component.js index 639a377c5c..4dd23ec59c 100644 --- a/src/component.js +++ b/src/component.js @@ -48,11 +48,10 @@ BaseComponent.prototype.setState = function (update, callback) { if (update) { assign(s, update); + } else { + return; } - // Skip update if updater function returned null - if (update == NULL) return; - if (this._vnode) { if (callback) { this._stateCallbacks.push(callback); From a3ebdc9ff2b40e3d85d2f270b50192566b166b75 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Wed, 20 Aug 2025 19:21:54 +0200 Subject: [PATCH 9/9] Save some render bytes --- src/render.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/render.js b/src/render.js index b6e50b70ff..0bd990fb78 100644 --- a/src/render.js +++ b/src/render.js @@ -18,7 +18,7 @@ export function render(vnode, parentDom) { if (options._root) options._root(vnode, parentDom); // @ts-expect-error - let isHydrating = !!(vnode && vnode._flags & MODE_HYDRATE); + let isHydrating = vnode && vnode._flags & MODE_HYDRATE; // To be able to support calling `render()` multiple times on the same // DOM node, we need to obtain a reference to the previous tree. We do @@ -27,7 +27,7 @@ export function render(vnode, parentDom) { // means that we are mounting a new tree for the first time. let oldVNode = isHydrating ? NULL : parentDom._children; - vnode = parentDom._children = createElement(Fragment, NULL, [vnode]); + parentDom._children = createElement(Fragment, NULL, [vnode]); // List of effects that need to be called after diffing. let commitQueue = [], @@ -37,7 +37,7 @@ export function render(vnode, parentDom) { parentDom, // Determine the new vnode tree and store it on the DOM element on // our custom `_children` property. - vnode, + parentDom._children, oldVNode || EMPTY_OBJ, EMPTY_OBJ, parentDom.namespaceURI, @@ -48,13 +48,14 @@ export function render(vnode, parentDom) { : NULL, commitQueue, oldVNode ? oldVNode._dom : parentDom.firstChild, + // @ts-expect-error we are doing a bit-wise operation so it's either 0 or true isHydrating, refQueue, parentDom.ownerDocument ); // Flush all queued effects - commitRoot(commitQueue, vnode, refQueue); + commitRoot(commitQueue, parentDom._children, refQueue); } /**