diff --git a/packages/inula-novdom/src/dom.ts b/packages/inula-novdom/src/dom.ts
index ff11474e332698d360049243cb33e8388a17e8f4..5a955cae05298c9361df425d6c53655edf4f8b77 100644
--- a/packages/inula-novdom/src/dom.ts
+++ b/packages/inula-novdom/src/dom.ts
@@ -37,118 +37,139 @@ export function template(html: string): () => Node {
};
}
-export function insert(parent: Node, maybeSignal: any, marker?: Node, initial?: any[]): any {
- if (marker !== undefined && !initial) {
+export function insert(parent: Node, maybeSignal: any, marker?: Node) {
+ let initial: any;
+ if (marker !== undefined) {
initial = [];
}
if (isReactiveObj(maybeSignal)) {
- watchRender((current: any) => {
- return insertExpression(parent, maybeSignal.get(), current, marker);
+ watchRender((prevValue: any) => {
+ return insertExpression(parent, maybeSignal.get(), prevValue, marker);
}, initial);
} else {
- return insertExpression(parent, maybeSignal, initial, marker);
+ insertExpression(parent, maybeSignal, initial, marker);
}
}
-function watchRender(fn: (value: any) => any, prevValue: any): void {
- let nextValue = prevValue;
+function watchRender(fn: (value: any) => any, initial?: any): void {
+ let prevValue = initial;
watch(() => {
- nextValue = fn(nextValue);
+ prevValue = fn(prevValue);
});
}
-function insertExpression(parent, value, current, marker, unwrapArray) {
- while (typeof current === 'function') current = current();
- if (value === current) return value;
+function insertExpression(parent: Node, value: any, prevValue: any, marker?: Node): any {
+ let result: any;
+ while (typeof prevValue === 'function') {
+ prevValue = prevValue();
+ }
+
+ if (value === prevValue) {
+ return value;
+ }
- const t = typeof value,
- multi = marker !== undefined;
+ const t: string = typeof value;
+ const multi: boolean = marker !== undefined;
if (t === 'string' || t === 'number') {
if (t === 'number') value = value.toString();
if (multi) {
- let node = current[0];
+ let node: Node | Text = prevValue[0];
if (node && node.nodeType === 3) {
- node.data = value;
+ (node as Text).data = value;
} else {
node = document.createTextNode(value);
}
- current = cleanChildren(parent, current, marker, node);
+ result = cleanChildren(parent, prevValue, marker, node);
} else {
- if (current !== '' && typeof current === 'string') {
- current = parent.firstChild.data = value;
- } else current = parent.textContent = value;
+ if (prevValue !== '' && typeof prevValue === 'string') {
+ result = (parent.firstChild as Text).data = value;
+ } else {
+ result = parent.textContent = value;
+ }
}
} else if (value == null || t === 'boolean') {
- current = cleanChildren(parent, current, marker);
+ result = cleanChildren(parent, prevValue, marker);
} else if (t === 'function') {
// 在watch里面执行
- watch(() => {
+ watchRender((prev) => {
let v = value();
while (isReactiveObj(v)) {
v = v.get();
}
+ result = insertExpression(parent, v, prev, marker);
+ return result;
+ }, prevValue);
- current = insertExpression(parent, v, current, marker);
- });
- return () => current;
+ return () => result;
} else if (Array.isArray(value)) {
- // return [() => {}, () => {}, ...]
- const array = [];
- const currentArray = current && Array.isArray(current);
- if (normalizeIncomingArray(array, value, current, unwrapArray)) {
- watchRender(() => (current = insertExpression(parent, array, current, marker, true)));
- return () => current;
+ // value:[() => {}, () => {}, ...]
+ const array: any[] = [];
+ const isPrevArray: boolean = prevValue && Array.isArray(prevValue);
+ if (flattenArray(array, value)) {
+ watchRender((prev) => {
+ result = insertExpression(parent, array, prev, marker);
+ return result;
+ }, prevValue);
+
+ return () => result;
}
- if (array.length === 0) {
- // 当前没有节点
- current = cleanChildren(parent, current, marker);
- if (multi) return current;
- } else if (currentArray) {
- if (current.length === 0) {
+ if (array.length === 0) { // 当前没有节点
+ result = cleanChildren(parent, prevValue, marker);
+ if (multi) {
+ return result;
+ }
+ } else if (isPrevArray) {
+ if (prevValue.length === 0) {
appendNodes(parent, array, marker); // 原来没有节点
} else {
- reconcileArrays(parent, current, array); // 原本有节点,现在也有节点
+ reconcileArrays(parent, prevValue, array); // 原本有节点,现在也有节点
}
} else {
- current && cleanChildren(parent);
+ if (prevValue) {
+ parent.textContent = ''; // 原来有节点,但不是数组
+ }
+
appendNodes(parent, array);
}
- current = array;
- } else if (value.nodeType) {
- if (Array.isArray(current)) {
- if (multi) return (current = cleanChildren(parent, current, marker, value));
- cleanChildren(parent, current, null, value);
- } else if (current == null || current === '' || !parent.firstChild) {
+ result = array;
+ } else if (value.nodeType) { // 是Node节点
+ if (Array.isArray(prevValue)) {
+ if (multi) {
+ return cleanChildren(parent, prevValue, marker, value);
+ } else {
+ cleanChildren(parent, prevValue, null, value);
+ }
+ } else if (prevValue == null || prevValue === '' || !parent.firstChild) {
parent.appendChild(value);
} else {
parent.replaceChild(value, parent.firstChild);
}
- current = value;
+ result = value;
}
- return current;
+ return result;
}
-function cleanChildren(parent: Node, current: Node[], marker?: Node, replacement?: Node): Node[] {
+function cleanChildren(parent: Node, prevNodes: Node[], marker?: Node, replacement?: Node): Node[] {
if (marker === undefined) {
parent.textContent = '';
return [];
}
const node = replacement || document.createTextNode('');
- if (current.length) {
+ if (prevNodes.length) {
let inserted = false;
- for (let i = current.length - 1; i >= 0; i--) {
- const el = current[i];
+ for (let i = prevNodes.length - 1; i >= 0; i--) {
+ const el = prevNodes[i];
if (node !== el) {
const isParent = el.parentNode === parent;
if (!inserted && !i) {
isParent ? parent.replaceChild(node, el) : parent.insertBefore(node, marker);
} else {
- isParent && el.remove();
+ isParent && (el as ChildNode).remove();
}
} else {
inserted = true;
@@ -168,7 +189,7 @@ function appendNodes(parent: Node, array: Node[], marker: Node | null = null): v
}
// 拆解数组,如:[[a, b], [c, d], ...] to [a, b, c, d]
-function normalizeIncomingArray(normalized: Node[], array: any[]): boolean {
+function flattenArray(normalized: Node[], array: any[]): boolean {
let dynamic = false;
for (let i = 0, len = array.length; i < len; i++) {
const item = array[i];
@@ -177,7 +198,7 @@ function normalizeIncomingArray(normalized: Node[], array: any[]): boolean {
// matches null, undefined, true or false
// skip
} else if (Array.isArray(item)) {
- dynamic = normalizeIncomingArray(normalized, item) || dynamic;
+ dynamic = flattenArray(normalized, item) || dynamic;
} else if ((t = typeof item) === 'string' || t === 'number') {
normalized.push(document.createTextNode(item));
} else if (t === 'function') {
@@ -225,7 +246,7 @@ export default function reconcileArrays(parentNode: Node, oldChildren: Node[], n
// 新节点全部和新节点相同(不是完全相同, 如:旧 abefcd 新 abcd)
while (oStart < oEnd) {
if (!map || !map.has(oldChildren[oStart])) {
- oldChildren[oStart].remove();
+ (oldChildren[oStart] as ChildNode).remove();
}
oStart++;
}
@@ -276,7 +297,7 @@ export default function reconcileArrays(parentNode: Node, oldChildren: Node[], n
oStart++;
}
} else {
- oldChildren[oStart++].remove();
+ (oldChildren[oStart++] as ChildNode).remove();
}
}
}
diff --git a/packages/inula-novdom/tests/For.bench.tsx b/packages/inula-novdom/tests/For.bench.tsx
index 3771a5593d24259711dfab28f7b17f839a80be56..db2c4a3a346b56f1caa104ab5ee8b4445cd8c8ca 100644
--- a/packages/inula-novdom/tests/For.bench.tsx
+++ b/packages/inula-novdom/tests/For.bench.tsx
@@ -20,7 +20,7 @@ import {
setAttribute as $$attr,
effect as $$effect,
} from '../src/dom';
-import { runComponent as $$runComponent, render as $$render } from '../src/core';
+import { runComponent as $$runComponent, render } from '../src/core';
import { delegateEvents as $$delegateEvents, addEventListener as $$on } from '../src/event';
import { For } from '../src/components/For';
@@ -100,7 +100,7 @@ bench('For', () => {
* );
* };
*
- * $$render(() => , document.getElementById("app"));
+ * render(() => , document.getElementById("app"));
*/
// 编译后:
@@ -229,7 +229,7 @@ bench('For', () => {
return _el$5;
})();
};
- $$render(() => $$runComponent(Main, {}), container);
+ render(() => $$runComponent(Main, {}), container);
$$delegateEvents(['click']);
container.querySelector('#run').click();
diff --git a/packages/inula-novdom/tests/For.test.tsx b/packages/inula-novdom/tests/For.test.tsx
index 46c4b94fb99dbf637954183ec7c0ee4fc10d0b91..532a39a93da89f41f7313a19f0d141957aa1a94c 100644
--- a/packages/inula-novdom/tests/For.test.tsx
+++ b/packages/inula-novdom/tests/For.test.tsx
@@ -22,7 +22,7 @@ import {
setAttribute as $$attr,
effect as $$effect,
} from '../src/dom';
-import { runComponent as $$runComponent, render as $$render } from '../src/core';
+import { runComponent as $$runComponent, render } from '../src/core';
import { delegateEvents as $$delegateEvents, addEventListener as $$on } from '../src/event';
import { describe, expect } from 'vitest';
import { domTest as it } from './utils';
@@ -73,7 +73,7 @@ describe('For', () => {
* ;
* };
*
- * $$render(() => , document.getElementById("app"));
+ * render(() => , document.getElementById("app"));
*/
// 编译后:
@@ -158,7 +158,7 @@ describe('For', () => {
return _el$5;
})();
};
- $$render(() => $$runComponent(CountingComponent, {}), container);
+ render(() => $$runComponent(CountingComponent, {}), container);
$$delegateEvents(['click']);
expect(container.querySelector('#todos').innerHTML).toEqual(
@@ -251,7 +251,7 @@ describe('For', () => {
* );
* };
*
- * $$render(() => , document.getElementById("app"));
+ * render(() => , document.getElementById("app"));
*/
// 编译后:
@@ -380,7 +380,7 @@ describe('For', () => {
return _el$5;
})();
};
- $$render(() => $$runComponent(Main, {}), container);
+ render(() => $$runComponent(Main, {}), container);
$$delegateEvents(['click']);
expect(container.querySelector('#tbody').innerHTML).toEqual(
diff --git a/packages/inula-novdom/tests/conditions.test.tsx b/packages/inula-novdom/tests/conditions.test.tsx
index d4b1753c9486f60537556541a9f031c096b6c905..01f85abc147ef110ccbedbff970fb8e75ddff968 100644
--- a/packages/inula-novdom/tests/conditions.test.tsx
+++ b/packages/inula-novdom/tests/conditions.test.tsx
@@ -19,7 +19,7 @@ import { describe, expect } from 'vitest';
import { domTest as it } from './utils';
import { template as $$template, insert as $$insert } from '../src/dom';
import { Cond } from '../src/components/Cond';
-import { runComponent as $$runComponent, render as $$render } from '../src/core';
+import { runComponent as $$runComponent, render } from '../src/core';
import { reactive } from 'inula-reactive';
describe('conditions', () => {
@@ -45,7 +45,7 @@ describe('conditions', () => {
*
* );
* }
- * $$render(() => , container);
+ * render(() => , container);
*/
// 编译后:
@@ -104,7 +104,7 @@ describe('conditions', () => {
})();
}
- $$render(() => $$runComponent(App, {}), container);
+ render(() => $$runComponent(App, {}), container);
expect(container.innerHTML).toBe('
');
change(11);
@@ -130,7 +130,7 @@ describe('conditions', () => {
*
* );
* }
- * $$render(() => , container);
+ * render(() => , container);
*/
// 编译后:
@@ -169,7 +169,7 @@ describe('conditions', () => {
})();
}
- $$render(() => $$runComponent(App, {}), container);
+ render(() => $$runComponent(App, {}), container);
expect(container.innerHTML).toMatchInlineSnapshot('"xxx
"');
change(11);
@@ -203,7 +203,7 @@ describe('conditions', () => {
*
* );
* }
- * $$render(() => , container);
+ * render(() => , container);
*/
// 编译后:
@@ -281,7 +281,7 @@ describe('conditions', () => {
})();
}
- $$render(() => $$runComponent(App, {}), container);
+ render(() => $$runComponent(App, {}), container);
expect(container.innerHTML).toMatchInlineSnapshot('""');
change(11);
expect(container.innerHTML).toMatchInlineSnapshot('""');
@@ -316,7 +316,7 @@ describe('conditions', () => {
*
* );
* }
- * $$render(() => , container);
+ * render(() => , container);
*/
// 编译后:
@@ -387,7 +387,7 @@ describe('conditions', () => {
})();
}
- $$render(() => $$runComponent(App, {}), container);
+ render(() => $$runComponent(App, {}), container);
expect(container.innerHTML).toMatchInlineSnapshot('""');
// hide X, Y, Z randomly
diff --git a/packages/inula-novdom/tests/event.test.tsx b/packages/inula-novdom/tests/event.test.tsx
index f1ce7b7a727c458371855d39170d6c694115dea9..9b4900a1c13ce719cc64346c23da814fb93f957c 100644
--- a/packages/inula-novdom/tests/event.test.tsx
+++ b/packages/inula-novdom/tests/event.test.tsx
@@ -18,7 +18,7 @@
import { describe, expect, vi } from 'vitest';
import { domTest as it } from './utils';
import { template as $$template } from '../src/dom';
-import { runComponent as $$runComponent, render as $$render} from '../src/core';
+import { runComponent as $$runComponent, render} from '../src/core';
import { delegateEvents as $$delegateEvents, addEventListener as $$on } from '../src/event';
function dispatchMouseEvent(element: HTMLElement, eventType = 'click') {
@@ -50,7 +50,7 @@ describe('event', () => {
* >;
* };
*
- * $$render(() => , container);
+ * render(() => , container);
*/
// 编译后:
@@ -97,7 +97,7 @@ describe('event', () => {
})(),
];
};
- $$render(() => $$runComponent(Comp), container);
+ render(() => $$runComponent(Comp), container);
$$delegateEvents(['click']);
dispatchChangeEvent(document.getElementById('inline-fn-change'), 'change');
diff --git a/packages/inula-novdom/tests/render.test.tsx b/packages/inula-novdom/tests/render.test.tsx
index a45eb5282e3b16e4ae299a7c69a717c49890f4bf..89509b807e9fef5ba0ecfb9bb0f8d0fb400c688e 100644
--- a/packages/inula-novdom/tests/render.test.tsx
+++ b/packages/inula-novdom/tests/render.test.tsx
@@ -23,7 +23,7 @@ import {
className as $$className,
setAttribute as $$attr,
} from '../src/dom';
-import { runComponent as $$runComponent, render as $$render } from '../src/core';
+import { runComponent as $$runComponent, render } from '../src/core';
import { delegateEvents as $$delegateEvents, addEventListener as $$on } from '../src/event';
import { describe, expect } from 'vitest';
import { domTest as it } from './utils';
@@ -36,7 +36,7 @@ describe('render', () => {
* return Count value is 0.
;
* };
*
- * $$render(() => , container);
+ * render(() => , container);
*/
// 编译后:
@@ -44,7 +44,7 @@ describe('render', () => {
const CountingComponent = () => {
return $tmpl();
};
- $$render(() => $$runComponent(CountingComponent, {}), container);
+ render(() => $$runComponent(CountingComponent, {}), container);
expect(container.querySelector('#count').innerHTML).toEqual('Count value is 0.');
});
@@ -56,7 +56,7 @@ describe('render', () => {
* return Count value is {0}.
;
* };
*
- * $$render(() => , container);
+ * render(() => , container);
*/
// 编译后:
@@ -70,7 +70,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(CountingComponent, {}), container);
+ render(() => $$runComponent(CountingComponent, {}), container);
expect(container.querySelector('#count').innerHTML).toEqual('Count value is 0.');
});
@@ -114,7 +114,7 @@ describe('render', () => {
})(),
];
};
- $$render(() => $$runComponent(CountingComponent, {}), container);
+ render(() => $$runComponent(CountingComponent, {}), container);
$$delegateEvents(['click']);
@@ -127,22 +127,22 @@ describe('render', () => {
/**
* 源码:
* const CountValue = (props) => {
- * return Count value is {props.count} .
;
+ * return Count value is {props.count} .
;
* }
*
* const CountingComponent = () => {
* const [count, setCount] = createSignal(0);
* const add = () => {
- * setCount((c) => c + 1);
- * }
+ * setCount((c) => c + 1);
+ * }
*
* return ;
+ * ;
* };
*
- * $$render(() => , document.getElementById("app"));
+ * render(() => , document.getElementById("app"));
*/
// 编译后:
@@ -177,7 +177,7 @@ describe('render', () => {
return _el$5;
})();
};
- $$render(() => $$runComponent(CountingComponent, {}), container);
+ render(() => $$runComponent(CountingComponent, {}), container);
$$delegateEvents(['click']);
container.querySelector('#btn').click();
@@ -285,7 +285,7 @@ describe('render', () => {
* );
* };
*
- * $$render(() => , document.getElementById("app"));
+ * render(() => , document.getElementById("app"));
*/
// 编译后:
@@ -329,7 +329,7 @@ describe('render', () => {
return _el$6;
})();
};
- $$render(() => $$runComponent(CountingComponent, {}), container);
+ render(() => $$runComponent(CountingComponent, {}), container);
$$delegateEvents(['click']);
expect(container.querySelector('h1').innerHTML).toMatchInlineSnapshot('"0"');
@@ -339,7 +339,7 @@ describe('render', () => {
it('should throw error when container is not valid', () => {
[undefined, null, 0, 1, true, false, 'string', Symbol('symbol'), {}].forEach(container => {
- expect(() => $$render(() => $$runComponent(() => null, undefined), container)).toThrowError(
+ expect(() => render(() => $$runComponent(() => null, undefined), container)).toThrowError(
'Render target is not valid.'
);
});
@@ -352,7 +352,7 @@ describe('render', () => {
* return Count value is 0.
;
* };
*
- * $$render(() => , container);
+ * render(() => , container);
*/
// 编译后:
@@ -360,7 +360,7 @@ describe('render', () => {
const Comp = () => {
return $tmpl();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').style.color).toEqual('red');
});
@@ -372,7 +372,7 @@ describe('render', () => {
* const color = 'red';
* return Count value is 0.
;
* }
- * $$render(() => , container);
+ * render(() => , container);
*
*/
// 编译后:
@@ -382,7 +382,7 @@ describe('render', () => {
return $tmpl();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').style.color).toEqual('red');
});
@@ -402,7 +402,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').style.color).toEqual('red');
});
@@ -425,7 +425,7 @@ describe('render', () => {
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').style.color).toEqual('red');
});
@@ -450,7 +450,7 @@ describe('render', () => {
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').style.color).toEqual('red');
container.querySelector('div').style.color = 'green';
expect(container.querySelector('div').style.color).toEqual('green');
@@ -475,7 +475,7 @@ describe('render', () => {
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').style.color).toEqual('red');
container.querySelector('div').style.color = 'green';
expect(container.querySelector('div').style.color).toEqual('green');
@@ -493,7 +493,7 @@ describe('render', () => {
const Comp = () => {
return $tmpl();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').className).toEqual('red');
});
@@ -515,7 +515,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').className).toEqual('red');
});
@@ -537,7 +537,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').className).toEqual('red');
});
@@ -557,7 +557,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').className).toEqual('red green');
});
@@ -579,7 +579,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').className).toEqual('red');
container.querySelector('div').className = 'green';
expect(container.querySelector('div').className).toEqual('green');
@@ -607,7 +607,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').className).toEqual('red');
});
@@ -629,7 +629,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').className).toEqual('red green');
});
@@ -651,7 +651,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').id).toEqual('test');
});
@@ -675,7 +675,7 @@ describe('render', () => {
return _el$;
})();
};
- $$render(() => $$runComponent(Comp, {}), container);
+ render(() => $$runComponent(Comp, {}), container);
expect(container.querySelector('div').id).toEqual('el');
id.set('test');
expect(container.querySelector('div').id).toEqual('test');