!144 fix(no-vdom): optimize dom.ts file

* fix(no-vdom): optimize dom.ts file
* Merge branch 'reactive' of https://gitee.com/openInula/inula into reactive
* fix(no-vdom): modify $$style
* fix(no-vdom): modify test name
* Merge branch 'reactive' of https://gitee.com/openInula/inula into reactive
* fix(no-vdom): modify event
* Merge branch 'reactive' of https://gitee.com/openInula/inula into reactive
* fix(no-vdom): modify test code's name rule
* Merge branch 'reactive' of https://gitee.com/openInula/inula into reactive
* fix(no-vdom): delete no-vnode module
* fix(no-vdom): update render function
* Merge branch 'reactive' of https://gitee.com/openInula/inula into reactive
* Merge branch 'reactive' of https://gitee.com/openInula/inula into reactive
* Merge remote-tracking branch 'origin/reactive' into reactive
* fix(no-vdom): add TS
* fix(no-vdom): change js 2 ts
This commit is contained in:
陈超涛 2024-02-18 07:06:57 +00:00
parent 521344f8ff
commit 843a64ebd9
6 changed files with 129 additions and 108 deletions

View File

@ -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();
}
const t = typeof value,
multi = marker !== undefined;
if (value === prevValue) {
return value;
}
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();
}
}
}

View File

@ -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(() => <Main />, document.getElementById("app"));
* render(() => <Main />, 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();

View File

@ -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', () => {
* </div>;
* };
*
* $$render(() => <CountingComponent />, document.getElementById("app"));
* render(() => <CountingComponent />, 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(() => <Main />, document.getElementById("app"));
* render(() => <Main />, 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(

View File

@ -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', () => {
* </div>
* );
* }
* $$render(() => <App />, container);
* render(() => <App />, container);
*/
// 编译后:
@ -104,7 +104,7 @@ describe('conditions', () => {
})();
}
$$render(() => $$runComponent(App, {}), container);
render(() => $$runComponent(App, {}), container);
expect(container.innerHTML).toBe('<div><h1>xxx</h1><p>7 is between 5 and 10</p></div>');
change(11);
@ -130,7 +130,7 @@ describe('conditions', () => {
* </div>
* );
* }
* $$render(() => <App />, container);
* render(() => <App />, container);
*/
// 编译后:
@ -169,7 +169,7 @@ describe('conditions', () => {
})();
}
$$render(() => $$runComponent(App, {}), container);
render(() => $$runComponent(App, {}), container);
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1></div>"');
change(11);
@ -203,7 +203,7 @@ describe('conditions', () => {
* </div>
* );
* }
* $$render(() => <App />, container);
* render(() => <App />, container);
*/
// 编译后:
@ -281,7 +281,7 @@ describe('conditions', () => {
})();
}
$$render(() => $$runComponent(App, {}), container);
render(() => $$runComponent(App, {}), container);
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1><p>7 is 7 or less</p></div>"');
change(11);
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1><p>11 is greater than 10</p></div>"');
@ -316,7 +316,7 @@ describe('conditions', () => {
* </div>
* );
* }
* $$render(() => <App />, container);
* render(() => <App />, container);
*/
// 编译后:
@ -387,7 +387,7 @@ describe('conditions', () => {
})();
}
$$render(() => $$runComponent(App, {}), container);
render(() => $$runComponent(App, {}), container);
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>parallel</h1><p>XXX</p><p>YYY</p><p>ZZZ</p></div>"');
// hide X, Y, Z randomly

View File

@ -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(() => <CountingComponent />, container);
* render(() => <CountingComponent />, container);
*/
// 编译后:
@ -97,7 +97,7 @@ describe('event', () => {
})(),
];
};
$$render(() => $$runComponent(Comp), container);
render(() => $$runComponent(Comp), container);
$$delegateEvents(['click']);
dispatchChangeEvent(document.getElementById('inline-fn-change'), 'change');

View File

@ -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 <div id="count">Count value is 0.</div>;
* };
*
* $$render(() => <CountingComponent />, container);
* render(() => <CountingComponent />, 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 <div id="count">Count value is {0}.</div>;
* };
*
* $$render(() => <CountingComponent />, container);
* render(() => <CountingComponent />, 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,7 +127,7 @@ describe('render', () => {
/**
*
* const CountValue = (props) => {
* return <div>Count value is {props.count} .</div>;
* return <div id="count">Count value is {props.count} .</div>;
* }
*
* const CountingComponent = () => {
@ -142,7 +142,7 @@ describe('render', () => {
* </div>;
* };
*
* $$render(() => <CountingComponent />, document.getElementById("app"));
* render(() => <CountingComponent />, 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(() => <CountingComponent />, document.getElementById("app"));
* render(() => <CountingComponent />, 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 <div style="color: red;">Count value is 0.</div>;
* };
*
* $$render(() => <CountingComponent />, container);
* render(() => <CountingComponent />, 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 <div style={`color: ${color};`}>Count value is 0.</div>;
* }
* $$render(() => <Comp />, container);
* render(() => <Comp />, 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');