diff --git a/.eslintignore b/.eslintignore index 25439044..60feb237 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ **/node_modules build/ +*.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index 79557bf5..46239631 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,8 @@ module.exports = { extends: [ 'eslint:recommended', + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", 'prettier', ], root: true, @@ -41,6 +43,9 @@ module.exports = { 'no-for-of-loops/no-for-of-loops': 'error', 'no-function-declare-after-return/no-function-declare-after-return': 'error', }, + globals: { + isDev: true + }, overrides: [ { files: [ diff --git a/libs/horizon/index.d.ts b/libs/horizon/index.d.ts index 4c54c7c5..bab6684d 100644 --- a/libs/horizon/index.d.ts +++ b/libs/horizon/index.d.ts @@ -1 +1,4 @@ -declare var isDev: any; +/* + 区分是否开发者模式 + */ +declare var isDev: boolean; diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 9bf2455a..721bbff6 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -35,6 +35,7 @@ import { updateParentsChildShouldUpdate, updateShouldUpdateOfTree } from './vnode/VNodeShouldUpdate'; +import { getPathArr } from './utils/vNodePath'; // 不可恢复错误 let unrecoverableErrorDuringBuild: any = null; @@ -142,11 +143,11 @@ function handleError(root, error): void { } // 判断数组中节点的path的idx元素是否都相等 -function isEqualByIndex(idx: number, nodes: Array) { - let val = nodes[0].path[idx]; - for (let i = 1; i < nodes.length; i++) { - let node = nodes[i]; - if (val !== node.path[idx]) { +function isEqualByIndex(idx: number, pathArrays: string[][]) { + const first = pathArrays[0][idx]; + for (let i = 1; i < pathArrays.length; i++) { + const pathArr = pathArrays[i]; + if (idx >= pathArr.length || first !== pathArr[idx]) { return false; } } @@ -179,29 +180,19 @@ export function calcStartUpdateVNode(treeRoot: VNode) { return treeRoot; } - // 找到路径最短的长度 - let minPath = toUpdateNodes[0].path.length; - for (let i = 1; i < toUpdateNodes.length; i++) { - let pathLen = toUpdateNodes[i].path.length; - if (pathLen < minPath) { - minPath = pathLen; - } - } - + const pathArrays = toUpdateNodes.map(node => getPathArr(node)); // 找出开始不相等的idx - let idx = 0; - for (; idx < minPath; idx++) { - if (!isEqualByIndex(idx, toUpdateNodes)) { - break; - } + let commonPathEndIndex = 0; + while (isEqualByIndex(commonPathEndIndex, pathArrays)) { + commonPathEndIndex++; } // 得到相等的路径 - const startNodePath = toUpdateNodes[0].path.slice(0, idx); + const startNodePath = pathArrays[0].slice(0, commonPathEndIndex); let node = treeRoot; for (let i = 1; i < startNodePath.length; i++) { const pathIndex = Number(startNodePath[i]); - node = getChildByIndex(node, pathIndex); + node = getChildByIndex(node, pathIndex)!; } return node; diff --git a/libs/horizon/src/renderer/components/BaseClassComponent.ts b/libs/horizon/src/renderer/components/BaseClassComponent.ts index 74f6df80..08b6fb30 100644 --- a/libs/horizon/src/renderer/components/BaseClassComponent.ts +++ b/libs/horizon/src/renderer/components/BaseClassComponent.ts @@ -1,22 +1,29 @@ /** * Component的api setState和forceUpdate在实例生成阶段实现 */ -class Component { +class Component { props: P; context: C; state: S | null; refs: any; - setState: any; forceUpdate: any; + isReactComponent: boolean; constructor(props: P, context: C) { this.props = props; this.context = context; } + + setState(state: S) { + if (isDev) { + console.error('Cant not call `this.setState` in the constructor of class component, it will do nothing'); + } + } } // 兼容三方件 react-lifecycles-compat,它会读取 isReactComponent 属性值,不添加会导致 eview-ui 官网白屏 Component.prototype.isReactComponent = true; + /** * 支持PureComponent */ diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index db920f79..2b97612f 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -17,6 +17,7 @@ import { isObjectType, } from './DiffTools'; import { travelChildren } from '../vnode/VNodeUtils'; +import { markVNodePath } from '../utils/vNodePath'; enum DiffCategory { TEXT_NODE = 'TEXT_NODE', @@ -241,7 +242,7 @@ function diffArrayNodesHandler( prevNewNode.next = newNode; newNode.cIndex = prevNewNode.cIndex + 1; } - newNode.path = newNode.parent.path + newNode.cIndex; + markVNodePath(newNode); prevNewNode = newNode; } @@ -477,7 +478,7 @@ function setVNodesCIndex(startChild: VNode | null, startIdx: number) { while (node !== null) { node.cIndex = idx; - node.path = node.parent.path + node.cIndex; + markVNodePath(node); node = node.next; idx++; } @@ -528,7 +529,7 @@ function diffStringNodeHandler( } newTextNode.parent = parentNode; newTextNode.cIndex = 0; - newTextNode.path = newTextNode.parent.path + newTextNode.cIndex; + markVNodePath(newTextNode); return newTextNode; } @@ -606,7 +607,8 @@ function diffObjectNodeHandler( resultNode.parent = parentNode; resultNode.cIndex = 0; - resultNode.path = resultNode.parent.path + resultNode.cIndex; + markVNodePath(resultNode); + if (startDelVNode) { deleteVNodes(parentNode, startDelVNode); } diff --git a/libs/horizon/src/renderer/render/MemoComponent.ts b/libs/horizon/src/renderer/render/MemoComponent.ts index 84daa883..99a34364 100644 --- a/libs/horizon/src/renderer/render/MemoComponent.ts +++ b/libs/horizon/src/renderer/render/MemoComponent.ts @@ -8,6 +8,7 @@ import { TYPE_PROFILER, TYPE_STRICT_MODE, } from '../../external/JSXElementType'; +import { markVNodePath } from '../utils/vNodePath'; export function bubbleRender() {} @@ -20,7 +21,7 @@ export function captureMemoComponent( const newProps = mergeDefaultProps(Component, processing.props); if (processing.isCreated) { - let newChild = null; + let newChild: VNode | null = null; const type = Component.type; if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) { newChild = createFragmentVNode(null, newProps.children); @@ -29,7 +30,7 @@ export function captureMemoComponent( } newChild.parent = processing; newChild.ref = processing.ref; - newChild.path = newChild.parent.path + newChild.cIndex; + markVNodePath(newChild); processing.child = newChild; return newChild; @@ -48,7 +49,7 @@ export function captureMemoComponent( const newChild = updateVNode(firstChild, newProps); newChild.parent = processing; newChild.cIndex = 0; - newChild.path = newChild.parent.path + newChild.cIndex; + markVNodePath(newChild); newChild.ref = processing.ref; processing.child = newChild; diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index cdc2f4a4..3e7bdcb2 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -11,6 +11,7 @@ import {pushForceUpdate} from '../UpdateHandler'; import {launchUpdateFromVNode, tryRenderFromRoot} from '../TreeBuilder'; import {updateShouldUpdateOfTree} from '../vnode/VNodeShouldUpdate'; import {getContextChangeCtx} from '../ContextSaver'; +import { markVNodePath } from '../utils/vNodePath'; export enum SuspenseChildStatus { Init = '', @@ -44,7 +45,7 @@ function createFallback(processing: VNode, fallbackChildren) { fallbackFragment.parent = processing; fallbackFragment.eIndex = 1; fallbackFragment.cIndex = 1; - fallbackFragment.path = fallbackFragment.parent.path + fallbackFragment.cIndex; + markVNodePath(fallbackFragment); processing.suspenseChildStatus = SuspenseChildStatus.ShowFallback; return fallbackFragment; @@ -76,7 +77,7 @@ function createSuspenseChildren(processing: VNode, newChildren) { childFragment.parent = processing; childFragment.cIndex = 0; - childFragment.path = childFragment.parent.path + childFragment.cIndex; + markVNodePath(childFragment); processing.child = childFragment; processing.promiseResolve = false; return processing.child; diff --git a/libs/horizon/src/renderer/utils/vNodePath.ts b/libs/horizon/src/renderer/utils/vNodePath.ts new file mode 100644 index 00000000..5f8b78ac --- /dev/null +++ b/libs/horizon/src/renderer/utils/vNodePath.ts @@ -0,0 +1,15 @@ +import { VNode } from '../vnode/VNode'; + +const PATH_DELIMITER = ','; + +/** + * 标记VNode在VNode树中的路径 + * @param vNode + */ +export function markVNodePath(vNode: VNode) { + vNode.path = `${vNode.parent!.path}${PATH_DELIMITER}${vNode.cIndex}`; +} + +export function getPathArr(vNode: VNode) { + return vNode.path.split(PATH_DELIMITER); +} diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 865f791e..8c76d63c 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -26,6 +26,7 @@ import { } from '../../external/JSXElementType'; import { VNode } from './VNode'; import { JSXElement } from '../Types'; +import { markVNodePath } from '../utils/vNodePath'; const typeLazyMap = { [TYPE_FORWARD_REF]: ForwardRef, @@ -135,7 +136,7 @@ export function createUndeterminedVNode(type, key, props) { export function createTreeRootVNode(container) { const vNode = newVirtualNode(TreeRoot, null, null, container); - vNode.path += 0; + vNode.path = '0'; vNode.updates = []; return vNode; } @@ -147,7 +148,7 @@ export function createVNode(tag: VNodeTag | string, ...secondArg) { case TreeRoot: // 创建treeRoot vNode = newVirtualNode(TreeRoot, null, null, secondArg[0]); - vNode.path += 0; + vNode.path = '0'; vNode.updates = []; break; @@ -178,7 +179,7 @@ export function onlyUpdateChildVNodes(processing: VNode): VNode | null { let child: VNode | null = processing.child; while (child !== null) { updateVNode(child, child.props); - child.path = child.parent.path + child.cIndex; + markVNodePath(child); child = child.next; } } @@ -209,7 +210,7 @@ export function onlyUpdateChildVNodes(processing: VNode): VNode | null { while (queue.length) { const vNode = queue.shift()!; - vNode.path = vNode.parent.path + vNode.cIndex; + markVNodePath(vNode); putChildrenIntoQueue(vNode) } diff --git a/tsconfig.json b/tsconfig.json index e05c4501..e85c8cac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -34,8 +34,8 @@ }, "include": [ "./libs/**/src/**/*.ts", - "libs/index.d.ts" + "libs/horizon/index.d.ts" ], "exclude": ["node_modules", "**/*.spec.ts", "dev"], - "types": ["node"], + "types": ["node"] }