Skip to content

Commit

Permalink
feat: queueEffect helper
Browse files Browse the repository at this point in the history
  • Loading branch information
aidenybai committed May 19, 2022
1 parent bf73896 commit 518c6e4
Show file tree
Hide file tree
Showing 8 changed files with 467 additions and 516 deletions.
32 changes: 16 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,36 +104,36 @@
"*": "zx scripts/check.mjs"
},
"devDependencies": {
"@babel/core": "^7.17.10",
"@babel/plugin-transform-react-constant-elements": "^7.17.6",
"@babel/plugin-transform-react-jsx": "^7.17.3",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@commitlint/cli": "^16.2.4",
"@commitlint/config-conventional": "^16.2.4",
"@babel/core": "^7.18.0",
"@babel/plugin-transform-react-constant-elements": "^7.17.12",
"@babel/plugin-transform-react-jsx": "^7.17.12",
"@babel/preset-react": "^7.17.12",
"@babel/preset-typescript": "^7.17.12",
"@commitlint/cli": "^17.0.0",
"@commitlint/config-conventional": "^17.0.0",
"@picocss/pico": "^1.5.0",
"@types/babel__core": "^7.1.19",
"@types/benchmark": "^2.1.1",
"@types/canvas-confetti": "^1.4.2",
"@types/lodash": "^4.14.182",
"@types/node": "^17.0.31",
"@types/node": "^17.0.35",
"@types/virtual-dom": "^2.1.1",
"@typescript-eslint/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^5.22.0",
"@typescript-eslint/eslint-plugin": "^5.25.0",
"@typescript-eslint/parser": "^5.25.0",
"@vitejs/plugin-legacy": "^1.8.2",
"@vitest/ui": "^0.12.0",
"@vitest/ui": "^0.12.6",
"benchmark": "^2.1.4",
"c8": "^7.11.2",
"c8": "^7.11.3",
"canvas-confetti": "^1.5.1",
"chart.js": "^3.7.1",
"esbuild": "^0.14.38",
"esbuild": "^0.14.39",
"eslint": "^8.15.0",
"eslint-config-prettier": "^8.5.0",
"export-size": "^0.5.2",
"fsxx": "^0.0.5",
"htm": "^3.1.1",
"hundred": "^0.1.0",
"husky": "^8.0.0",
"husky": "^8.0.1",
"jsdom": "^19.0.0",
"lint-staged": "^12.4.1",
"lodash": "^4.17.21",
Expand All @@ -150,8 +150,8 @@
"typescript": "^4.6.4",
"unbuild": "^0.7.4",
"virtual-dom": "^2.1.1",
"vite": "^2.9.8",
"vitest": "^0.12.0",
"vite": "^2.9.9",
"vitest": "^0.12.6",
"zx": "^6.1.0"
}
}
125 changes: 40 additions & 85 deletions packages/million/drivers/useChildren.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createElement } from '../createElement';
import { createEffectQueuer } from '../effect';
import {
Commit,
Delta,
Expand Down Expand Up @@ -26,6 +27,7 @@ export const useChildren =
effects: Effect[] = [],
driver?: Driver,
): ReturnType<Driver> => {
const queueEffect = createEffectQueuer(el, effects);
const getData = (element: DOMNode): ReturnType<Driver> => ({
el: element,
newVNode,
Expand Down Expand Up @@ -61,12 +63,9 @@ export const useChildren =
const child = el.childNodes.item(deltaPosition) as DOMNode;

if (deltaType === DeltaTypes.CREATE) {
effects.push({
el,
type: EffectTypes.CREATE,
flush: () =>
el.insertBefore(createElement(newVNodeChildren![deltaPosition], false), child),
});
queueEffect(EffectTypes.CREATE, () =>
el.insertBefore(createElement(newVNodeChildren![deltaPosition], false), child),
);
}

if (deltaType === DeltaTypes.UPDATE) {
Expand All @@ -80,11 +79,7 @@ export const useChildren =
}

if (deltaType === DeltaTypes.REMOVE) {
effects.push({
el,
type: EffectTypes.REMOVE,
flush: () => el.removeChild(child),
});
queueEffect(EffectTypes.REMOVE, () => el.removeChild(child));
}
}
return finish(el);
Expand All @@ -96,21 +91,15 @@ export const useChildren =
if (!newVNodeChildren || newVNode.flag === Flags.ELEMENT_NO_CHILDREN) {
if (!oldVNodeChildren) return finish(el);

effects.push({
el,
type: EffectTypes.REMOVE,
flush: () => (el.textContent = ''),
});
queueEffect(EffectTypes.REMOVE, () => (el.textContent = ''));
return finish(el);
}

if (!oldVNodeChildren || oldVNodeChildren?.length === 0) {
for (let i = 0; i < newVNodeChildren.length; ++i) {
effects.push({
el,
type: EffectTypes.CREATE,
flush: () => el.appendChild(createElement(newVNodeChildren[i], false)),
});
queueEffect(EffectTypes.CREATE, () =>
el.appendChild(createElement(newVNodeChildren[i], false)),
);
}
return finish(el);
}
Expand Down Expand Up @@ -295,49 +284,38 @@ export const useChildren =
// [4] Right move
const node = el.childNodes.item(oldHead++);
const tail = newTail--;
effects.push({
el,
type: EffectTypes.CREATE,
flush: () => el.insertBefore(node, el.childNodes.item(tail).nextSibling),
});
queueEffect(EffectTypes.CREATE, () =>
el.insertBefore(node, el.childNodes.item(tail).nextSibling),
);
} else if (oldTailVNode.key === newHeadVNode.key) {
// [5] Left move
const node = el.childNodes.item(oldTail--);
const head = newHead++;
effects.push({
el,
type: EffectTypes.CREATE,
flush: () => el.insertBefore(node, el.childNodes.item(head)),
});
queueEffect(EffectTypes.CREATE, () => el.insertBefore(node, el.childNodes.item(head)));
} else break;
}

