Compare commits

...

2 Commits

Author SHA1 Message Date
Géry Debongnie 4cffb23bd5 [REF] blockdom: some small optimization 2023-07-18 13:30:45 +02:00
Géry Debongnie 5154a1cc1d [REF] blockdom: micro optimizations 2023-07-17 15:01:42 +02:00
6 changed files with 77 additions and 37 deletions
+59 -19
View File
@@ -93,6 +93,17 @@ function normalizeNode(node: HTMLElement | Text) {
} }
} }
/**
* Encode 2 numbers and 1 boolean in a number, using 31 bits:
* n1 => encoded in 16 most significant bits
* n2 => encoded in 15 next bits
* boolean => encoded in last significant bit.
* This code assumes that n1 and n2 are small enough to fit in that number of bits
*/
function encodeValue(n1: number, n2: number, b: boolean): number {
return (((n1 << 15) | n2) << 1) | (b ? 1 : 0);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// building a intermediate tree // building a intermediate tree
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -306,7 +317,7 @@ interface IndexedLocation extends Location {
interface Child { interface Child {
parentRefIdx: number; parentRefIdx: number;
afterRefIdx?: number; afterRefIdx: number;
isOnlyChild?: boolean; isOnlyChild?: boolean;
} }
@@ -374,6 +385,7 @@ function updateCtx(ctx: BlockCtx, tree: IntermediateTree) {
// tree is the parentnode here // tree is the parentnode here
ctx.children[info.idx] = { ctx.children[info.idx] = {
parentRefIdx: info.refIdx!, parentRefIdx: info.refIdx!,
afterRefIdx: 0,
isOnlyChild: true, isOnlyChild: true,
}; };
} else { } else {
@@ -501,7 +513,6 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
})); }));
const locN = locations.length; const locN = locations.length;
const childN = children.length; const childN = children.length;
const childrenLocs = children;
const isDynamic = refN > 0; const isDynamic = refN > 0;
// these values are defined here to make them faster to lookup in the class // these values are defined here to make them faster to lookup in the class
@@ -556,6 +567,19 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
} }
if (isDynamic) { if (isDynamic) {
const nextSibling = nodeGetNextSibling;
const firstChild = nodeGetFirstChild;
const bitPackedCollectors = new Uint32Array(
collectors.map((c) => {
return encodeValue(c.idx, c.prevIdx, c.getVal === nextSibling);
})
);
const childrenLocs = new Uint32Array(
children.map((c) => {
return encodeValue(c.afterRefIdx, c.parentRefIdx, Boolean(c.isOnlyChild));
})
);
Block.prototype.mount = function mount(parent: HTMLElement, afterNode: Node | null) { Block.prototype.mount = function mount(parent: HTMLElement, afterNode: Node | null) {
const el = nodeCloneNode.call(template, true); const el = nodeCloneNode.call(template, true);
// collecting references // collecting references
@@ -563,12 +587,17 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
this.refs = refs; this.refs = refs;
refs[0] = el; refs[0] = el;
for (let i = 0; i < colN; i++) { for (let i = 0; i < colN; i++) {
const w = collectors[i]; let info = bitPackedCollectors[i];
refs[w.idx] = w.getVal.call(refs[w.prevIdx]); // decode info
const fn = (info & 1) === 1 ? nextSibling : firstChild;
info = info >> 1;
const prevIdx = info & 0b111111111111111;
const idx = info >> 15;
refs[idx] = fn.call(refs[prevIdx]);
} }
// applying data to all update points // applying data to all update points
if (locN) { if (locN !== 0) {
const data = this.data!; const data = this.data!;
for (let i = 0; i < locN; i++) { for (let i = 0; i < locN; i++) {
const loc = locations[i]; const loc = locations[i];
@@ -579,15 +608,21 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
nodeInsertBefore.call(parent, el, afterNode); nodeInsertBefore.call(parent, el, afterNode);
// preparing all children // preparing all children
if (childN) { if (childN !== 0) {
const children = this.children; const children = this.children;
for (let i = 0; i < childN; i++) { for (let i = 0; i < childN; i++) {
const child = children![i]; const child = children![i];
if (child) { if (child !== undefined) {
const loc = childrenLocs[i]; let info = childrenLocs[i];
const afterNode = loc.afterRefIdx ? refs[loc.afterRefIdx] : null; // decode info
child.isOnlyChild = loc.isOnlyChild; const isOnlyChild = info & 1;
child.mount(refs[loc.parentRefIdx] as any, afterNode); info = info >> 1;
const parentRefIdx = info & 0b111111111111111;
const afterRefIdx = info >> 15;
const afterNode = afterRefIdx !== 0 ? refs[afterRefIdx] : null;
child.isOnlyChild = isOnlyChild as any;
child.mount(refs[parentRefIdx] as any, afterNode);
} }
} }
} }
@@ -601,7 +636,7 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
} }
const refs = this.refs!; const refs = this.refs!;
// update texts/attributes/ // update texts/attributes/
if (locN) { if (locN !== 0) {
const data1 = this.data!; const data1 = this.data!;
const data2 = other.data!; const data2 = other.data!;
for (let i = 0; i < locN; i++) { for (let i = 0; i < locN; i++) {
@@ -616,14 +651,14 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
} }
// update children // update children
if (childN) { if (childN !== 0) {
let children1 = this.children; let children1 = this.children;
const children2 = other.children; const children2 = other.children;
for (let i = 0; i < childN; i++) { for (let i = 0; i < childN; i++) {
const child1 = children1![i]; const child1 = children1![i];
const child2 = children2![i]; const child2 = children2![i];
if (child1) { if (child1 !== undefined) {
if (child2) { if (child2 !== undefined) {
child1.patch(child2, withBeforeRemove); child1.patch(child2, withBeforeRemove);
} else { } else {
if (withBeforeRemove) { if (withBeforeRemove) {
@@ -632,10 +667,15 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
child1.remove(); child1.remove();
children1![i] = undefined; children1![i] = undefined;
} }
} else if (child2) { } else if (child2 !== undefined) {
const loc = childrenLocs[i]; let info = childrenLocs[i];
const afterNode = loc.afterRefIdx ? refs[loc.afterRefIdx] : null; // decode info
child2.mount(refs[loc.parentRefIdx] as any, afterNode); info = info >> 1;
const parentRefIdx = info & 0b111111111111111;
const afterRefIdx = info >> 15;
const afterNode = afterRefIdx !== 0 ? refs[afterRefIdx] : null;
child2.mount(refs[parentRefIdx] as any, afterNode);
children1![i] = child2; children1![i] = child2;
} }
} }
+1 -1
View File
@@ -41,7 +41,7 @@ function createElementHandler(evName: string, capture: boolean = false): EventHa
} }
function remove(this: HTMLElement) { function remove(this: HTMLElement) {
delete (this as any)[eventKey]; (this as any)[eventKey] = false;
this.removeEventListener(evName, listener, { capture }); this.removeEventListener(evName, listener, { capture });
} }
function update(this: HTMLElement, data: any) { function update(this: HTMLElement, data: any) {
+5 -5
View File
@@ -28,7 +28,7 @@ class VList {
this.anchor = _anchor; this.anchor = _anchor;
nodeInsertBefore.call(parent, _anchor, afterNode); nodeInsertBefore.call(parent, _anchor, afterNode);
const l = children.length; const l = children.length;
if (l) { if (l !== 0) {
const mount = children[0].mount; const mount = children[0].mount;
for (let i = 0; i < l; i++) { for (let i = 0; i < l; i++) {
mount.call(children[i], parent, _anchor); mount.call(children[i], parent, _anchor);
@@ -186,7 +186,7 @@ class VList {
} else { } else {
for (let i = startIdx1; i <= endIdx1; i++) { for (let i = startIdx1; i <= endIdx1; i++) {
let ch = ch1[i]; let ch = ch1[i];
if (ch) { if (ch !== null) {
if (withBeforeRemove) { if (withBeforeRemove) {
beforeRemove.call(ch); beforeRemove.call(ch);
} }
@@ -200,7 +200,7 @@ class VList {
beforeRemove() { beforeRemove() {
const children = this.children; const children = this.children;
const l = children.length; const l = children.length;
if (l) { if (l !== 0) {
const beforeRemove = children[0].beforeRemove; const beforeRemove = children[0].beforeRemove;
for (let i = 0; i < l; i++) { for (let i = 0; i < l; i++) {
beforeRemove.call(children[i]); beforeRemove.call(children[i]);
@@ -215,7 +215,7 @@ class VList {
} else { } else {
const children = this.children; const children = this.children;
const l = children.length; const l = children.length;
if (l) { if (l !== 0) {
const remove = children[0].remove; const remove = children[0].remove;
for (let i = 0; i < l; i++) { for (let i = 0; i < l; i++) {
remove.call(children[i]); remove.call(children[i]);
@@ -240,7 +240,7 @@ export function list(children: VNode[]): VNode<VList> {
} }
function createMapping(ch1: any[], startIdx1: number, endIdx2: number): { [key: string]: any } { function createMapping(ch1: any[], startIdx1: number, endIdx2: number): { [key: string]: any } {
let mapping: any = {}; const mapping: any = {};
for (let i = startIdx1; i <= endIdx2; i++) { for (let i = startIdx1; i <= endIdx2; i++) {
mapping[ch1[i].key] = i; mapping[ch1[i].key] = i;
} }
+10 -10
View File
@@ -26,7 +26,7 @@ export class VMulti {
const anchors = new Array(l); const anchors = new Array(l);
for (let i = 0; i < l; i++) { for (let i = 0; i < l; i++) {
let child = children[i]; let child = children[i];
if (child) { if (child !== undefined) {
child.mount(parent, afterNode); child.mount(parent, afterNode);
} else { } else {
const childAnchor = document.createTextNode(""); const childAnchor = document.createTextNode("");
@@ -44,7 +44,7 @@ export class VMulti {
const anchors = this.anchors; const anchors = this.anchors;
for (let i = 0, l = children.length; i < l; i++) { for (let i = 0, l = children.length; i < l; i++) {
let child = children[i]; let child = children[i];
if (child) { if (child !== undefined) {
child.moveBeforeDOMNode(node, parent); child.moveBeforeDOMNode(node, parent);
} else { } else {
const anchor = anchors![i]; const anchor = anchors![i];
@@ -56,14 +56,14 @@ export class VMulti {
moveBeforeVNode(other: VMulti | null, afterNode: Node | null) { moveBeforeVNode(other: VMulti | null, afterNode: Node | null) {
if (other) { if (other) {
const next = other!.children[0]; const next = other!.children[0];
afterNode = (next ? next.firstNode() : other!.anchors![0]) || null; afterNode = (next !== undefined ? next.firstNode() : other!.anchors![0]) || null;
} }
const children = this.children; const children = this.children;
const parent = this.parentEl; const parent = this.parentEl;
const anchors = this.anchors; const anchors = this.anchors;
for (let i = 0, l = children.length; i < l; i++) { for (let i = 0, l = children.length; i < l; i++) {
let child = children[i]; let child = children[i];
if (child) { if (child !== undefined) {
child.moveBeforeVNode(null, afterNode); child.moveBeforeVNode(null, afterNode);
} else { } else {
const anchor = anchors![i]; const anchor = anchors![i];
@@ -83,8 +83,8 @@ export class VMulti {
for (let i = 0, l = children1.length; i < l; i++) { for (let i = 0, l = children1.length; i < l; i++) {
const vn1 = children1[i]; const vn1 = children1[i];
const vn2 = children2[i]; const vn2 = children2[i];
if (vn1) { if (vn1 !== undefined) {
if (vn2) { if (vn2 !== undefined) {
vn1.patch(vn2, withBeforeRemove); vn1.patch(vn2, withBeforeRemove);
} else { } else {
const afterNode = vn1.firstNode()!; const afterNode = vn1.firstNode()!;
@@ -97,7 +97,7 @@ export class VMulti {
vn1.remove(); vn1.remove();
children1[i] = undefined; children1[i] = undefined;
} }
} else if (vn2) { } else if (vn2 !== undefined) {
children1[i] = vn2; children1[i] = vn2;
const anchor = anchors[i]; const anchor = anchors[i];
vn2.mount(parentEl, anchor); vn2.mount(parentEl, anchor);
@@ -110,7 +110,7 @@ export class VMulti {
const children = this.children; const children = this.children;
for (let i = 0, l = children.length; i < l; i++) { for (let i = 0, l = children.length; i < l; i++) {
const child = children[i]; const child = children[i];
if (child) { if (child !== undefined) {
child.beforeRemove(); child.beforeRemove();
} }
} }
@@ -125,7 +125,7 @@ export class VMulti {
const anchors = this.anchors; const anchors = this.anchors;
for (let i = 0, l = children.length; i < l; i++) { for (let i = 0, l = children.length; i < l; i++) {
const child = children[i]; const child = children[i];
if (child) { if (child !== undefined) {
child.remove(); child.remove();
} else { } else {
nodeRemoveChild.call(parentEl, anchors![i]); nodeRemoveChild.call(parentEl, anchors![i]);
@@ -136,7 +136,7 @@ export class VMulti {
firstNode(): Node | undefined { firstNode(): Node | undefined {
const child = this.children[0]; const child = this.children[0];
return child ? child.firstNode() : this.anchors![0]; return child !== undefined ? child.firstNode() : this.anchors![0];
} }
toString(): string { toString(): string {
+1 -1
View File
@@ -30,7 +30,7 @@ function callSlot(
const slots = ctx.props.slots || {}; const slots = ctx.props.slots || {};
const { __render, __ctx, __scope } = slots[name] || {}; const { __render, __ctx, __scope } = slots[name] || {};
const slotScope = ObjectCreate(__ctx || {}); const slotScope = ObjectCreate(__ctx || {});
if (__scope) { if (__scope !== undefined) {
slotScope[__scope] = extra; slotScope[__scope] = extra;
} }
const slotBDom = __render ? __render(slotScope, parent, key) : null; const slotBDom = __render ? __render(slotScope, parent, key) : null;
+1 -1
View File
@@ -11,7 +11,7 @@ export type Callback = () => void;
export function batched(callback: Callback): Callback { export function batched(callback: Callback): Callback {
let scheduled = false; let scheduled = false;
return async (...args) => { return async (...args) => {
if (!scheduled) { if (scheduled === false) {
scheduled = true; scheduled = true;
await Promise.resolve(); await Promise.resolve();
scheduled = false; scheduled = false;