Skip to content

Commit

Permalink
Merge pull request #7346 from QwikDev/v2-perf-improv
Browse files Browse the repository at this point in the history
fix(core): performance improvements
  • Loading branch information
wmertens authored Feb 18, 2025
2 parents 9f1e5fa + d8740b6 commit f75c875
Show file tree
Hide file tree
Showing 40 changed files with 542 additions and 622 deletions.
7 changes: 5 additions & 2 deletions packages/qwik/src/core/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ class DomContainer extends _SharedContainer implements ClientContainer {
export { DomContainer }
export { DomContainer as _DomContainer }

// @internal (undocumented)
export const _EFFECT_BACK_REF: unique symbol;

// @internal (undocumented)
export class _EffectData {
constructor(data: NodePropData);
Expand All @@ -244,7 +247,7 @@ _VNode | null | undefined,
Element,
//////////////////// 6 - Element
string | undefined,
...(string | null)[]
(string | null)[]
] & {
__brand__: 'ElementVNode';
};
Expand Down Expand Up @@ -1709,7 +1712,7 @@ _VNode | null,
_VNode | null,
/////////////// 4 - First child
_VNode | null,
...(string | null | boolean)[]
(string | null | boolean)[]
] & {
__brand__: 'FragmentNode' & 'HostElement';
};
Expand Down
15 changes: 8 additions & 7 deletions packages/qwik/src/core/client/dom-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
QSlotParent,
QStyle,
QStyleSelector,
QSubscribers,
QBackRefs,
Q_PROPS_SEPARATOR,
USE_ON_LOCAL_SEQ_IDX,
getQFuncs,
Expand Down Expand Up @@ -56,7 +56,7 @@ import {
vnode_getDomParent,
vnode_getParent,
vnode_getProp,
vnode_getPropStartIndex,
vnode_getProps,
vnode_insertBefore,
vnode_isVirtualVNode,
vnode_locate,
Expand Down Expand Up @@ -275,7 +275,7 @@ export class DomContainer extends _SharedContainer implements IClientContainer {
case ELEMENT_PROPS:
case OnRenderProp:
case QCtxAttr:
case QSubscribers:
case QBackRefs:
getObjectById = this.$getObjectById$;
break;
case ELEMENT_SEQ_IDX:
Expand Down Expand Up @@ -316,12 +316,13 @@ export class DomContainer extends _SharedContainer implements IClientContainer {
ensureProjectionResolved(vNode: VirtualVNode): void {
if ((vNode[VNodeProps.flags] & VNodeFlags.Resolved) === 0) {
vNode[VNodeProps.flags] |= VNodeFlags.Resolved;
for (let i = vnode_getPropStartIndex(vNode); i < vNode.length; i = i + 2) {
const prop = vNode[i] as string;
const props = vnode_getProps(vNode);
for (let i = 0; i < props.length; i = i + 2) {
const prop = props[i] as string;
if (isSlotProp(prop)) {
const value = vNode[i + 1];
const value = props[i + 1];
if (typeof value == 'string') {
vNode[i + 1] = this.vNodeLocate(value);
props[i + 1] = this.vNodeLocate(value);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/qwik/src/core/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export type ElementVNode = [
Element, //////////////////// 6 - Element
string | undefined, ///////// 7 - tag
/// Props
...(string | null)[], /////// 8 - attrs
(string | null)[], /////// 8 - attrs
] & { __brand__: 'ElementVNode' };

export const enum TextVNodeProps {
Expand Down Expand Up @@ -169,7 +169,7 @@ export type VirtualVNode = [
VNode | null, /////////////// 4 - First child
VNode | null, /////////////// 5 - Last child
/// Props
...(string | null | boolean)[], /////// 6 - attrs
(string | null | boolean)[], /////// 6 - attrs
] & { __brand__: 'FragmentNode' & 'HostElement' };

/** @internal */
Expand Down
46 changes: 20 additions & 26 deletions packages/qwik/src/core/client/vnode-diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
QSlot,
QSlotParent,
QStyle,
QSubscribers,
QBackRefs,
QTemplate,
Q_PREFIX,
dangerouslySetInnerHTML,
Expand All @@ -47,10 +47,8 @@ import type { HostElement, QElement, QwikLoaderEventScope, qWindow } from '../sh
import { DEBUG_TYPE, QContainerValue, VirtualType } from '../shared/types';
import type { DomContainer } from './dom-container';
import {
ElementVNodeProps,
VNodeFlags,
VNodeProps,
VirtualVNodeProps,
type ClientAttrKey,
type ClientAttrs,
type ClientContainer,
Expand All @@ -70,7 +68,7 @@ import {
vnode_getParent,
vnode_getProjectionParentComponent,
vnode_getProp,
vnode_getPropStartIndex,
vnode_getProps,
vnode_getText,
vnode_getType,
vnode_insertBefore,
Expand All @@ -93,15 +91,12 @@ import {
import { mapApp_findIndx } from './util-mapArray';
import { mapArray_set } from './util-mapArray';
import { getNewElementNamespaceData } from './vnode-namespace';
import { WrappedSignal, EffectProperty, isSignal, EffectPropData } from '../signal/signal';
import { WrappedSignal, EffectProperty, isSignal, SubscriptionData } from '../signal/signal';
import type { Signal } from '../signal/signal.public';
import { executeComponent } from '../shared/component-execution';
import { isParentSlotProp, isSlotProp } from '../shared/utils/prop';
import { escapeHTML } from '../shared/utils/character-escaping';
import {
clearSubscriberEffectDependencies,
clearVNodeEffectDependencies,
} from '../signal/signal-subscriber';
import { clearAllEffects } from '../signal/signal-cleanup';
import { serializeAttribute } from '../shared/utils/styles';
import { QError, qError } from '../shared/error/error';
import { getFileLocationFromJsx } from '../shared/utils/jsx-filename';
Expand Down Expand Up @@ -196,7 +191,7 @@ export const vnode_diff = (
descend(jsxValue, false);
} else if (isSignal(jsxValue)) {
if (vCurrent) {
clearVNodeEffectDependencies(container, vCurrent);
clearAllEffects(container, vCurrent);
}
expectVirtual(VirtualType.WrappedSignal, null);
descend(
Expand Down Expand Up @@ -412,9 +407,10 @@ export const vnode_diff = (

const projections: Array<string | JSXNodeInternal> = [];
if (host) {
const props = vnode_getProps(host);
// we need to create empty projections for all the slots to remove unused slots content
for (let i = vnode_getPropStartIndex(host); i < host.length; i = i + 2) {
const prop = host[i] as string;
for (let i = 0; i < props.length; i = i + 2) {
const prop = props[i] as string;
if (isSlotProp(prop)) {
const slotName = prop;
projections.push(slotName);
Expand Down Expand Up @@ -653,7 +649,7 @@ export const vnode_diff = (
}

if (isSignal(value)) {
const signalData = new EffectPropData({
const signalData = new SubscriptionData({
$scopedStyleIdPrefix$: scopedStyleIdPrefix,
$isConst$: true,
});
Expand Down Expand Up @@ -722,9 +718,7 @@ export const vnode_diff = (

function expectElement(jsx: JSXNodeInternal, elementName: string) {
const isSameElementName =
vCurrent &&
vnode_isElementVNode(vCurrent) &&
elementName.toLowerCase() === vnode_getElementName(vCurrent);
vCurrent && vnode_isElementVNode(vCurrent) && elementName === vnode_getElementName(vCurrent);
const jsxKey: string | null = jsx.key;
let needsQDispatchEventPatch = false;
const currentFile = getFileLocationFromJsx(jsx.dev);
Expand Down Expand Up @@ -801,10 +795,10 @@ export const vnode_diff = (
currentFile?: string | null
): boolean {
vnode_ensureElementInflated(vnode);
const dstAttrs = vnode as ClientAttrs;
const dstAttrs = vnode_getProps(vnode) as ClientAttrs;
let srcIdx = 0;
const srcLength = srcAttrs.length;
let dstIdx = ElementVNodeProps.PROPS_OFFSET;
let dstIdx = 0;
let dstLength = dstAttrs.length;
let srcKey: ClientAttrKey | null = srcIdx < srcLength ? srcAttrs[srcIdx++] : null;
let dstKey: ClientAttrKey | null = dstIdx < dstLength ? dstAttrs[dstIdx++] : null;
Expand Down Expand Up @@ -832,7 +826,7 @@ export const vnode_diff = (
}

if (isSignal(value)) {
const signalData = new EffectPropData({
const signalData = new SubscriptionData({
$scopedStyleIdPrefix$: scopedStyleIdPrefix,
$isConst$: false,
});
Expand Down Expand Up @@ -1136,7 +1130,7 @@ export const vnode_diff = (
jsxProps: Props
) {
if (host) {
clearVNodeEffectDependencies(container, host);
clearAllEffects(container, host);
}
vnode_insertBefore(
journal,
Expand Down Expand Up @@ -1253,8 +1247,8 @@ function propsDiffer(src: Record<string, any>, dst: Record<string, any>): boolea
if (!src || !dst) {
return true;
}
let srcKeys = removePropsKeys(Object.keys(src), ['children', QSubscribers]);
let dstKeys = removePropsKeys(Object.keys(dst), ['children', QSubscribers]);
let srcKeys = removePropsKeys(Object.keys(src), ['children', QBackRefs]);
let dstKeys = removePropsKeys(Object.keys(dst), ['children', QBackRefs]);
if (srcKeys.length !== dstKeys.length) {
return true;
}
Expand Down Expand Up @@ -1303,7 +1297,7 @@ export function cleanup(container: ClientContainer, vNode: VNode) {
do {
const type = vCursor[VNodeProps.flags];
if (type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) {
clearVNodeEffectDependencies(container, vCursor);
clearAllEffects(container, vCursor);
markVNodeAsDeleted(vCursor);
// Only elements and virtual nodes need to be traversed for children
if (type & VNodeFlags.Virtual) {
Expand All @@ -1313,7 +1307,7 @@ export function cleanup(container: ClientContainer, vNode: VNode) {
const obj = seq[i];
if (isTask(obj)) {
const task = obj;
clearSubscriberEffectDependencies(container, task);
clearAllEffects(container, task);
if (task.$flags$ & TaskFlags.VISIBLE_TASK) {
container.$scheduler$(ChoreType.CLEANUP_VISIBLE, task);
} else {
Expand All @@ -1329,8 +1323,8 @@ export function cleanup(container: ClientContainer, vNode: VNode) {
vnode_getProp(vCursor as VirtualVNode, OnRenderProp, null) !== null;
if (isComponent) {
// SPECIAL CASE: If we are a component, we need to descend into the projected content and release the content.
const attrs = vCursor;
for (let i = VirtualVNodeProps.PROPS_OFFSET; i < attrs.length; i = i + 2) {
const attrs = vnode_getProps(vCursor);
for (let i = 0; i < attrs.length; i = i + 2) {
const key = attrs[i] as string;
if (!isParentSlotProp(key) && isSlotProp(key)) {
const value = attrs[i + 1];
Expand Down
6 changes: 4 additions & 2 deletions packages/qwik/src/core/client/vnode-namespace.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isDev } from '@qwik.dev/core/build';
import { HTML_NS, MATH_NS, Q_PROPS_SEPARATOR, SVG_NS } from '../shared/utils/markers';
import { getDomContainerFromQContainerElement } from './dom-container';
import {
Expand All @@ -23,8 +24,9 @@ import {
type VNodeJournal,
} from './vnode';

export const isForeignObjectElement = (elementName: string) =>
elementName.toLowerCase() === 'foreignobject';
export const isForeignObjectElement = (elementName: string) => {
return isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject';
};

export const isSvgElement = (elementName: string) =>
elementName === 'svg' || isForeignObjectElement(elementName);
Expand Down
Loading

0 comments on commit f75c875

Please sign in to comment.