if (oldHead > oldTail) {
// [6] Old children optimization
while (newHead <= newTail) {
const head = newHead++;
effects.push({
el,
type: EffectTypes.CREATE,
flush: () =>
el.insertBefore(
el[NODE_OBJECT_POOL_FIELD].get((newVNodeChildren[head] as VElement).key!) ??
createElement(newVNodeChildren[head], false),
el.childNodes.item(head),
),
});
const cachedNode = el[NODE_OBJECT_POOL_FIELD].get(
(newVNodeChildren[head] as VElement).key,
);
queueEffect(EffectTypes.CREATE, () =>
el.insertBefore(
cachedNode ?? createElement(newVNodeChildren[head], false),
el.childNodes.item(head),
),
);
}
} else if (newHead > newTail) {
// [7] New children optimization
while (oldHead <= oldTail) {
const head = oldHead++;
const node = el.childNodes.item(head);
el[NODE_OBJECT_POOL_FIELD].set((oldVNodeChildren[head] as VElement).key!, node);
effects.push({
el,
type: EffectTypes.REMOVE,
flush: () => el.removeChild(node),
});
queueEffect(EffectTypes.REMOVE, () => el.removeChild(node));
}
} else {
// [8] Indexing old children
Expand All @@ -350,41 +328,30 @@ export const useChildren =
while (newHead <= newTail) {
const head = newHead++;
const newVNodeChild = newVNodeChildren[head] as VElement;
const oldVNodePosition = oldKeyMap.get(newVNodeChild.key!);
const oldIndex = oldKeyMap.get(newVNodeChild.key!);

if (oldVNodePosition !== undefined) {
if (oldIndex !== undefined) {
// [9] Reordering continuous nodes
const node = el.childNodes.item(oldVNodePosition);
effects.push({
el,
type: EffectTypes.CREATE,
flush: () => el.insertBefore(node, el.childNodes.item(head)),
});
const node = el.childNodes.item(oldIndex);
queueEffect(EffectTypes.CREATE, () => el.insertBefore(node, el.childNodes.item(head)));
oldKeyMap.delete(newVNodeChild.key!);
} else {
// [10] Create new nodes
effects.push({
el,
type: EffectTypes.CREATE,
flush: () =>
el.insertBefore(
el[NODE_OBJECT_POOL_FIELD].get(newVNodeChild.key) ??
createElement(newVNodeChild, false),
el.childNodes.item(head),
),
});
const cachedNode = el[NODE_OBJECT_POOL_FIELD].get(newVNodeChild.key);
queueEffect(EffectTypes.CREATE, () =>
el.insertBefore(
cachedNode ?? createElement(newVNodeChild, false),
el.childNodes.item(head),
),
);
}
}

// [11] Clean up removed nodes
for (const [oldVNodeKey, oldVNodeValue] of oldKeyMap) {
const node = el.childNodes.item(oldVNodeValue);
el[NODE_OBJECT_POOL_FIELD].set(oldVNodeKey, node);
effects.push({
el,
type: EffectTypes.REMOVE,
flush: () => el.removeChild(node),
});
queueEffect(EffectTypes.REMOVE, () => el.removeChild(node));
}
}

Expand All @@ -399,11 +366,7 @@ export const useChildren =
? newVNode?.children.join('')
: newVNode?.children;
if (oldString !== newString) {
effects.push({
el,
type: EffectTypes.REPLACE,
flush: () => (el.textContent = newString!),
});
queueEffect(EffectTypes.REPLACE, () => (el.textContent = newString!));
}
return finish(el);
}
Expand All @@ -427,25 +390,17 @@ export const useChildren =
if (newVNodeChildren.length > oldVNodeChildren.length) {
for (let i = commonLength; i < newVNodeChildren.length; ++i) {
const node = createElement(newVNodeChildren[i], false);
effects.push({ el, type: EffectTypes.CREATE, flush: () => el.appendChild(node) });
queueEffect(EffectTypes.CREATE, () => el.appendChild(node));
}
} else if (newVNodeChildren.length < oldVNodeChildren.length) {
for (let i = oldVNodeChildren.length - 1; i >= commonLength; --i) {
effects.push({
el,
type: EffectTypes.REMOVE,
flush: () => el.removeChild(el.childNodes.item(i)),
});
queueEffect(EffectTypes.REMOVE, () => el.removeChild(el.childNodes.item(i)));
}
}
} else if (newVNodeChildren) {
for (let i = 0; i < newVNodeChildren.length; ++i) {
const node = createElement(newVNodeChildren[i], false);
effects.push({
el,
type: EffectTypes.CREATE,
flush: () => el.appendChild(node),
});
queueEffect(EffectTypes.CREATE, () => el.appendChild(node));
}
}

