!135 fix(no-vdom): delete no-vnode module

* 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-05 09:40:56 +00:00
parent a2e27d163a
commit 05f11d2c35
7 changed files with 17 additions and 693 deletions

View File

@ -17,76 +17,20 @@ import {insert} from './dom';
type ComponentConstructor<T> = (props: T) => any;
type CodeFunction = () => any;
type InitFunction = () => void;
type Options = Record<string, any>;
type RootFunction = () => () => void;
type UpdateFunction = () => any;
export function createComponent<T>(Comp: ComponentConstructor<T>, props?: T): any {
return Comp(props || ({} as T));
export function createComponent<T>(Comp: ComponentConstructor<T>, props: T = {} as T): any {
return Comp(props);
}
export function render(code: CodeFunction, element: HTMLElement, init: InitFunction): () => void {
let disposer: () => void;
export function render(codeFn: CodeFunction, element: HTMLElement): () => void {
const disposer = (): void => {
// TODO
};
createRoot(dispose => {
disposer = dispose;
if (element === document) {
code();
} else {
insert(element, code(), element.firstChild ? null : undefined, init);
}
});
insert(element, codeFn(), element.firstChild ? null : undefined);
return () => {
disposer();
element.textContent = '';
};
}
let Owner: any;
let Listener: any;
function createRoot(fn: RootFunction): any {
const listener = Listener;
const owner = Owner;
const unowned = fn.length === 0;
const current = owner;
const root = {
owned: null,
cleanups: null,
context: current ? current.context : null,
owner: current,
};
const updateFn = () => {
fn(() => {
});
};
Owner = root;
Listener = null;
try {
return runUpdates(updateFn, true);
} finally {
Listener = listener;
Owner = owner;
}
}
let Updates: UpdateFunction[] | null;
let Effects: any[] | null;
let ExecCount = 0;
function runUpdates(fn: UpdateFunction, init: boolean): any {
if (Updates) return fn();
let wait = false;
if (!init) Updates = [];
if (Effects) {
wait = true;
} else {
Effects = [];
}
ExecCount++;
const res = fn();
return res;
}

View File

@ -13,20 +13,21 @@
* See the Mulan PSL v2 for more details.
*/
import { watch } from 'inula-reactive';
import { isReactiveObj, ReactiveObj } from 'inula-reactive';
import { isReactiveObj, ReactiveObj, watch } from 'inula-reactive';
/**
* Creates a function that returns a Node created from the provided HTML string.
* @param {string} html - The HTML string to be used for creating the Node.
* @returns {() => Node} A function that returns a Node created from the provided HTML string.
*/
export function template(html: string): () => Node {
let node: Node | null;
const create = (): Node => {
const t = document.createElement('template');
t.innerHTML = html;
return t.content.firstChild as Node;
};
const fn = (): Node => (node || (node = create())).cloneNode(true);
fn.cloneNode = fn;
return fn;
return (): Node => create();
}
export function insert(parent: Node, accessor: ReactiveObj<any> | any, marker?: Node, initial?: any[]): any {
@ -160,11 +161,11 @@ function appendNodes(parent: Node, array: Node[], marker: Node | null = null): v
}
// 拆解数组,如:[[a, b], [c, d], ...] to [a, b, c, d]
function normalizeIncomingArray(normalized, array, unwrap) {
function normalizeIncomingArray(normalized: Node[], array: any[], unwrap?: boolean): boolean {
let dynamic = false;
for (let i = 0, len = array.length; i < len; i++) {
let item = array[i],
t;
let item = array[i];
let t: string;
if (item == null || item === true || item === false) {
// matches null, undefined, true or false
// skip

View File

@ -1,125 +0,0 @@
/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import { JSXElement } from '../../renderer/Types';
import { computed } from '../../reactive/Computed';
import { untrack } from '../../reactive/RNode';
// TODO 需要优化为精细更新
export function For<T>(props: { each: any; children?: (value: any, index: number) => JSXElement }) {
let list = props.each,
mapFn = props.children,
items = [],
mapped = [],
disposers = [],
len = 0;
return () => {
let newItems = list.get() || [];
untrack(() => {
let i, j;
let newLen = newItems.length,
newIndices,
newIndicesNext,
temp,
start,
end,
newEnd,
item;
if (newLen === 0) {
// 没新数据
if (len !== 0) {
disposers = [];
items = [];
mapped = [];
len = 0;
}
} else if (len === 0) {
// 上一次没有数据
mapped = new Array(newLen);
for (j = 0; j < newLen; j++) {
items[j] = newItems[j];
mapped[j] = mapFn(list[j]);
}
len = newLen;
} else { // 都有数据
// temp = new Array(newLen);
//
// // 从前往后,判断相等。但是这种前度比较经常是不生效的,比如数组的值相同,指针不一样
// for (start = 0, end = Math.min(len, newLen); start < end && items[start] === newItems[start]; start++);
//
// // 从后往前
// for (
// end = len - 1, newEnd = newLen - 1;
// end >= start && newEnd >= start && items[end] === newItems[newEnd]; // 值相等
// end--, newEnd--
// ) {
// temp[newEnd] = mapped[end]; // 把dom取出来
// }
// // 从start -> newEnd就是不相等的
//
// newIndices = new Map();
// newIndicesNext = new Array(newEnd + 1);
// for (j = newEnd; j >= start; j--) {
// item = newItems[j];
// i = newIndices.get(item);
// newIndicesNext[j] = i === undefined ? -1 : i; // item数据可能指针相同重复。因为是倒序遍历所以i就是相同数据下一个位置。
// newIndices.set(item, j); // 新数据放到map中
// }
//
// // 遍历旧数据
// for (i = start; i <= end; i++) {
// item = items[i];
// j = newIndices.get(item); // j 是相同数据的第一个
// // 旧行数据在新数据中存在在j位置
// if (j !== undefined && j !== -1) {
// temp[j] = mapped[i]; // 把就dom放到新的j位置
// j = newIndicesNext[j];
// newIndices.set(item, j); // 修改map里面的位置改为下一个
// }
// }
//
// // 往mapped中放入start - newLen的dom数据
// for (j = start; j < newLen; j++) { // 按新数据来遍历
// if (j in temp) {
// mapped[j] = temp[j]; // 直接取旧的
// } else {
// mapped[j] = mapFn(list[j]); // 创建新的dom
// }
// }
//
// // 0 - start 数据没有变动
// mapped = mapped.slice(0, (len = newLen)); // 如果newLen小于len就截断
// items = newItems.slice(0);
// 假设新旧相同行数据已经更新
if (newLen > len) {
for (let i = len; i < newLen; i++) {
mapped[i] = mapFn(list[i]); // 创建新的dom
}
}
// 0 - start 数据没有变动
mapped = mapped.slice(0, (len = newLen)); // 如果newLen小于len就截断
items = newItems.slice(0);
}
});
return mapped;
};
}

View File

@ -1,25 +0,0 @@
/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import { JSXElement } from '../../renderer/Types';
import { Show as RShow } from '../../reactive/components/Show';
export function Show<T>(props: {
if: any | (() => T);
else?: any;
children: JSXElement | (() => JSXElement);
}): any {
return () => RShow(props);
}

View File

@ -1,91 +0,0 @@
/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import { insert } from './dom';
export function createComponent<T>(Comp, props) {
return Comp(props || ({} as T));
}
export function render(code, element, init, options = {}) {
let disposer;
createRoot(dispose => {
disposer = dispose;
if (element === document) {
code();
} else {
insert(element, code(), element.firstChild ? null : undefined, init);
}
});
return () => {
disposer();
element.textContent = '';
};
}
let Owner;
let Listener;
function createRoot(fn) {
const listener = Listener;
const owner = Owner;
const unowned = fn.length === 0;
const current = owner;
const root = {
owned: null,
cleanups: null,
context: current ? current.context : null,
owner: current,
};
const updateFn = () => {
// fn(() => cleanNode(root));
fn(() => {});
};
Owner = root;
Listener = null;
try {
return runUpdates(updateFn, true);
} finally {
Listener = listener;
Owner = owner;
}
}
let Updates, Effects;
let ExecCount = 0;
function runUpdates(fn, init) {
if (Updates) return fn();
let wait = false;
if (!init) Updates = [];
if (Effects) {
wait = true;
} else {
Effects = [];
}
ExecCount++;
// try {
const res = fn();
// completeUpdates(wait);
return res;
// } catch (err) {
// if (!wait) Effects = null;
// Updates = null;
// // handleError(err);
// }
}

View File

@ -1,295 +0,0 @@
/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import { watch } from '../reactive/Watch';
import { isReactiveObj } from '../reactive/Utils';
export function template(html) {
let node;
const create = () => {
const t = document.createElement('template');
t.innerHTML = html;
return t.content.firstChild;
};
const fn = () => (node || (node = create())).cloneNode(true);
fn.cloneNode = fn;
return fn;
}
export function insert(parent, accessor, marker, initial) {
if (marker !== undefined && !initial) {
initial = [];
}
if (isReactiveObj(accessor)) {
watchRender(current => {
return insertExpression(parent, accessor.get(), current, marker);
}, initial);
} else {
return insertExpression(parent, accessor, initial, marker);
}
}
function watchRender(fn, prevValue) {
let nextValue = prevValue;
watch(() => {
nextValue = fn(nextValue);
});
}
function insertExpression(parent, value, current, marker, unwrapArray) {
while (typeof current === 'function') current = current();
if (value === current) return value;
const t = typeof value,
multi = marker !== undefined;
if (t === 'string' || t === 'number') {
if (t === 'number') value = value.toString();
if (multi) {
let node = current[0];
if (node && node.nodeType === 3) {
node.data = value;
} else {
node = document.createTextNode(value);
}
current = cleanChildren(parent, current, marker, node);
} else {
if (current !== '' && typeof current === 'string') {
current = parent.firstChild.data = value;
} else current = parent.textContent = value;
}
} else if (value == null || t === 'boolean') {
current = cleanChildren(parent, current, marker);
} else if (t === 'function') {
// 在watch里面执行
watch(() => {
let v = value();
while (isReactiveObj(v)) {
v = v.get();
}
current = insertExpression(parent, v, current, marker);
});
return () => current;
} 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;
}
if (array.length === 0) {
// 当前没有节点
current = cleanChildren(parent, current, marker);
if (multi) return current;
} else if (currentArray) {
if (current.length === 0) {
appendNodes(parent, array, marker); // 原来没有节点
} else {
reconcileArrays(parent, current, array); // 原本有节点,现在也有节点
}
} else {
current && cleanChildren(parent);
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) {
parent.appendChild(value);
} else {
parent.replaceChild(value, parent.firstChild);
}
current = value;
}
return current;
}
function cleanChildren(parent, current, marker, replacement) {
if (marker === undefined) {
return (parent.textContent = '');
}
const node = replacement || document.createTextNode('');
if (current.length) {
let inserted = false;
for (let i = current.length - 1; i >= 0; i--) {
const el = current[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();
}
} else {
inserted = true;
}
}
} else {
parent.insertBefore(node, marker);
}
return [node];
}
function appendNodes(parent, array, marker = null) {
for (let i = 0, len = array.length; i < len; i++) {
parent.insertBefore(array[i], marker);
}
}
// 拆解数组,如:[[a, b], [c, d], ...] to [a, b, c, d]
function normalizeIncomingArray(normalized, array, unwrap) {
let dynamic = false;
for (let i = 0, len = array.length; i < len; i++) {
let item = array[i],
t;
if (item == null || item === true || item === false) {
// matches null, undefined, true or false
// skip
} else if (Array.isArray(item)) {
dynamic = normalizeIncomingArray(normalized, item) || dynamic;
} else if ((t = typeof item) === 'string' || t === 'number') {
normalized.push(document.createTextNode(item));
} else if (t === 'function') {
if (unwrap) {
while (typeof item === 'function') item = item();
dynamic = normalizeIncomingArray(normalized, Array.isArray(item) ? item : [item]) || dynamic;
} else {
normalized.push(item);
dynamic = true;
}
} else {
normalized.push(item);
}
}
return dynamic;
}
// 原本有节点,现在也有节点
export default function reconcileArrays(parentNode, oldChildren, newChildren) {
let nLength = newChildren.length,
oEnd = oldChildren.length,
nEnd = nLength,
oStart = 0,
nStart = 0,
after = oldChildren[oEnd - 1].nextSibling,
map = null;
while (oStart < oEnd || nStart < nEnd) {
// 从前到后对比相同内容
if (oldChildren[oStart] === newChildren[nStart]) {
oStart++;
nStart++;
continue;
}
// 从后往前对比相同内容
while (oldChildren[oEnd - 1] === newChildren[nEnd - 1]) {
oEnd--;
nEnd--;
}
// append
if (oEnd === oStart) {
// 旧节点全部和新节点相同(不是完全相同, 如:旧 abcd 新 abefcd
const node = nEnd < nLength ? (nStart ? newChildren[nStart - 1].nextSibling : newChildren[nEnd - nStart]) : after;
while (nStart < nEnd) {
parentNode.insertBefore(newChildren[nStart++], node);
}
// remove
} else if (nEnd === nStart) {
// 新节点全部和新节点相同(不是完全相同, 如:旧 abefcd 新 abcd
while (oStart < oEnd) {
if (!map || !map.has(oldChildren[oStart])) {
oldChildren[oStart].remove();
}
oStart++;
}
// swap backward
} else if (oldChildren[oStart] === newChildren[nEnd - 1] && newChildren[nStart] === oldChildren[oEnd - 1]) {
// 如:旧 ab ef cd 新 ab fe cd
const node = oldChildren[--oEnd].nextSibling;
parentNode.insertBefore(newChildren[nStart++], oldChildren[oStart++].nextSibling); // 如:旧 abe f fcd
parentNode.insertBefore(newChildren[--nEnd], node); // 如:旧 abeff e cd
oldChildren[oEnd] = newChildren[nEnd];
// fallback to map
} else {
// 如:旧 ab feww cd 新 ab hgeht cd
if (!map) {
map = new Map();
let i = nStart;
while (i < nEnd) {
map.set(newChildren[i], i++); // 收集 hgeht
}
}
const index = map.get(oldChildren[oStart]);
if (index != null) {
// 如e就在newChildren中
if (nStart < index && index < nEnd) {
// 且位置在新节点 之间
let i = oStart,
sequence = 1,
t;
while (++i < oEnd && i < nEnd) {
// 如:旧 ab feww cd 新 ab hgeht cd, e 的 sequence 是 2
if ((t = map.get(oldChildren[i])) == null || t !== index + sequence) break;
sequence++;
}
if (sequence > index - nStart) {
const node = oldChildren[oStart];
while (nStart < index) {
parentNode.insertBefore(newChildren[nStart++], node);
}
} else {
parentNode.replaceChild(newChildren[nStart++], oldChildren[oStart++]);
}
} else {
oStart++;
}
} else {
oldChildren[oStart++].remove();
}
}
}
}
export function setAttribute(node, name, value) {
if (value == null) {
node.removeAttribute(name);
} else {
node.setAttribute(name, value);
}
}
export function className(node, value) {
if (value == null) {
node.removeAttribute('class');
} else {
node.className = value;
}
}

View File

@ -1,85 +0,0 @@
/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
const $$EVENTS = "_$DX_DELEGATE";
/**
* document上注册事件
* @param eventNames
* @param document
*/
export function delegateEvents(eventNames, document = window.document) {
const e = document[$$EVENTS] || (document[$$EVENTS] = new Set());
for (let i = 0, l = eventNames.length; i < l; i++) {
const name = eventNames[i];
if (!e.has(name)) {
e.add(name);
document.addEventListener(name, eventHandler);
}
}
}
export function clearDelegatedEvents(document = window.document) {
if (document[$$EVENTS]) {
for (let name of document[$$EVENTS].keys()) document.removeEventListener(name, eventHandler);
delete document[$$EVENTS];
}
}
function eventHandler(e) {
const key = `$$${e.type}`;
let node = (e.composedPath && e.composedPath()[0]) || e.target;
if (e.target !== node) {
Object.defineProperty(e, "target", {
configurable: true,
value: node
});
}
Object.defineProperty(e, "currentTarget", {
configurable: true,
get() {
return node || document;
}
});
// 冒泡执行事件
while (node) {
const handler = node[key];
if (handler && !node.disabled) {
const data = node[`${key}Data`];
data !== undefined ? handler.call(node, data, e) : handler.call(node, e);
if (e.cancelBubble) {
return;
}
}
node = node._$host || node.parentNode || node.host;
}
}
export function addEventListener(node, name, handler, delegate) {
if (delegate) {
if (Array.isArray(handler)) {
node[`$$${name}`] = handler[0];
node[`$$${name}Data`] = handler[1];
} else {
node[`$$${name}`] = handler;
}
} else if (Array.isArray(handler)) {
const handlerFn = handler[0];
node.addEventListener(name, (handler[0] = e => handlerFn.call(node, handler[1], e)));
} else {
node.addEventListener(name, handler);
}
}