Skip to content

Commit f75c875

Browse files
authored
Merge pull request #7346 from QwikDev/v2-perf-improv
fix(core): performance improvements
2 parents 9f1e5fa + d8740b6 commit f75c875

40 files changed

+542
-622
lines changed

packages/qwik/src/core/api.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@ class DomContainer extends _SharedContainer implements ClientContainer {
219219
export { DomContainer }
220220
export { DomContainer as _DomContainer }
221221

222+
// @internal (undocumented)
223+
export const _EFFECT_BACK_REF: unique symbol;
224+
222225
// @internal (undocumented)
223226
export class _EffectData {
224227
constructor(data: NodePropData);
@@ -244,7 +247,7 @@ _VNode | null | undefined,
244247
Element,
245248
//////////////////// 6 - Element
246249
string | undefined,
247-
...(string | null)[]
250+
(string | null)[]
248251
] & {
249252
__brand__: 'ElementVNode';
250253
};
@@ -1709,7 +1712,7 @@ _VNode | null,
17091712
_VNode | null,
17101713
/////////////// 4 - First child
17111714
_VNode | null,
1712-
...(string | null | boolean)[]
1715+
(string | null | boolean)[]
17131716
] & {
17141717
__brand__: 'FragmentNode' & 'HostElement';
17151718
};