Expand Down
20 changes: 5 additions & 15 deletions packages/million/drivers/useNode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { fromDomNodeToVNode } from '../../shared/convert';
import { createElement } from '../createElement';
import { createEffectQueuer } from '../effect';
import { resolveVNode } from '../m';
import {
Commit,
Expand Down Expand Up @@ -28,6 +29,7 @@ export const useNode = (drivers: any[]): any => {
const resolvedOldVNode: VNode =
resolveVNode(oldVNode) ?? el[OLD_VNODE_FIELD] ?? fromDomNodeToVNode(el);
const resolvedNewVNode: VNode = resolveVNode(newVNode)!;
const queueEffect = createEffectQueuer(el, effects);

const finish = (element: DOMNode): ReturnType<Driver> => {
return {
Expand All @@ -39,23 +41,15 @@ export const useNode = (drivers: any[]): any => {
};

if (resolvedNewVNode === undefined || resolvedNewVNode === null) {
effects.push({
el,
type: EffectTypes.REMOVE,
flush: () => el.remove(),
});
queueEffect(EffectTypes.REMOVE, () => el.remove());
return finish(el);
} else {
const hasString =
typeof resolvedOldVNode === 'string' || typeof resolvedNewVNode === 'string';

if (hasString && resolvedOldVNode !== resolvedNewVNode) {
const newEl = createElement(resolvedNewVNode, false);
effects.push({
el,
type: EffectTypes.REPLACE,
flush: () => el.replaceWith(newEl),
});
queueEffect(EffectTypes.REPLACE, () => el.replaceWith(newEl));
return finish(newEl);
}
if (
Expand Down Expand Up @@ -87,11 +81,7 @@ export const useNode = (drivers: any[]): any => {
) {
if (resolvedOldVNode.tag !== resolvedNewVNode.tag) {
const newEl = createElement(resolvedNewVNode, false);
effects.push({
el,
type: EffectTypes.REPLACE,
flush: () => el.replaceWith(newEl),
});
queueEffect(EffectTypes.REPLACE, () => el.replaceWith(newEl));
return finish(newEl);
}

Expand Down

0 comments on commit 518c6e4

Please sign in to comment.