packages/qwik/src/core/client/dom-container.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
QSlotParent,
2626
QStyle,
2727
QStyleSelector,
28-
QSubscribers,
28+
QBackRefs,
2929
Q_PROPS_SEPARATOR,
3030
USE_ON_LOCAL_SEQ_IDX,
3131
getQFuncs,
@@ -56,7 +56,7 @@ import {
5656
vnode_getDomParent,
5757
vnode_getParent,
5858
vnode_getProp,
59-
vnode_getPropStartIndex,
59+
vnode_getProps,
6060
vnode_insertBefore,
6161
vnode_isVirtualVNode,
6262
vnode_locate,
@@ -275,7 +275,7 @@ export class DomContainer extends _SharedContainer implements IClientContainer {
275275
case ELEMENT_PROPS:
276276
case OnRenderProp:
277277
case QCtxAttr:
278-
case QSubscribers:
278+
case QBackRefs:
279279
getObjectById = this.$getObjectById$;
280280
break;
281281
case ELEMENT_SEQ_IDX:
@@ -316,12 +316,13 @@ export class DomContainer extends _SharedContainer implements IClientContainer {
316316
ensureProjectionResolved(vNode: VirtualVNode): void {
317317
if ((vNode[VNodeProps.flags] & VNodeFlags.Resolved) === 0) {
318318
vNode[VNodeProps.flags] |= VNodeFlags.Resolved;
319-
for (let i = vnode_getPropStartIndex(vNode); i < vNode.length; i = i + 2) {
320-
const prop = vNode[i] as string;
319+
const props = vnode_getProps(vNode);
320+
for (let i = 0; i < props.length; i = i + 2) {
321+
const prop = props[i] as string;
321322
if (isSlotProp(prop)) {
322-
const value = vNode[i + 1];
323+
const value = props[i + 1];
323324
if (typeof value == 'string') {
324-
vNode[i + 1] = this.vNodeLocate(value);
325+
props[i + 1] = this.vNodeLocate(value);
325326
}
326327
}
327328
}

packages/qwik/src/core/client/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export type ElementVNode = [
132132
Element, //////////////////// 6 - Element
133133
string | undefined, ///////// 7 - tag
134134
/// Props
135-
...(string | null)[], /////// 8 - attrs
135+
(string | null)[], /////// 8 - attrs
136136
] & { __brand__: 'ElementVNode' };
137137

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

175175
/** @internal */

packages/qwik/src/core/client/vnode-diff.ts

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
QSlot,
2828
QSlotParent,
2929
QStyle,
30-
QSubscribers,
30+
QBackRefs,
3131
QTemplate,
3232
Q_PREFIX,
3333
dangerouslySetInnerHTML,
@@ -47,10 +47,8 @@ import type { HostElement, QElement, QwikLoaderEventScope, qWindow } from '../sh
4747
import { DEBUG_TYPE, QContainerValue, VirtualType } from '../shared/types';
4848
import type { DomContainer } from './dom-container';
4949
import {
50-
ElementVNodeProps,
5150
VNodeFlags,
5251
VNodeProps,
53-
VirtualVNodeProps,
5452
type ClientAttrKey,
5553
type ClientAttrs,
5654
type ClientContainer,
@@ -70,7 +68,7 @@ import {
7068
vnode_getParent,
7169
vnode_getProjectionParentComponent,
7270
vnode_getProp,
73-
vnode_getPropStartIndex,
71+
vnode_getProps,
7472
vnode_getText,
7573
vnode_getType,
7674
vnode_insertBefore,
@@ -93,15 +91,12 @@ import {
9391
import { mapApp_findIndx } from './util-mapArray';
9492
import { mapArray_set } from './util-mapArray';
9593
import { getNewElementNamespaceData } from './vnode-namespace';
96-
import { WrappedSignal, EffectProperty, isSignal, EffectPropData } from '../signal/signal';
94+
import { WrappedSignal, EffectProperty, isSignal, SubscriptionData } from '../signal/signal';
9795
import type { Signal } from '../signal/signal.public';
9896
import { executeComponent } from '../shared/component-execution';
9997
import { isParentSlotProp, isSlotProp } from '../shared/utils/prop';
10098
import { escapeHTML } from '../shared/utils/character-escaping';
101-
import {
102-
clearSubscriberEffectDependencies,
103-
clearVNodeEffectDependencies,
104-
} from '../signal/signal-subscriber';
99+
import { clearAllEffects } from '../signal/signal-cleanup';
105100
import { serializeAttribute } from '../shared/utils/styles';
106101
import { QError, qError } from '../shared/error/error';
107102
import { getFileLocationFromJsx } from '../shared/utils/jsx-filename';
@@ -196,7 +191,7 @@ export const vnode_diff = (
196191
descend(jsxValue, false);
197192
} else if (isSignal(jsxValue)) {
198193
if (vCurrent) {
199-
clearVNodeEffectDependencies(container, vCurrent);
194+
clearAllEffects(container, vCurrent);
200195
}
201196
expectVirtual(VirtualType.WrappedSignal, null);
202197
descend(
@@ -412,9 +407,10 @@ export const vnode_diff = (
412407

413408
const projections: Array<string | JSXNodeInternal> = [];
414409
if (host) {
410+
const props = vnode_getProps(host);
415411
// we need to create empty projections for all the slots to remove unused slots content
416-
for (let i = vnode_getPropStartIndex(host); i < host.length; i = i + 2) {
417-
const prop = host[i] as string;
412+
for (let i = 0; i < props.length; i = i + 2) {
413+
const prop = props[i] as string;
418414
if (isSlotProp(prop)) {
419415
const slotName = prop;
420416
projections.push(slotName);
@@ -653,7 +649,7 @@ export const vnode_diff = (
653649
}
654650

655651
if (isSignal(value)) {
656-
const signalData = new EffectPropData({
652+
const signalData = new SubscriptionData({
657653
$scopedStyleIdPrefix$: scopedStyleIdPrefix,
658654
$isConst$: true,
659655
});
@@ -722,9 +718,7 @@ export const vnode_diff = (
722718

723719
function expectElement(jsx: JSXNodeInternal, elementName: string) {
724720
const isSameElementName =
725-
vCurrent &&
726-
vnode_isElementVNode(vCurrent) &&
727-
elementName.toLowerCase() === vnode_getElementName(vCurrent);
721+
vCurrent && vnode_isElementVNode(vCurrent) && elementName === vnode_getElementName(vCurrent);
728722
const jsxKey: string | null = jsx.key;
729723
let needsQDispatchEventPatch = false;
730724
const currentFile = getFileLocationFromJsx(jsx.dev);
@@ -801,10 +795,10 @@ export const vnode_diff = (
801795
currentFile?: string | null
802796
): boolean {
803797
vnode_ensureElementInflated(vnode);
804-
const dstAttrs = vnode as ClientAttrs;
798+
const dstAttrs = vnode_getProps(vnode) as ClientAttrs;
805799
let srcIdx = 0;
806800
const srcLength = srcAttrs.length;
807-
let dstIdx = ElementVNodeProps.PROPS_OFFSET;
801+
let dstIdx = 0;
808802
let dstLength = dstAttrs.length;
809803
let srcKey: ClientAttrKey | null = srcIdx < srcLength ? srcAttrs[srcIdx++] : null;
810804
let dstKey: ClientAttrKey | null = dstIdx < dstLength ? dstAttrs[dstIdx++] : null;
@@ -832,7 +826,7 @@ export const vnode_diff = (
832826
}
833827

834828
if (isSignal(value)) {
835-
const signalData = new EffectPropData({
829+
const signalData = new SubscriptionData({
836830
$scopedStyleIdPrefix$: scopedStyleIdPrefix,
837831
$isConst$: false,
838832
});
@@ -1136,7 +1130,7 @@ export const vnode_diff = (
11361130
jsxProps: Props
11371131
) {
11381132
if (host) {
1139-
clearVNodeEffectDependencies(container, host);
1133+
clearAllEffects(container, host);
11401134
}
11411135
vnode_insertBefore(
11421136
journal,
@@ -1253,8 +1247,8 @@ function propsDiffer(src: Record<string, any>, dst: Record<string, any>): boolea
12531247
if (!src || !dst) {
12541248
return true;
12551249
}
1256-
let srcKeys = removePropsKeys(Object.keys(src), ['children', QSubscribers]);
1257-
let dstKeys = removePropsKeys(Object.keys(dst), ['children', QSubscribers]);
1250+
let srcKeys = removePropsKeys(Object.keys(src), ['children', QBackRefs]);
1251+
let dstKeys = removePropsKeys(Object.keys(dst), ['children', QBackRefs]);
12581252
if (srcKeys.length !== dstKeys.length) {
12591253
return true;
12601254
}
@@ -1303,7 +1297,7 @@ export function cleanup(container: ClientContainer, vNode: VNode) {
13031297
do {
13041298
const type = vCursor[VNodeProps.flags];
13051299
if (type & VNodeFlags.ELEMENT_OR_VIRTUAL_MASK) {
1306-
clearVNodeEffectDependencies(container, vCursor);
1300+
clearAllEffects(container, vCursor);
13071301
markVNodeAsDeleted(vCursor);
13081302
// Only elements and virtual nodes need to be traversed for children
13091303
if (type & VNodeFlags.Virtual) {
@@ -1313,7 +1307,7 @@ export function cleanup(container: ClientContainer, vNode: VNode) {
13131307
const obj = seq[i];
13141308
if (isTask(obj)) {
13151309
const task = obj;
1316-
clearSubscriberEffectDependencies(container, task);
1310+
clearAllEffects(container, task);
13171311
if (task.$flags$ & TaskFlags.VISIBLE_TASK) {
13181312
container.$scheduler$(ChoreType.CLEANUP_VISIBLE, task);
13191313
} else {
@@ -1329,8 +1323,8 @@ export function cleanup(container: ClientContainer, vNode: VNode) {
13291323
vnode_getProp(vCursor as VirtualVNode, OnRenderProp, null) !== null;
13301324
if (isComponent) {
13311325
// SPECIAL CASE: If we are a component, we need to descend into the projected content and release the content.
1332-
const attrs = vCursor;
1333-
for (let i = VirtualVNodeProps.PROPS_OFFSET; i < attrs.length; i = i + 2) {
1326+
const attrs = vnode_getProps(vCursor);
1327+
for (let i = 0; i < attrs.length; i = i + 2) {
13341328
const key = attrs[i] as string;
13351329
if (!isParentSlotProp(key) && isSlotProp(key)) {
13361330
const value = attrs[i + 1];

packages/qwik/src/core/client/vnode-namespace.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { isDev } from '@qwik.dev/core/build';
12
import { HTML_NS, MATH_NS, Q_PROPS_SEPARATOR, SVG_NS } from '../shared/utils/markers';
23
import { getDomContainerFromQContainerElement } from './dom-container';
34
import {
@@ -23,8 +24,9 @@ import {
2324
type VNodeJournal,
2425
} from './vnode';
2526

26-
export const isForeignObjectElement = (elementName: string) =>
27-
elementName.toLowerCase() === 'foreignobject';
27+
export const isForeignObjectElement = (elementName: string) => {
28+
return isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject';
29+
};
2830

2931
export const isSvgElement = (elementName: string) =>
3032
elementName === 'svg' || isForeignObjectElement(elementName);

0 commit comments

Comments
 (0)