Match-id-eed3541e50592150d80fd93a0767866a60146fb8
This commit is contained in:
commit
5cde85df95
|
@ -1,2 +1,3 @@
|
|||
**/node_modules
|
||||
build/
|
||||
*.d.ts
|
||||
|
|
20
.eslintrc.js
20
.eslintrc.js
|
@ -1,6 +1,8 @@
|
|||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
'prettier',
|
||||
],
|
||||
root: true,
|
||||
|
@ -29,8 +31,11 @@ module.exports = {
|
|||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'semi': ['warn', 'always'],
|
||||
'quotes': ['warn', 'single'],
|
||||
'accessor-pairs': 'off',
|
||||
'brace-style': ['error', '1tbs'],
|
||||
'func-style': ['warn', 'declaration', { allowArrowFunctions: true }],
|
||||
|
@ -42,4 +47,17 @@ 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: [
|
||||
'scripts/__tests__/**/*.js'
|
||||
],
|
||||
globals: {
|
||||
container: true
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@babel/react',
|
||||
'@babel/react',
|
||||
'@babel/preset-typescript',
|
||||
['@babel/preset-env', { targets: { node: 'current' } }]
|
||||
],
|
||||
|
@ -30,7 +30,10 @@ module.exports = {
|
|||
['@babel/plugin-proposal-private-methods', { 'loose': true }],
|
||||
['@babel/plugin-proposal-private-property-in-object', { 'loose': true }],
|
||||
'@babel/plugin-syntax-jsx',
|
||||
'@babel/plugin-transform-react-jsx',
|
||||
['@babel/plugin-transform-react-jsx', {
|
||||
pragma: 'Horizon.createElement',
|
||||
pragmaFrag: 'Horizon.Fragment'
|
||||
}],
|
||||
'@babel/plugin-transform-flow-strip-types',
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
declare var isDev: any;
|
||||
/*
|
||||
区分是否开发者模式
|
||||
*/
|
||||
declare var isDev: boolean;
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
* 在深度遍历过程中,begin阶段会修改一些全局的值,在complete阶段会恢复。
|
||||
*/
|
||||
|
||||
import type {VNode, ContextType} from './Types';
|
||||
import type {Container} from '../dom/DOMOperator';
|
||||
import type { VNode, ContextType } from './Types';
|
||||
import type { Container } from '../dom/DOMOperator';
|
||||
|
||||
import {getNSCtx} from '../dom/DOMOperator';
|
||||
import {ContextProvider} from './vnode/VNodeTags';
|
||||
import { getNSCtx } from '../dom/DOMOperator';
|
||||
import { ContextProvider } from './vnode/VNodeTags';
|
||||
|
||||
// 保存的是“http://www.w3.org/1999/xhtml”或“http://www.w3.org/2000/svg”,
|
||||
// 用于识别是使用document.createElement()还是使用document.createElementNS()创建DOM
|
||||
|
@ -18,16 +18,8 @@ const CTX_CONTEXT = 'CTX_CONTEXT';
|
|||
|
||||
// 旧版context API,是否更改。
|
||||
const CTX_OLD_CHANGE = 'CTX_OLD_CHANGE';
|
||||
// 旧版context API,保存的是的当前组件提供给子组件使用的context。
|
||||
const CTX_OLD_CONTEXT = 'CTX_OLD_CONTEXT';
|
||||
// 旧版context API,保存的是的上一个提供者提供给后代组件使用的context。
|
||||
const CTX_OLD_PREVIOUS_CONTEXT = 'CTX_OLD_PREVIOUS_CONTEXT';
|
||||
|
||||
let ctxNamespace: string = '';
|
||||
|
||||
let ctxOldContext: Object = {};
|
||||
let ctxOldChange: Boolean = false;
|
||||
let ctxOldPreviousContext: Object = {};
|
||||
let ctxOldChange = false;
|
||||
let ctxNamespace = '';
|
||||
|
||||
function setContext(vNode: VNode, contextName, value) {
|
||||
if (vNode.contexts === null) {
|
||||
|
@ -38,6 +30,7 @@ function setContext(vNode: VNode, contextName, value) {
|
|||
vNode.contexts[contextName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
function getContext(vNode: VNode, contextName) {
|
||||
if (vNode.contexts !== null) {
|
||||
return vNode.contexts[contextName];
|
||||
|
@ -87,44 +80,6 @@ function recoverParentsContextCtx(vNode: VNode) {
|
|||
}
|
||||
}
|
||||
|
||||
// ctxOldContext是 旧context提供者的context
|
||||
function setVNodeOldContext(providerVNode: VNode, context: Object) {
|
||||
setContext(providerVNode, CTX_OLD_CONTEXT, context);
|
||||
}
|
||||
|
||||
function getVNodeOldContext(vNode: VNode) {
|
||||
return getContext(vNode, CTX_OLD_CONTEXT);
|
||||
}
|
||||
|
||||
function setOldContextCtx(providerVNode: VNode, context: Object) {
|
||||
setVNodeOldContext(providerVNode, context);
|
||||
ctxOldContext = context;
|
||||
}
|
||||
|
||||
function getOldContextCtx() {
|
||||
return ctxOldContext;
|
||||
}
|
||||
|
||||
function resetOldContextCtx(vNode: VNode) {
|
||||
ctxOldContext = getVNodeOldContext(vNode);
|
||||
}
|
||||
|
||||
function setVNodeOldPreviousContext(providerVNode: VNode, context: Object) {
|
||||
setContext(providerVNode, CTX_OLD_PREVIOUS_CONTEXT, context);
|
||||
}
|
||||
|
||||
function getVNodeOldPreviousContext(vNode: VNode) {
|
||||
return getContext(vNode, CTX_OLD_PREVIOUS_CONTEXT);
|
||||
}
|
||||
|
||||
function setOldPreviousContextCtx(context: Object) {
|
||||
ctxOldPreviousContext = context;
|
||||
}
|
||||
|
||||
function getOldPreviousContextCtx() {
|
||||
return ctxOldPreviousContext;
|
||||
}
|
||||
|
||||
function setContextChangeCtx(providerVNode: VNode, didChange: boolean) {
|
||||
setContext(providerVNode, CTX_OLD_CHANGE, didChange);
|
||||
ctxOldChange = didChange;
|
||||
|
@ -145,18 +100,9 @@ export {
|
|||
setContextCtx,
|
||||
resetContextCtx,
|
||||
recoverParentsContextCtx,
|
||||
setOldContextCtx,
|
||||
getOldContextCtx,
|
||||
resetOldContextCtx,
|
||||
setContextChangeCtx,
|
||||
getContextChangeCtx,
|
||||
resetContextChangeCtx,
|
||||
setOldPreviousContextCtx,
|
||||
getOldPreviousContextCtx,
|
||||
setVNodeOldContext,
|
||||
getVNodeOldContext,
|
||||
setVNodeOldPreviousContext,
|
||||
getVNodeOldPreviousContext,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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<VNode>) {
|
||||
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;
|
||||
|
|
|
@ -1,22 +1,29 @@
|
|||
/**
|
||||
* Component的api setState和forceUpdate在实例生成阶段实现
|
||||
*/
|
||||
class Component<P,S,C> {
|
||||
class Component<P, S, C> {
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
import type {VNode} from '../../Types';
|
||||
|
||||
import {
|
||||
setOldContextCtx,
|
||||
setContextChangeCtx,
|
||||
getOldContextCtx,
|
||||
resetOldContextCtx,
|
||||
resetContextChangeCtx,
|
||||
setOldPreviousContextCtx,
|
||||
getOldPreviousContextCtx,
|
||||
setVNodeOldContext,
|
||||
getVNodeOldContext,
|
||||
setVNodeOldPreviousContext,
|
||||
getVNodeOldPreviousContext,
|
||||
} from '../../ContextSaver';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
// 判断是否是过时的context的提供者
|
||||
export function isOldProvider(comp: Function): boolean {
|
||||
// @ts-ignore
|
||||
const childContextTypes = comp.childContextTypes;
|
||||
return childContextTypes !== null && childContextTypes !== undefined;
|
||||
}
|
||||
|
||||
// 判断是否是过时的context的消费者
|
||||
export function isOldConsumer(comp: Function): boolean {
|
||||
// @ts-ignore
|
||||
const contextTypes = comp.contextTypes;
|
||||
return contextTypes !== null && contextTypes !== undefined;
|
||||
}
|
||||
|
||||
// 如果是旧版context提供者,则缓存两个全局变量,上一个提供者提供的context和当前提供者提供的context
|
||||
export function cacheOldCtx(processing: VNode, hasOldContext: any): void {
|
||||
// 每一个context提供者都会更新ctxOldContext
|
||||
if (hasOldContext) {
|
||||
setOldPreviousContextCtx(getOldContextCtx());
|
||||
|
||||
const vNodeContext = getVNodeOldContext(processing) || emptyObject;
|
||||
setOldContextCtx(processing, vNodeContext);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前组件可以消费的context
|
||||
export function getOldContext(processing: VNode, clazz: Function, ifProvider: boolean) {
|
||||
const type = processing.type;
|
||||
// 不是context消费者, 则直接返回空对象
|
||||
if (!isOldConsumer(type)) {
|
||||
return emptyObject;
|
||||
}
|
||||
|
||||
// 当组件既是提供者,也是消费者时,取上一个context,不能直接取最新context,因为已经被更新为当前组件的context;
|
||||
// 当组件只是消费者时,则取最新context
|
||||
const parentContext = (ifProvider && isOldProvider(clazz)) ?
|
||||
getOldPreviousContextCtx() :
|
||||
getOldContextCtx();
|
||||
|
||||
// 除非父级context更改,否则不需要重新创建子context,直接取对应节点上存的。
|
||||
if (getVNodeOldPreviousContext(processing) === parentContext) {
|
||||
return getVNodeOldContext(processing);
|
||||
}
|
||||
|
||||
// 从父的context中取出子定义的context
|
||||
const context = {};
|
||||
for (const key in type.contextTypes) {
|
||||
context[key] = parentContext[key];
|
||||
}
|
||||
|
||||
// 缓存当前组件的context,最近祖先传递下来context,当前可消费的context
|
||||
setVNodeOldPreviousContext(processing, parentContext);
|
||||
setVNodeOldContext(processing, context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
// 重置context
|
||||
export function resetOldCtx(vNode: VNode): void {
|
||||
resetOldContextCtx(vNode);
|
||||
resetContextChangeCtx(vNode);
|
||||
}
|
||||
|
||||
// 当前组件是提供者,则需要合并祖先context和当前组件提供的context
|
||||
function handleContext(vNode: VNode, parentContext: Object): Object {
|
||||
const instance = vNode.realNode;
|
||||
|
||||
if (typeof instance.getChildContext !== 'function') {
|
||||
return parentContext;
|
||||
}
|
||||
|
||||
// 合并祖先提供的context和当前组件提供的context
|
||||
return {...parentContext, ...instance.getChildContext()};
|
||||
}
|
||||
|
||||
// 当前组件是context提供者,更新时,需要合并祖先context和当前组件提供的context
|
||||
export function updateOldContext(vNode: VNode): void {
|
||||
const ctx = handleContext(vNode, getOldPreviousContextCtx());
|
||||
// 更新context,给子组件用的context
|
||||
setOldContextCtx(vNode, ctx);
|
||||
// 标记更改
|
||||
setContextChangeCtx(vNode, true);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { VNode } from '../Types';
|
||||
import type { Hook } from './HookType';
|
||||
|
||||
let processingVNode: VNode = null;
|
||||
let processingVNode: VNode | null = null;
|
||||
|
||||
// lastTimeHook是上一次执行func时产生的hooks中,与currentHook对应的hook
|
||||
let lastTimeHook: Hook<any, any> | null = null;
|
||||
|
@ -13,7 +13,7 @@ export function getProcessingVNode() {
|
|||
return processingVNode;
|
||||
}
|
||||
|
||||
export function setProcessingVNode(vNode: VNode) {
|
||||
export function setProcessingVNode(vNode: VNode | null) {
|
||||
processingVNode = vNode;
|
||||
}
|
||||
|
||||
|
@ -21,11 +21,11 @@ export function getLastTimeHook() {
|
|||
return lastTimeHook;
|
||||
}
|
||||
|
||||
export function setLastTimeHook(hook: Hook<any, any>) {
|
||||
export function setLastTimeHook(hook: Hook<any, any> | null) {
|
||||
lastTimeHook = hook;
|
||||
}
|
||||
|
||||
export function setCurrentHook(hook: Hook<any, any>) {
|
||||
export function setCurrentHook(hook: Hook<any, any> | null) {
|
||||
currentHook = hook;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,17 +15,17 @@ import {
|
|||
setProcessingVNode,
|
||||
setCurrentHook, getNextHook
|
||||
} from './BaseHook';
|
||||
import {useStateImpl,} from './UseStateHook';
|
||||
import {useReducerImpl,} from './UseReducerHook';
|
||||
import {useStateImpl} from './UseStateHook';
|
||||
import {useReducerImpl} from './UseReducerHook';
|
||||
import {HookStage, setHookStage} from './HookStage';
|
||||
|
||||
// hook对外入口
|
||||
export function exeFunctionHook(
|
||||
funcComp: (props: Object, arg: Object) => any,
|
||||
props: Object,
|
||||
arg: Object,
|
||||
export function exeFunctionHook<Props extends Record<string, any>, Arg>(
|
||||
funcComp: (props: Props, arg: Arg) => any,
|
||||
props: Props,
|
||||
arg: Arg,
|
||||
processing: VNode,
|
||||
): any {
|
||||
) {
|
||||
// 重置全局变量
|
||||
resetGlobalVariable();
|
||||
|
||||
|
@ -39,13 +39,13 @@ export function exeFunctionHook(
|
|||
processing.effectList = [];
|
||||
|
||||
// 设置hook阶段
|
||||
if (processing.isCreated || !processing.oldHooks.length) {
|
||||
if (processing.isCreated || !processing.oldHooks!.length) {
|
||||
setHookStage(HookStage.Init);
|
||||
} else {
|
||||
setHookStage(HookStage.Update);
|
||||
}
|
||||
|
||||
let comp = funcComp(props, arg);
|
||||
const comp = funcComp(props, arg);
|
||||
|
||||
// 设置hook阶段为null,用于判断hook是否在函数组件中调用
|
||||
setHookStage(null);
|
||||
|
|
|
@ -10,6 +10,6 @@ export function getHookStage() {
|
|||
return hookStage;
|
||||
}
|
||||
|
||||
export function setHookStage(phase: HookStage) {
|
||||
export function setHookStage(phase: HookStage | null) {
|
||||
hookStage = phase;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import type { VNode } from '../Types';
|
||||
|
||||
import {cacheOldCtx, isOldProvider} from '../components/context/CompatibleContext';
|
||||
import {
|
||||
ClassComponent,
|
||||
ContextProvider,
|
||||
DomComponent,
|
||||
DomPortal,
|
||||
|
@ -23,11 +21,6 @@ function handlerContext(processing: VNode) {
|
|||
case DomComponent:
|
||||
setNamespaceCtx(processing);
|
||||
break;
|
||||
case ClassComponent: {
|
||||
const isOldCxtExist = isOldProvider(processing.type);
|
||||
cacheOldCtx(processing, isOldCxtExist);
|
||||
break;
|
||||
}
|
||||
case DomPortal:
|
||||
setNamespaceCtx(processing, processing.realNode);
|
||||
break;
|
||||
|
|
|
@ -2,13 +2,6 @@ import type { VNode } from '../Types';
|
|||
|
||||
import { mergeDefaultProps } from './LazyComponent';
|
||||
import { getNewContext, resetDepContexts } from '../components/context/Context';
|
||||
import {
|
||||
cacheOldCtx,
|
||||
getOldContext,
|
||||
isOldProvider,
|
||||
resetOldCtx,
|
||||
updateOldContext,
|
||||
} from '../components/context/CompatibleContext';
|
||||
import {
|
||||
callComponentWillMount,
|
||||
callComponentWillReceiveProps,
|
||||
|
@ -25,17 +18,18 @@ import { markRef } from './BaseComponent';
|
|||
import {
|
||||
processUpdates,
|
||||
} from '../UpdateHandler';
|
||||
import { getContextChangeCtx, setContextChangeCtx } from '../ContextSaver';
|
||||
import { getContextChangeCtx } from '../ContextSaver';
|
||||
import { setProcessingClassVNode } from '../GlobalVar';
|
||||
import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator';
|
||||
import { createChildrenByDiff } from '../diff/nodeDiffComparator';
|
||||
|
||||
const emptyContextObj = {};
|
||||
// 获取当前节点的context
|
||||
export function getCurrentContext(clazz, processing: VNode) {
|
||||
const context = clazz.contextType;
|
||||
return typeof context === 'object' && context !== null
|
||||
? getNewContext(processing, context)
|
||||
: getOldContext(processing, clazz, true);
|
||||
: emptyContextObj;
|
||||
}
|
||||
|
||||
// 挂载实例
|
||||
|
@ -112,8 +106,6 @@ export function captureRender(processing: VNode): VNode | null {
|
|||
clazz = clazz._load(clazz._content);
|
||||
}
|
||||
}
|
||||
const isOldCxtExist = isOldProvider(clazz);
|
||||
cacheOldCtx(processing, isOldCxtExist);
|
||||
|
||||
resetDepContexts(processing);
|
||||
|
||||
|
@ -170,24 +162,13 @@ export function captureRender(processing: VNode): VNode | null {
|
|||
|
||||
// 不复用
|
||||
if (shouldUpdate) {
|
||||
// 更新context
|
||||
if (isOldCxtExist) {
|
||||
updateOldContext(processing);
|
||||
}
|
||||
return createChildren(clazz, processing);
|
||||
} else {
|
||||
if (isOldCxtExist) {
|
||||
setContextChangeCtx(processing, false);
|
||||
}
|
||||
return onlyUpdateChildVNodes(processing);
|
||||
}
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
if (isOldProvider(processing.type)) {
|
||||
resetOldCtx(processing);
|
||||
}
|
||||
}
|
||||
export function bubbleRender(processing: VNode) {}
|
||||
|
||||
// 用于未完成的类组件
|
||||
export function getIncompleteClassComponent(clazz, processing: VNode, nextProps: object): VNode | null {
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import type {VNode} from '../Types';
|
||||
|
||||
import {FunctionComponent} from '../vnode/VNodeTags';
|
||||
import {resetDepContexts} from '../components/context/Context';
|
||||
import {getOldContext} from '../components/context/CompatibleContext';
|
||||
import {FlagUtils} from '../vnode/VNodeFlags';
|
||||
import {exeFunctionHook} from '../hooks/HookMain';
|
||||
import { createChildrenByDiff } from '../diff/nodeDiffComparator';
|
||||
|
||||
function captureIndeterminateComponent(
|
||||
processing: VNode,
|
||||
): VNode | null {
|
||||
const funcComp = processing.type;
|
||||
|
||||
if (!processing.isCreated) {
|
||||
processing.isCreated = true;
|
||||
FlagUtils.markAddition(processing);
|
||||
}
|
||||
|
||||
const props = processing.props;
|
||||
const context = getOldContext(processing, funcComp, false);
|
||||
|
||||
resetDepContexts(processing);
|
||||
|
||||
const newElements = exeFunctionHook(funcComp, props, context, processing);
|
||||
|
||||
processing.tag = FunctionComponent;
|
||||
processing.child = createChildrenByDiff(processing, processing.child, newElements, !processing.isCreated);
|
||||
return processing.child;
|
||||
}
|
||||
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
return captureIndeterminateComponent(processing);
|
||||
}
|
||||
|
||||
export function bubbleRender() {}
|
|
@ -1,19 +1,19 @@
|
|||
import type {VNode} from '../Types';
|
||||
import type { VNode } from '../Types';
|
||||
|
||||
import {mergeDefaultProps} from './LazyComponent';
|
||||
import {getOldContext} from '../components/context/CompatibleContext';
|
||||
import {resetDepContexts} from '../components/context/Context';
|
||||
import {exeFunctionHook} from '../hooks/HookMain';
|
||||
import {ForwardRef} from '../vnode/VNodeTags';
|
||||
import {FlagUtils, Update} from '../vnode/VNodeFlags';
|
||||
import {getContextChangeCtx} from '../ContextSaver';
|
||||
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
||||
import { mergeDefaultProps } from './LazyComponent';
|
||||
import { resetDepContexts } from '../components/context/Context';
|
||||
import { exeFunctionHook } from '../hooks/HookMain';
|
||||
import { ForwardRef } from '../vnode/VNodeTags';
|
||||
import { FlagUtils, Update } from '../vnode/VNodeFlags';
|
||||
import { getContextChangeCtx } from '../ContextSaver';
|
||||
import { onlyUpdateChildVNodes } from '../vnode/VNodeCreator';
|
||||
import { createChildrenByDiff } from '../diff/nodeDiffComparator';
|
||||
|
||||
// 在useState, useReducer的时候,会触发state变化
|
||||
let stateChange = false;
|
||||
|
||||
export function bubbleRender() {}
|
||||
export function bubbleRender() {
|
||||
}
|
||||
|
||||
// 判断children是否可以复用
|
||||
function checkIfCanReuseChildren(processing: VNode, shouldUpdate?: boolean) {
|
||||
|
@ -52,13 +52,16 @@ export function captureFunctionComponent(
|
|||
processing: VNode,
|
||||
funcComp: any,
|
||||
nextProps: any,
|
||||
shouldUpdate?: boolean
|
||||
shouldUpdate?: boolean,
|
||||
) {
|
||||
let context;
|
||||
if (processing.tag !== ForwardRef) {
|
||||
context = getOldContext(processing, funcComp, true);
|
||||
}
|
||||
// 函数组件内已完成异步动作
|
||||
if (processing.isSuspended) {
|
||||
// 由于首次被打断,应仍为首次渲染
|
||||
processing.isCreated = true;
|
||||
FlagUtils.markAddition(processing);
|
||||
|
||||
processing.isSuspended = false;
|
||||
}
|
||||
resetDepContexts(processing);
|
||||
|
||||
const isCanReuse = checkIfCanReuseChildren(processing, shouldUpdate);
|
||||
|
@ -68,7 +71,7 @@ export function captureFunctionComponent(
|
|||
const newElements = exeFunctionHook(
|
||||
processing.tag === ForwardRef ? funcComp.render : funcComp,
|
||||
nextProps,
|
||||
processing.tag === ForwardRef ? processing.ref : context,
|
||||
processing.tag === ForwardRef ? processing.ref : undefined,
|
||||
processing,
|
||||
);
|
||||
|
||||
|
@ -95,7 +98,7 @@ export function captureRender(processing: VNode, shouldUpdate?: boolean): VNode
|
|||
processing,
|
||||
Component,
|
||||
resolvedProps,
|
||||
shouldUpdate
|
||||
shouldUpdate,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,18 +4,10 @@ import {mergeDefaultProps} from './LazyComponent';
|
|||
import {ClassComponent} from '../vnode/VNodeTags';
|
||||
import {resetDepContexts} from '../components/context/Context';
|
||||
import {getIncompleteClassComponent} from './ClassComponent';
|
||||
import {
|
||||
isOldProvider,
|
||||
resetOldCtx,
|
||||
cacheOldCtx,
|
||||
} from '../components/context/CompatibleContext';
|
||||
|
||||
function captureIncompleteClassComponent(processing, Component, nextProps) {
|
||||
processing.tag = ClassComponent;
|
||||
|
||||
const hasOldContext = isOldProvider(Component);
|
||||
cacheOldCtx(processing, hasOldContext);
|
||||
|
||||
resetDepContexts(processing);
|
||||
|
||||
return getIncompleteClassComponent(Component, processing, nextProps);
|
||||
|
@ -32,10 +24,4 @@ export function captureRender(processing: VNode): VNode | null {
|
|||
return captureIncompleteClassComponent(processing, Component, resolvedProps);
|
||||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
// 处理与类组件相同。
|
||||
const Component = processing.type;
|
||||
if (isOldProvider(Component)) {
|
||||
resetOldCtx(processing);
|
||||
}
|
||||
}
|
||||
export function bubbleRender(processing: VNode) {}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import {FlagUtils, Interrupted} from '../vnode/VNodeFlags';
|
|||
import {onlyUpdateChildVNodes, updateVNode, createFragmentVNode} from '../vnode/VNodeCreator';
|
||||
import {
|
||||
ClassComponent,
|
||||
FunctionComponent,
|
||||
IncompleteClassComponent,
|
||||
SuspenseComponent,
|
||||
} from '../vnode/VNodeTags';
|
||||
|
@ -11,6 +12,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 = '',
|
||||
|
@ -20,7 +22,7 @@ export enum SuspenseChildStatus {
|
|||
|
||||
// 创建fallback子节点
|
||||
function createFallback(processing: VNode, fallbackChildren) {
|
||||
const childFragment: VNode = processing.child;
|
||||
const childFragment: VNode = processing.child!;
|
||||
let fallbackFragment;
|
||||
childFragment.childShouldUpdate = false;
|
||||
|
||||
|
@ -44,7 +46,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 +78,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;
|
||||
|
@ -184,6 +186,9 @@ export function handleSuspenseChildThrowError(parent: VNode, processing: VNode,
|
|||
}
|
||||
}
|
||||
|
||||
if(processing.tag === FunctionComponent) {
|
||||
processing.isSuspended = true;
|
||||
}
|
||||
// 应该抛出promise未完成更新,标志待更新
|
||||
processing.shouldUpdate = true;
|
||||
|
||||
|
@ -222,7 +227,6 @@ export function listenToPromise(suspenseVNode: VNode) {
|
|||
// 记录已经监听的 promise
|
||||
let promiseCache = suspenseVNode.realNode;
|
||||
if (promiseCache === null) {
|
||||
// @ts-ignore
|
||||
promiseCache = new PossiblyWeakSet();
|
||||
suspenseVNode.realNode = new PossiblyWeakSet();
|
||||
}
|
||||
|
|
|
@ -2,13 +2,11 @@ import type {VNode} from '../Types';
|
|||
import {throwIfTrue} from '../utils/throwIfTrue';
|
||||
import {processUpdates} from '../UpdateHandler';
|
||||
import {resetNamespaceCtx, setNamespaceCtx} from '../ContextSaver';
|
||||
import {resetOldCtx} from '../components/context/CompatibleContext';
|
||||
import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator';
|
||||
import { createChildrenByDiff } from '../diff/nodeDiffComparator';
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
resetNamespaceCtx(processing);
|
||||
resetOldCtx(processing);
|
||||
}
|
||||
|
||||
function updateTreeRoot(processing) {
|
||||
|
|
|
@ -10,7 +10,6 @@ import * as DomPortalRender from './DomPortal';
|
|||
import * as TreeRootRender from './TreeRoot';
|
||||
import * as DomTextRender from './DomText';
|
||||
import * as IncompleteClassComponentRender from './IncompleteClassComponent';
|
||||
import * as ClsOrFunComponentRender from './ClsOrFunComponent';
|
||||
import * as LazyComponentRender from './LazyComponent';
|
||||
import * as MemoComponentRender from './MemoComponent';
|
||||
import * as SuspenseComponentRender from './SuspenseComponent';
|
||||
|
@ -27,7 +26,6 @@ import {
|
|||
TreeRoot,
|
||||
DomText,
|
||||
IncompleteClassComponent,
|
||||
ClsOrFunComponent,
|
||||
LazyComponent,
|
||||
MemoComponent,
|
||||
SuspenseComponent,
|
||||
|
@ -49,7 +47,6 @@ export default {
|
|||
[TreeRoot]: TreeRootRender,
|
||||
[DomText]: DomTextRender,
|
||||
[IncompleteClassComponent]: IncompleteClassComponentRender,
|
||||
[ClsOrFunComponent]: ClsOrFunComponentRender,
|
||||
[LazyComponent]: LazyComponentRender,
|
||||
[MemoComponent]: MemoComponentRender,
|
||||
[SuspenseComponent]: SuspenseComponentRender,
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* 虚拟DOM结构体
|
||||
*/
|
||||
import { TreeRoot, FunctionComponent, ClassComponent, DomPortal, DomText, ContextConsumer, ForwardRef, SuspenseComponent, LazyComponent, ClsOrFunComponent, DomComponent, Fragment, ContextProvider, Profiler, MemoComponent, IncompleteClassComponent } from './VNodeTags';
|
||||
import { TreeRoot, FunctionComponent, ClassComponent, DomPortal, DomText, ContextConsumer, ForwardRef, SuspenseComponent, LazyComponent, DomComponent, Fragment, ContextProvider, Profiler, MemoComponent, IncompleteClassComponent } from './VNodeTags';
|
||||
import type { VNodeTag } from './VNodeTags';
|
||||
import type { RefType, ContextType } from '../Types';
|
||||
import type { Hook } from '../hooks/HookType';
|
||||
|
@ -18,8 +18,8 @@ export class VNode {
|
|||
parent: VNode | null = null; // 父节点
|
||||
child: VNode | null = null; // 子节点
|
||||
next: VNode | null = null; // 兄弟节点
|
||||
cIndex: number = 0; // 节点在children数组中的位置
|
||||
eIndex: number = 0; // HorizonElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致
|
||||
cIndex = 0; // 节点在children数组中的位置
|
||||
eIndex = 0; // HorizonElement在jsx中的位置,例如:jsx中的null不会生成vNode,所以eIndex和cIndex不一致
|
||||
|
||||
ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数,submit阶段使用,比如将外部useRef生成的对象赋值到ref上
|
||||
oldProps: any = null;
|
||||
|
@ -30,15 +30,15 @@ export class VNode {
|
|||
updates: any[] | null; // TreeRoot和ClassComponent使用的更新数组
|
||||
stateCallbacks: any[] | null; // 存放存在setState的第二个参数和HorizonDOM.render的第三个参数所在的node数组
|
||||
isForceUpdate: boolean; // 是否使用强制更新
|
||||
|
||||
isSuspended = false; // 是否被suspense打断更新
|
||||
state: any; // ClassComponent和TreeRoot的状态
|
||||
hooks: Array<Hook<any, any>> | null; // 保存hook
|
||||
suspenseChildStatus: string = ''; // Suspense的Children是否显示
|
||||
suspenseChildStatus = ''; // Suspense的Children是否显示
|
||||
depContexts: Array<ContextType<any>> | null; // FunctionComponent和ClassComponent对context的依赖列表
|
||||
isDepContextChange: boolean; // context是否变更
|
||||
dirtyNodes: Array<VNode> | null = null; // 需要改动的节点数组
|
||||
shouldUpdate: boolean = false;
|
||||
childShouldUpdate: boolean = false;
|
||||
shouldUpdate = false;
|
||||
childShouldUpdate = false;
|
||||
task: any;
|
||||
|
||||
// 使用这个变量来记录修改前的值,用于恢复。
|
||||
|
@ -51,7 +51,7 @@ export class VNode {
|
|||
flags = InitFlag;
|
||||
clearChild: VNode | null;
|
||||
// one tree相关属性
|
||||
isCreated: boolean = true;
|
||||
isCreated = true;
|
||||
oldHooks: Array<Hook<any, any>> | null; // 保存上一次执行的hook
|
||||
oldState: any;
|
||||
oldRef: RefType | ((handle: any) => void) | null = null;
|
||||
|
@ -61,7 +61,7 @@ export class VNode {
|
|||
suspenseDidCapture: boolean; // suspense是否捕获了异常
|
||||
promiseResolve: boolean; // suspense的promise是否resolve
|
||||
|
||||
path: string = ''; // 保存从根到本节点的路径
|
||||
path = ''; // 保存从根到本节点的路径
|
||||
toUpdateNodes: Set<VNode> | null; // 保存要更新的节点
|
||||
|
||||
belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用
|
||||
|
@ -84,6 +84,7 @@ export class VNode {
|
|||
this.contexts = null;
|
||||
break;
|
||||
case FunctionComponent:
|
||||
this.realNode = null;
|
||||
this.effectList = null;
|
||||
this.hooks = null;
|
||||
this.depContexts = null;
|
||||
|
@ -101,10 +102,6 @@ export class VNode {
|
|||
this.oldState = null;
|
||||
this.contexts = null;
|
||||
break;
|
||||
case ClsOrFunComponent:
|
||||
this.realNode = null;
|
||||
this.contexts = null;
|
||||
break;
|
||||
case DomPortal:
|
||||
this.realNode = null;
|
||||
this.contexts = null;
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
DomPortal,
|
||||
TreeRoot,
|
||||
DomText,
|
||||
ClsOrFunComponent,
|
||||
LazyComponent,
|
||||
MemoComponent,
|
||||
SuspenseComponent,
|
||||
|
@ -26,6 +25,7 @@ import {
|
|||
} from '../../external/JSXElementType';
|
||||
import { VNode } from './VNode';
|
||||
import { JSXElement } from '../Types';
|
||||
import { markVNodePath } from '../utils/vNodePath';
|
||||
|
||||
const typeLazyMap = {
|
||||
[TYPE_FORWARD_REF]: ForwardRef,
|
||||
|
@ -51,13 +51,12 @@ function isClassComponent(comp: Function) {
|
|||
|
||||
// 解析懒组件的tag
|
||||
export function getLazyVNodeTag(lazyComp: any): string {
|
||||
let vNodeTag = ClsOrFunComponent;
|
||||
if (typeof lazyComp === 'function') {
|
||||
vNodeTag = isClassComponent(lazyComp) ? ClassComponent : FunctionComponent;
|
||||
return isClassComponent(lazyComp) ? ClassComponent : FunctionComponent;
|
||||
} else if (lazyComp !== undefined && lazyComp !== null && typeLazyMap[lazyComp.vtype]) {
|
||||
vNodeTag = typeLazyMap[lazyComp.vtype];
|
||||
return typeLazyMap[lazyComp.vtype];
|
||||
}
|
||||
return vNodeTag;
|
||||
throw Error('Horizon can\'t resolve the content of lazy');
|
||||
}
|
||||
|
||||
// 创建processing
|
||||
|
@ -104,7 +103,7 @@ export function createPortalVNode(portal) {
|
|||
}
|
||||
|
||||
export function createUndeterminedVNode(type, key, props) {
|
||||
let vNodeTag = ClsOrFunComponent;
|
||||
let vNodeTag = FunctionComponent;
|
||||
let isLazy = false;
|
||||
const componentType = typeof type;
|
||||
|
||||
|
@ -135,7 +134,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 +146,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 +177,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 +208,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)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* vNode结构的变化标志
|
||||
*/
|
||||
|
||||
import type { VNode } from '../Types';
|
||||
import type { VNode } from './VNode';
|
||||
|
||||
|
||||
export const InitFlag = /** */ 0;
|
||||
|
|
|
@ -6,7 +6,6 @@ export type VNodeTag = string;
|
|||
export const TreeRoot = 'TreeRoot'; // tree的根节点,用于存放一些tree级的变量
|
||||
export const FunctionComponent = 'FunctionComponent';
|
||||
export const ClassComponent = 'ClassComponent';
|
||||
export const ClsOrFunComponent = 'ClsOrFunComponent';
|
||||
export const DomPortal = 'DomPortal';
|
||||
export const DomComponent = 'DomComponent';
|
||||
export const DomText = 'DomText';
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"libs/*"
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "eslint . --ext .ts",
|
||||
"build": " webpack --config ./scripts/webpack/webpack.config.js",
|
||||
"build-3rdLib": "node ./scripts/gen3rdLib.js",
|
||||
"build-3rdLib-dev": "npm run build & node ./scripts/gen3rdLib.js --dev",
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
/* eslint-disable no-undef */
|
||||
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
|
||||
describe('useCallback Hook Test', () => {
|
||||
const { useState, useCallback } = React;
|
||||
const { useState, useCallback } = Horizon;
|
||||
|
||||
it('测试useCallback', () => {
|
||||
const App = (props) => {
|
||||
|
@ -18,7 +16,7 @@ describe('useCallback Hook Test', () => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
HorizonDOM.render(<App text={1} />, container);
|
||||
Horizon.render(<App text={1} />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||
// 点击按钮触发num加1
|
||||
container.querySelector('button').click();
|
||||
|
@ -27,7 +25,7 @@ describe('useCallback Hook Test', () => {
|
|||
container.querySelector('button').click();
|
||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||
|
||||
HorizonDOM.render(<App text={2} />, container);
|
||||
Horizon.render(<App text={2} />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||
// 依赖项有变化,点击按钮num增加
|
||||
container.querySelector('button').click();
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/* eslint-disable no-undef */
|
||||
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import { act } from '../../jest/customMatcher';
|
||||
|
||||
describe('useContext Hook Test', () => {
|
||||
const { useState, useContext } = React;
|
||||
const { unmountComponentAtNode } = HorizonDOM;
|
||||
const { useState, useContext } = Horizon;
|
||||
const { unmountComponentAtNode } = Horizon;
|
||||
|
||||
it('简单使用useContext', () => {
|
||||
const LanguageTypes = {
|
||||
|
@ -13,7 +11,7 @@ describe('useContext Hook Test', () => {
|
|||
JAVASCRIPT: 'JavaScript',
|
||||
};
|
||||
const defaultValue = { type: LanguageTypes.JAVASCRIPT };
|
||||
const SystemLanguageContext = React.createContext(defaultValue);
|
||||
const SystemLanguageContext = Horizon.createContext(defaultValue);
|
||||
|
||||
const SystemLanguageProvider = ({ type, children }) => {
|
||||
return (
|
||||
|
@ -38,11 +36,11 @@ describe('useContext Hook Test', () => {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
HorizonDOM.render(<TestFunction />, container);
|
||||
Horizon.render(<TestFunction />, container);
|
||||
// 测试当Provider未提供时,获取到的默认值'JavaScript'。
|
||||
expect(container.querySelector('p').innerHTML).toBe('JavaScript');
|
||||
unmountComponentAtNode(container);
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
// 测试当Provider提供时,可以获取到Provider的值'Java'。
|
||||
expect(container.querySelector('p').innerHTML).toBe('Java');
|
||||
// 测试当Provider改变时,可以获取到最新Provider的值。
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
/* eslint-disable no-undef */
|
||||
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../../jest/logUtils';
|
||||
import { act } from '../../jest/customMatcher';
|
||||
import Text from '../../jest/Text';
|
||||
|
||||
describe('useEffect Hook Test', () => {
|
||||
const {
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useState,
|
||||
memo,
|
||||
forwardRef
|
||||
} = React;
|
||||
const {
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useState,
|
||||
memo,
|
||||
forwardRef
|
||||
} = Horizon;
|
||||
|
||||
it('简单使用useEffect', () => {
|
||||
const App = () => {
|
||||
|
@ -27,7 +25,7 @@ describe('useEffect Hook Test', () => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(document.getElementById('p').style.display).toBe('block');
|
||||
// 点击按钮触发num加1
|
||||
container.querySelector('button').click();
|
||||
|
@ -42,7 +40,7 @@ describe('useEffect Hook Test', () => {
|
|||
}
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App />, container, () => {
|
||||
Horizon.render(<App />, container, () => {
|
||||
LogUtils.log('num effect');
|
||||
});
|
||||
// 第一次渲染为同步,所以同步执行的可以写在act里做判断
|
||||
|
@ -50,7 +48,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(container.textContent).toBe('op');
|
||||
});
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container, () => {
|
||||
Horizon.render(null, container, () => {
|
||||
LogUtils.log('num effect89');
|
||||
});
|
||||
// 第二次渲染为异步,所以同步执行的不可以写在act里做判断,act里拿到的为空数组
|
||||
|
@ -72,7 +70,7 @@ describe('useEffect Hook Test', () => {
|
|||
}
|
||||
const na = <NewApp />;
|
||||
// <App />必须设置key值,否则在diff的时候na会被视为不同组件
|
||||
HorizonDOM.render([<App key="app" />, na], container);
|
||||
Horizon.render([<App key="app" />, na], container);
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'App',
|
||||
'NewApp'
|
||||
|
@ -80,7 +78,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(container.textContent).toBe('AppNewApp');
|
||||
expect(LogUtils.getAndClear()).toEqual([]);
|
||||
// 在执行新的render前,会执行完上一次render的useEffect,所以LogUtils会加入'NewApp effect'。
|
||||
HorizonDOM.render([na], container);
|
||||
Horizon.render([na], container);
|
||||
expect(LogUtils.getAndClear()).toEqual(['NewApp effect']);
|
||||
expect(container.textContent).toBe('NewApp');
|
||||
expect(LogUtils.getAndClear()).toEqual([]);
|
||||
|
@ -104,7 +102,7 @@ describe('useEffect Hook Test', () => {
|
|||
return <Text text="NewApp" />;
|
||||
}
|
||||
// <App />必须设置key值,否则在diff的时候na会被视为不同组件
|
||||
HorizonDOM.render([<App key="app" />, <NewApp />], container);
|
||||
Horizon.render([<App key="app" />, <NewApp />], container);
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'App',
|
||||
'NewApp',
|
||||
|
@ -122,7 +120,7 @@ describe('useEffect Hook Test', () => {
|
|||
const App = () => {
|
||||
useLayoutEffect(() => {
|
||||
LogUtils.log('App Layout effect');
|
||||
HorizonDOM.render(<Text text="NewContainer" />, newContainer);
|
||||
Horizon.render(<Text text="NewContainer" />, newContainer);
|
||||
});
|
||||
return <Text text="App" />;
|
||||
}
|
||||
|
@ -133,7 +131,7 @@ describe('useEffect Hook Test', () => {
|
|||
return <Text text="NewApp" />;
|
||||
}
|
||||
// <App />必须设置key值,否则在diff的时候na会被视为不同组件
|
||||
HorizonDOM.render([<App key="app" />, <NewApp />], container);
|
||||
Horizon.render([<App key="app" />, <NewApp />], container);
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'App',
|
||||
'NewApp',
|
||||
|
@ -153,13 +151,13 @@ describe('useEffect Hook Test', () => {
|
|||
return <Text text={'num: ' + props.num} />;
|
||||
}
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']);
|
||||
expect(container.textContent).toEqual('num: 0');
|
||||
})
|
||||
expect(LogUtils.getAndClear()).toEqual(['First effect [0]']);
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
|
||||
})
|
||||
// 此时异步执行,act执行完后会执行新render的useEffect
|
||||
|
@ -182,13 +180,13 @@ describe('useEffect Hook Test', () => {
|
|||
return <Text text={'num: ' + props.num} />;
|
||||
}
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']);
|
||||
expect(container.textContent).toEqual('num: 0');
|
||||
})
|
||||
expect(LogUtils.getAndClear()).toEqual(['First effect [0]', 'Second effect [0]']);
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
})
|
||||
// 第二次render时异步执行,act保证所有效果都已更新,所以先常规记录日志
|
||||
// 然后记录useEffect的日志
|
||||
|
@ -231,7 +229,7 @@ describe('useEffect Hook Test', () => {
|
|||
}
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} word={'App'} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} word={'App'} />, container, () => LogUtils.log('callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'num: 0,word: App',
|
||||
'num Layouteffect [0]',
|
||||
|
@ -246,21 +244,21 @@ describe('useEffect Hook Test', () => {
|
|||
|
||||
act(() => {
|
||||
// 此时word改变,num不变
|
||||
HorizonDOM.render(<App num={0} word={'React'} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} word={'Horizon'} />, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'num: 0,word: React',
|
||||
'num: 0,word: Horizon',
|
||||
'word Layouteffect destroy',
|
||||
'word Layouteffect [React]',
|
||||
'word Layouteffect [Horizon]',
|
||||
'callback effect',
|
||||
// 最后执行异步的
|
||||
'word effect destroy',
|
||||
'word effect [React]',
|
||||
'word effect [Horizon]',
|
||||
]);
|
||||
|
||||
act(() => {
|
||||
// 此时num和word的所有effect都销毁
|
||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(null, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'num Layouteffect destroy',
|
||||
|
@ -284,7 +282,7 @@ describe('useEffect Hook Test', () => {
|
|||
}
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'num: 0',
|
||||
'callback effect'
|
||||
|
@ -296,7 +294,7 @@ describe('useEffect Hook Test', () => {
|
|||
]);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'num: 1',
|
||||
|
@ -309,7 +307,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(LogUtils.getAndClear()).toEqual([]);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(null, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'callback effect',
|
||||
|
@ -331,7 +329,7 @@ describe('useEffect Hook Test', () => {
|
|||
}
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'num: 0',
|
||||
'callback effect'
|
||||
|
@ -343,7 +341,7 @@ describe('useEffect Hook Test', () => {
|
|||
]);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'num: 1',
|
||||
|
@ -354,7 +352,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(LogUtils.getAndClear()).toEqual([]);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(null, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'callback effect',
|
||||
|
@ -367,7 +365,7 @@ describe('useEffect Hook Test', () => {
|
|||
it('useEffect里使用useState(1', () => {
|
||||
let setNum;
|
||||
const App = () => {
|
||||
const [num, _setNum] = React.useState(0);
|
||||
const [num, _setNum] = Horizon.useState(0);
|
||||
useEffect(() => {
|
||||
LogUtils.log(`num effect [${num}]`);
|
||||
setNum = () => _setNum(1);
|
||||
|
@ -382,7 +380,7 @@ describe('useEffect Hook Test', () => {
|
|||
}
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'num: 0',
|
||||
'num Layouteffect [0]',
|
||||
|
@ -415,7 +413,7 @@ describe('useEffect Hook Test', () => {
|
|||
return <Text text={'Num: ' + num} />;
|
||||
}
|
||||
|
||||
HorizonDOM.render(<App />, container, () => LogUtils.log('App callback effect'));
|
||||
Horizon.render(<App />, container, () => LogUtils.log('App callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual(['Num: 0', 'App callback effect']);
|
||||
expect(container.textContent).toEqual('Num: 0');
|
||||
act(() => {
|
||||
|
@ -445,7 +443,7 @@ describe('useEffect Hook Test', () => {
|
|||
return <Text text={num} />;
|
||||
})
|
||||
act(() => {
|
||||
HorizonDOM.render(<App />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App />, container, () => LogUtils.log('callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
0,
|
||||
'callback effect'
|
||||
|
@ -456,7 +454,7 @@ describe('useEffect Hook Test', () => {
|
|||
|
||||
// 不会重新渲染
|
||||
act(() => {
|
||||
HorizonDOM.render(<App />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App />, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual(['callback effect']);
|
||||
expect(container.textContent).toEqual('0');
|
||||
|
@ -475,7 +473,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(LogUtils.getAndClear()).toEqual([]);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(null, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'callback effect',
|
||||
|
@ -497,7 +495,7 @@ describe('useEffect Hook Test', () => {
|
|||
return <Text text={props.num} />;
|
||||
}, compare)
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
0,
|
||||
'callback effect'
|
||||
|
@ -508,7 +506,7 @@ describe('useEffect Hook Test', () => {
|
|||
|
||||
// 不会重新渲染
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual(['callback effect']);
|
||||
expect(container.textContent).toEqual('0');
|
||||
|
@ -516,7 +514,7 @@ describe('useEffect Hook Test', () => {
|
|||
|
||||
// 会重新渲染
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
1,
|
||||
|
@ -529,7 +527,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(LogUtils.getAndClear()).toEqual([]);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(null, container, () => LogUtils.log('callback effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual(['callback effect', 'num effect destroy 1']);
|
||||
expect(container.textContent).toEqual('');
|
||||
|
@ -550,7 +548,7 @@ describe('useEffect Hook Test', () => {
|
|||
return <Text text={'Number: ' + props.num} />;
|
||||
}
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () =>
|
||||
Horizon.render(<App num={0} />, container, () =>
|
||||
LogUtils.log('App callback effect'),
|
||||
);
|
||||
expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'App callback effect']);
|
||||
|
@ -560,7 +558,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(LogUtils.getAndClear()).toEqual(['throw Error']);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container, () =>
|
||||
Horizon.render(null, container, () =>
|
||||
LogUtils.log('App callback effect'),
|
||||
);
|
||||
});
|
||||
|
@ -592,14 +590,14 @@ describe('useEffect Hook Test', () => {
|
|||
}
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('num effect'));
|
||||
Horizon.render(<App num={0} />, container, () => LogUtils.log('num effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'num effect']);
|
||||
expect(container.textContent).toBe('Number: 0');
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual(['num effect [0]']);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container, () => LogUtils.log('num effect'));
|
||||
Horizon.render(null, container, () => LogUtils.log('num effect'));
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual(['num effect', 'num effect destroy 0']);
|
||||
expect(container.textContent).toBe('');
|
||||
|
@ -618,7 +616,7 @@ describe('useEffect Hook Test', () => {
|
|||
return <Text text={`Number: ${num}`} />;
|
||||
}
|
||||
|
||||
HorizonDOM.render(<App />, container, () => LogUtils.log('num effect'));
|
||||
Horizon.render(<App />, container, () => LogUtils.log('num effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'num effect']);
|
||||
expect(container.textContent).toBe('Number: 0');
|
||||
|
||||
|
@ -648,13 +646,13 @@ describe('useEffect Hook Test', () => {
|
|||
}
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App />, container, () => LogUtils.log('num effect'));
|
||||
Horizon.render(<App />, container, () => LogUtils.log('num effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual(['useEffect', 'num effect']);
|
||||
});
|
||||
expect(LogUtils.getAndClear()).toEqual(['effect']);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container);
|
||||
Horizon.render(null, container);
|
||||
});
|
||||
// 不会处理setNum(1)
|
||||
expect(LogUtils.getAndClear()).toEqual(['effect destroy']);
|
||||
|
@ -663,7 +661,7 @@ describe('useEffect Hook Test', () => {
|
|||
it('当组件的更新方法在卸载函数中,组件的子组件更新不会告警', () => {
|
||||
const App = () => {
|
||||
LogUtils.log('App');
|
||||
const appRef = React.createRef(null);
|
||||
const appRef = Horizon.createRef(null);
|
||||
useEffect(() => {
|
||||
LogUtils.log('App effect');
|
||||
return () => {
|
||||
|
@ -686,7 +684,7 @@ describe('useEffect Hook Test', () => {
|
|||
AppChild = forwardRef(AppChild);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App />, container, () => LogUtils.log('num effect'));
|
||||
Horizon.render(<App />, container, () => LogUtils.log('num effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'App',
|
||||
'AppChild',
|
||||
|
@ -696,7 +694,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(LogUtils.getAndClear()).toEqual(['Child effect', 'App effect']);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container);
|
||||
Horizon.render(null, container);
|
||||
});
|
||||
// 销毁时执行appRef.current(1)不会报错
|
||||
expect(LogUtils.getAndClear()).toEqual(['App effect destroy']);
|
||||
|
@ -722,7 +720,7 @@ describe('useEffect Hook Test', () => {
|
|||
}
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App />, container, () => LogUtils.log('num effect'));
|
||||
Horizon.render(<App />, container, () => LogUtils.log('num effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'App',
|
||||
'AppChild',
|
||||
|
@ -732,7 +730,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(LogUtils.getAndClear()).toEqual(['Child effect']);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container);
|
||||
Horizon.render(null, container);
|
||||
});
|
||||
// 销毁时执行 props.setNum(1);不会报错
|
||||
expect(LogUtils.getAndClear()).toEqual(['Child effect destroy']);
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
/* eslint-disable no-undef */
|
||||
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../../jest/logUtils';
|
||||
import { act } from '../../jest/customMatcher';
|
||||
import Text from '../../jest/Text';
|
||||
|
@ -10,9 +8,9 @@ describe('useImperativeHandle Hook Test', () => {
|
|||
useState,
|
||||
useImperativeHandle,
|
||||
forwardRef
|
||||
} = React;
|
||||
const { unmountComponentAtNode } = HorizonDOM;
|
||||
|
||||
} = Horizon;
|
||||
const { unmountComponentAtNode } = Horizon;
|
||||
|
||||
it('测试useImperativeHandle', () => {
|
||||
|
||||
let App = (props, ref) => {
|
||||
|
@ -28,9 +26,9 @@ describe('useImperativeHandle Hook Test', () => {
|
|||
|
||||
App = forwardRef(App);
|
||||
App1 = forwardRef(App1);
|
||||
const counter = React.createRef(null);
|
||||
const counter1 = React.createRef(null);
|
||||
HorizonDOM.render(<App ref={counter} />, container);
|
||||
const counter = Horizon.createRef(null);
|
||||
const counter1 = Horizon.createRef(null);
|
||||
Horizon.render(<App ref={counter} />, container);
|
||||
expect(counter.current.num).toBe(0);
|
||||
act(() => {
|
||||
counter.current.setNum(1);
|
||||
|
@ -40,7 +38,7 @@ describe('useImperativeHandle Hook Test', () => {
|
|||
// 清空container
|
||||
unmountComponentAtNode(container);
|
||||
|
||||
HorizonDOM.render(<App1 ref={counter1} />, container);
|
||||
Horizon.render(<App1 ref={counter1} />, container);
|
||||
expect(counter1.current.num1).toBe(0);
|
||||
act(() => {
|
||||
counter1.current.setNum1(1);
|
||||
|
@ -64,9 +62,9 @@ describe('useImperativeHandle Hook Test', () => {
|
|||
|
||||
App = forwardRef(App);
|
||||
App1 = forwardRef(App1);
|
||||
const counter = React.createRef(null);
|
||||
const counter1 = React.createRef(null);
|
||||
HorizonDOM.render(<App ref={counter} />, container);
|
||||
const counter = Horizon.createRef(null);
|
||||
const counter1 = Horizon.createRef(null);
|
||||
Horizon.render(<App ref={counter} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual([0]);
|
||||
expect(counter.current.num).toBe(0);
|
||||
act(() => {
|
||||
|
@ -78,7 +76,7 @@ describe('useImperativeHandle Hook Test', () => {
|
|||
// 清空container
|
||||
unmountComponentAtNode(container);
|
||||
|
||||
HorizonDOM.render(<App1 ref={counter1} />, container);
|
||||
Horizon.render(<App1 ref={counter1} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual([0]);
|
||||
expect(counter1.current.num1).toBe(0);
|
||||
act(() => {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
/* eslint-disable no-undef */
|
||||
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../../jest/logUtils';
|
||||
import { act } from '../../jest/customMatcher';
|
||||
import Text from '../../jest/Text';
|
||||
|
@ -10,7 +8,7 @@ describe('useLayoutEffect Hook Test', () => {
|
|||
useState,
|
||||
useEffect,
|
||||
useLayoutEffect
|
||||
} = React;
|
||||
} = Horizon;
|
||||
|
||||
it('简单使用useLayoutEffect', () => {
|
||||
const App = () => {
|
||||
|
@ -25,7 +23,7 @@ describe('useLayoutEffect Hook Test', () => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(document.getElementById('p').style.display).toBe('none');
|
||||
container.querySelector('button').click();
|
||||
expect(container.querySelector('p').style.display).toBe('inline');
|
||||
|
@ -38,7 +36,7 @@ describe('useLayoutEffect Hook Test', () => {
|
|||
});
|
||||
return <Text text={props.num} />;
|
||||
}
|
||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('Sync effect'));
|
||||
Horizon.render(<App num={1} />, container, () => LogUtils.log('Sync effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
1,
|
||||
// 同步在渲染之后
|
||||
|
@ -47,7 +45,7 @@ describe('useLayoutEffect Hook Test', () => {
|
|||
]);
|
||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||
// 更新
|
||||
HorizonDOM.render(<App num={2} />, container, () => LogUtils.log('Sync effect'));
|
||||
Horizon.render(<App num={2} />, container, () => LogUtils.log('Sync effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
2,
|
||||
'LayoutEffect',
|
||||
|
@ -74,7 +72,7 @@ describe('useLayoutEffect Hook Test', () => {
|
|||
}
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'num: 0',
|
||||
'num Layouteffect [0]',
|
||||
|
@ -85,7 +83,7 @@ describe('useLayoutEffect Hook Test', () => {
|
|||
|
||||
// 更新
|
||||
act(() => {
|
||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||
})
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
// 异步effect
|
||||
|
@ -103,7 +101,7 @@ describe('useLayoutEffect Hook Test', () => {
|
|||
]);
|
||||
|
||||
act(() => {
|
||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||
Horizon.render(null, container, () => LogUtils.log('callback effect'));
|
||||
})
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
// 同步Layouteffect销毁
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/* eslint-disable no-undef */
|
||||
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../../jest/logUtils';
|
||||
import Text from '../../jest/Text';
|
||||
|
||||
describe('useMemo Hook Test', () => {
|
||||
const { useMemo, useState } = React;
|
||||
const { useMemo, useState } = Horizon;
|
||||
|
||||
it('测试useMemo', () => {
|
||||
let setMemo;
|
||||
|
@ -24,7 +22,7 @@ describe('useMemo Hook Test', () => {
|
|||
</>
|
||||
);
|
||||
}
|
||||
HorizonDOM.render(<App words="App" />, container);
|
||||
Horizon.render(<App words="App" />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('App');
|
||||
expect(container.querySelector('#p').innerHTML).toBe('1');
|
||||
// 修改useMemo的依赖项,num会加一,text会改变。
|
||||
|
@ -49,26 +47,26 @@ describe('useMemo Hook Test', () => {
|
|||
}, [props._num]);
|
||||
return <Text text={num} />;
|
||||
}
|
||||
HorizonDOM.render(<App _num={0} />, container);
|
||||
Horizon.render(<App _num={0} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
0,
|
||||
1
|
||||
]);
|
||||
expect(container.textContent).toBe('1');
|
||||
|
||||
HorizonDOM.render(<App _num={1} />, container);
|
||||
Horizon.render(<App _num={1} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
1,
|
||||
2
|
||||
]);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
HorizonDOM.render(<App _num={1} />, container);
|
||||
Horizon.render(<App _num={1} />, container);
|
||||
// 不会触发useMemo
|
||||
expect(LogUtils.getAndClear()).toEqual([2]);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
HorizonDOM.render(<App _num={2} />, container);
|
||||
Horizon.render(<App _num={2} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
2,
|
||||
3
|
||||
|
@ -92,16 +90,16 @@ describe('useMemo Hook Test', () => {
|
|||
return 2;
|
||||
}
|
||||
|
||||
HorizonDOM.render(<App _num={num1} />, container);
|
||||
Horizon.render(<App _num={num1} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual(['num 1', 1]);
|
||||
|
||||
HorizonDOM.render(<App _num={num1} />, container);
|
||||
Horizon.render(<App _num={num1} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual(['num 1', 1]);
|
||||
|
||||
HorizonDOM.render(<App _num={num1} />, container);
|
||||
Horizon.render(<App _num={num1} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual(['num 1', 1]);
|
||||
|
||||
HorizonDOM.render(<App _num={num2} />, container);
|
||||
Horizon.render(<App _num={num2} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual(['num 2', 2]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
/* eslint-disable no-undef */
|
||||
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
|
||||
describe('useReducer Hook Test', () => {
|
||||
const { useReducer } = React;
|
||||
|
||||
const { useReducer } = Horizon;
|
||||
|
||||
it('简单使用useReducer', () => {
|
||||
const intlCar = { logo: '', price: 0 };
|
||||
let dispatch;
|
||||
|
@ -46,7 +44,7 @@ describe('useReducer Hook Test', () => {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('');
|
||||
expect(container.querySelector('#senP').innerHTML).toBe('0');
|
||||
// 触发bmw
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/* eslint-disable no-undef */
|
||||
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../../jest/logUtils';
|
||||
import Text from '../../jest/Text';
|
||||
|
||||
describe('useRef Hook Test', () => {
|
||||
const { useState, useRef } = React;
|
||||
const { useState, useRef } = Horizon;
|
||||
|
||||
it('测试useRef', () => {
|
||||
const App = () => {
|
||||
|
@ -22,7 +20,7 @@ describe('useRef Hook Test', () => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||
expect(container.querySelector('#sp').innerHTML).toBe('1');
|
||||
// 点击按钮触发num加1,ref不变
|
||||
|
@ -46,7 +44,7 @@ describe('useRef Hook Test', () => {
|
|||
)
|
||||
|
||||
}
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual([1]);
|
||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||
// 点击按钮触发ref.current加1
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
/* eslint-disable no-undef */
|
||||
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../../jest/logUtils';
|
||||
import { act } from '../../jest/customMatcher';
|
||||
import Text from '../../jest/Text';
|
||||
|
@ -11,7 +9,7 @@ describe('useState Hook Test', () => {
|
|||
forwardRef,
|
||||
useImperativeHandle,
|
||||
memo
|
||||
} = React;
|
||||
} = Horizon;
|
||||
|
||||
it('简单使用useState', () => {
|
||||
const App = () => {
|
||||
|
@ -23,7 +21,7 @@ describe('useState Hook Test', () => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||
// 点击按钮触发num加1
|
||||
container.querySelector('button').click();
|
||||
|
@ -45,7 +43,7 @@ describe('useState Hook Test', () => {
|
|||
</p>
|
||||
);
|
||||
}
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('00');
|
||||
container.querySelector('p').click();
|
||||
expect(container.querySelector('p').innerHTML).toBe('12');
|
||||
|
@ -67,7 +65,7 @@ describe('useState Hook Test', () => {
|
|||
</p>
|
||||
);
|
||||
}
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||
container.querySelector('p').click();
|
||||
expect(container.querySelector('p').innerHTML).toBe('2');
|
||||
|
@ -82,7 +80,7 @@ describe('useState Hook Test', () => {
|
|||
setNum = _setNum;
|
||||
return <Text text={num} />;
|
||||
}
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||
expect(LogUtils.getAndClear()).toEqual([0]);
|
||||
// useState修改state 时,设置相同的值,函数组件不会重新渲染
|
||||
|
@ -101,8 +99,8 @@ describe('useState Hook Test', () => {
|
|||
return <p>{num}</p>;
|
||||
|
||||
})
|
||||
const ref = React.createRef(null);
|
||||
HorizonDOM.render(<App initNum={1} ref={ref} />, container);
|
||||
const ref = Horizon.createRef(null);
|
||||
Horizon.render(<App initNum={1} ref={ref} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual([1]);
|
||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||
// 设置num为3
|
||||
|
@ -119,11 +117,11 @@ describe('useState Hook Test', () => {
|
|||
setNum = _setNum;
|
||||
return <Text text={num} />;
|
||||
})
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual([0]);
|
||||
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||
// 不会重新渲染
|
||||
HorizonDOM.render(<App />, container);
|
||||
Horizon.render(<App />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual([]);
|
||||
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||
// 会重新渲染
|
||||
|
@ -153,7 +151,7 @@ describe('useState Hook Test', () => {
|
|||
return <Text text={`Number: ${num}, Count: ${count}`} />;
|
||||
}
|
||||
|
||||
HorizonDOM.render(<App hasCount={true} />, container);
|
||||
Horizon.render(<App hasCount={true} />, container);
|
||||
expect(LogUtils.getAndClear()).toEqual(['Number: 0, Count: 0']);
|
||||
expect(container.textContent).toBe('Number: 0, Count: 0');
|
||||
act(() => {
|
||||
|
@ -164,7 +162,7 @@ describe('useState Hook Test', () => {
|
|||
expect(container.textContent).toBe('Number: 1, Count: 2');
|
||||
|
||||
expect(() => {
|
||||
HorizonDOM.render(<App hasCount={false} />, container);
|
||||
Horizon.render(<App hasCount={false} />, container);
|
||||
}).toThrow(
|
||||
'Hooks are less than expected, please check whether the hook is written in the condition.',
|
||||
);
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../jest/logUtils';
|
||||
import * as TestUtils from '../jest/testUtils';
|
||||
|
||||
describe('事件', () => {
|
||||
it('根节点挂载全量事件', () => {
|
||||
const App = () => {
|
||||
return <div />;
|
||||
}
|
||||
Horizon.render(<App />, container);
|
||||
console.log(TestUtils.getEventListeners(container));
|
||||
})
|
||||
|
||||
it('事件捕获与冒泡', () => {
|
||||
const App = () => {
|
||||
return (
|
||||
<>
|
||||
<div onClickCapture={() => LogUtils.log('div capture')} onClick={() => LogUtils.log('div bubble')}>
|
||||
<p onClickCapture={() => LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}>
|
||||
<button onClickCapture={() => LogUtils.log('btn capture')} onClick={() => LogUtils.log('btn bubble')} />
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Horizon.render(<App />, container);
|
||||
const a = container.querySelector('button');
|
||||
a.click();
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
// 从外到内先捕获再冒泡
|
||||
'div capture',
|
||||
'p capture',
|
||||
'btn capture',
|
||||
'btn bubble',
|
||||
'p bubble',
|
||||
'div bubble'
|
||||
]);
|
||||
})
|
||||
|
||||
it('returns 0', () => {
|
||||
let keyCode = null;
|
||||
const node = Horizon.render(
|
||||
<input
|
||||
onKeyPress={e => {
|
||||
keyCode = e.keyCode;
|
||||
}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
node.dispatchEvent(
|
||||
new KeyboardEvent('keypress', {
|
||||
keyCode: 65,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
expect(keyCode).toBe(65);
|
||||
});
|
||||
|
||||
it('阻止事件冒泡', () => {
|
||||
const App = () => {
|
||||
return (
|
||||
<>
|
||||
<div onClickCapture={() => LogUtils.log('div capture')} onClick={() => LogUtils.log('div bubble')}>
|
||||
<p onClickCapture={() => LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}>
|
||||
<button onClickCapture={() => LogUtils.log('btn capture')} onClick={(e) => TestUtils.stopBubbleOrCapture(e, 'btn bubble')} />
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Horizon.render(<App />, container);
|
||||
container.querySelector('button').click();
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
// 到button时停止冒泡
|
||||
'div capture',
|
||||
'p capture',
|
||||
'btn capture',
|
||||
'btn bubble'
|
||||
]);
|
||||
})
|
||||
|
||||
it('阻止事件捕获', () => {
|
||||
const App = () => {
|
||||
return (
|
||||
<>
|
||||
<div onClickCapture={(e) => TestUtils.stopBubbleOrCapture(e, 'div capture')} onClick={() => LogUtils.log('div bubble')}>
|
||||
<p onClickCapture={() => LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}>
|
||||
<button onClickCapture={() => LogUtils.log('btn capture')} onClick={() => LogUtils.log('btn bubble')} />
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Horizon.render(<App />, container);
|
||||
container.querySelector('button').click();
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
// 阻止捕获,不再继续向下执行
|
||||
'div capture'
|
||||
]);
|
||||
})
|
||||
|
||||
it('阻止原生事件冒泡', () => {
|
||||
const App = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<button />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Horizon.render(<App />, container);
|
||||
container.querySelector('div').addEventListener('click', () => {
|
||||
LogUtils.log('div bubble');
|
||||
}, false);
|
||||
container.querySelector('p').addEventListener('click', () => {
|
||||
LogUtils.log('p bubble');
|
||||
}, false);
|
||||
container.querySelector('button').addEventListener('click', (e) => {
|
||||
LogUtils.log('btn bubble');
|
||||
e.stopPropagation();
|
||||
}, false);
|
||||
container.querySelector('button').click();
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'btn bubble'
|
||||
]);
|
||||
})
|
||||
})
|
|
@ -0,0 +1,46 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../jest/logUtils';
|
||||
import { act } from '../jest/customMatcher';
|
||||
|
||||
describe('合成焦点事件', () => {
|
||||
|
||||
it('onFocus', () => {
|
||||
const realNode = Horizon.render(
|
||||
<input
|
||||
onFocus={event => LogUtils.log(`onFocus: ${event.type}`)}
|
||||
onFocusCapture={event => LogUtils.log(`onFocusCapture: ${event.type}`)}
|
||||
/>, container);
|
||||
|
||||
realNode.dispatchEvent(
|
||||
new FocusEvent('focusin', {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'onFocusCapture: focus',
|
||||
'onFocus: focus',
|
||||
]);
|
||||
});
|
||||
|
||||
it('onBlur', () => {
|
||||
const realNode = Horizon.render(
|
||||
<input
|
||||
onBlur={event => LogUtils.log(`onBlur: ${event.type}`)}
|
||||
onBlurCapture={event => LogUtils.log(`onBlurCapture: ${event.type}`)}
|
||||
/>, container);
|
||||
|
||||
realNode.dispatchEvent(
|
||||
new FocusEvent('focusout', {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'onBlurCapture: blur',
|
||||
'onBlur: blur',
|
||||
]);
|
||||
})
|
||||
})
|
|
@ -0,0 +1,179 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../jest/logUtils';
|
||||
|
||||
describe('Keyboard Event', () => {
|
||||
|
||||
it('keydown,keypress,keyup的keycode,charcode', () => {
|
||||
const node = Horizon.render(
|
||||
<input
|
||||
onKeyUp={(e) => {
|
||||
LogUtils.log('onKeyUp: keycode: ' + e.keyCode + ',charcode: ' + e.charCode);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
LogUtils.log('onKeyDown: keycode: ' + e.keyCode + ',charcode: ' + e.charCode)
|
||||
}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
node.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
keyCode: 50,
|
||||
code: 'Digit2',
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
node.dispatchEvent(
|
||||
new KeyboardEvent('keyup', {
|
||||
keyCode: 50,
|
||||
code: 'Digit2',
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'onKeyDown: keycode: 50,charcode: 0',
|
||||
'onKeyUp: keycode: 50,charcode: 0'
|
||||
]);
|
||||
});
|
||||
|
||||
it('keypress的keycode,charcode', () => {
|
||||
const node = Horizon.render(
|
||||
<input
|
||||
onKeyPress={(e) => {
|
||||
LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode);
|
||||
}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
node.dispatchEvent(
|
||||
new KeyboardEvent('keypress', {
|
||||
charCode: 50,
|
||||
code: 'Digit2',
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'onKeyPress: keycode: 0,charcode: 50'
|
||||
]);
|
||||
});
|
||||
|
||||
it('当charcode为13,且不设置keycode的时候', () => {
|
||||
const node = Horizon.render(
|
||||
<input
|
||||
onKeyPress={(e) => {
|
||||
LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode);
|
||||
}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
node.dispatchEvent(
|
||||
new KeyboardEvent('keypress', {
|
||||
charCode: 13,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'onKeyPress: keycode: 0,charcode: 13'
|
||||
]);
|
||||
});
|
||||
|
||||
it('keydown,keypress,keyup的code', () => {
|
||||
const node = Horizon.render(
|
||||
<input
|
||||
onKeyUp={(e) => {
|
||||
LogUtils.log('onKeyUp: code: ' + e.code);
|
||||
}}
|
||||
onKeyPress={(e) => {
|
||||
LogUtils.log('onKeyPress: code: ' + e.code);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
LogUtils.log('onKeyDown: code: ' + e.code);
|
||||
}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
node.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
code: 'Digit2',
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
|
||||
node.dispatchEvent(
|
||||
new KeyboardEvent('keypress', {
|
||||
keyCode: 50,
|
||||
code: 'Digit2',
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
|
||||
node.dispatchEvent(
|
||||
new KeyboardEvent('keyup', {
|
||||
code: 'Digit2',
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'onKeyDown: code: Digit2',
|
||||
'onKeyPress: code: Digit2',
|
||||
'onKeyUp: code: Digit2'
|
||||
]);
|
||||
});
|
||||
|
||||
it('可以执行preventDefault和 stopPropagation', () => {
|
||||
const keyboardProcessing = e => {
|
||||
expect(e.isDefaultPrevented()).toBe(false);
|
||||
e.preventDefault();
|
||||
expect(e.isDefaultPrevented()).toBe(true);
|
||||
|
||||
expect(e.isPropagationStopped()).toBe(false);
|
||||
e.stopPropagation();
|
||||
expect(e.isPropagationStopped()).toBe(true);
|
||||
LogUtils.log(e.type + ' handle');
|
||||
};
|
||||
const div = Horizon.render(
|
||||
<div
|
||||
onKeyDown={keyboardProcessing}
|
||||
onKeyUp={keyboardProcessing}
|
||||
onKeyPress={keyboardProcessing}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
|
||||
div.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
keyCode: 40,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
div.dispatchEvent(
|
||||
new KeyboardEvent('keyup', {
|
||||
keyCode: 40,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
div.dispatchEvent(
|
||||
new KeyboardEvent('keypress', {
|
||||
charCode: 40,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'keydown handle',
|
||||
'keyup handle',
|
||||
'keypress handle'
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,160 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../jest/logUtils';
|
||||
|
||||
describe('MouseEvent Test', () => {
|
||||
describe('onClick Test', () => {
|
||||
it('绑定this', () => {
|
||||
class App extends Horizon.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
num: this.props.num,
|
||||
price: this.props.price
|
||||
};
|
||||
}
|
||||
|
||||
setNum() {
|
||||
this.setState(
|
||||
{
|
||||
num: this.state.num + 1
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
setPrice = (e) => {
|
||||
this.setState(
|
||||
{
|
||||
num: this.state.price + 1
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<p>{this.state.num}</p>
|
||||
<p id="p">{this.state.price}</p>
|
||||
<button onClick={this.setNum.bind(this)} >button</button>
|
||||
<button id="btn" onClick={() => this.setPrice()} >button</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
Horizon.render(<App num={0} price={100} />, container);
|
||||
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||
expect(container.querySelector('#p').innerHTML).toBe('100');
|
||||
// 点击按钮触发num加1
|
||||
container.querySelector('button').click();
|
||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||
|
||||
container.querySelector('#btn').click();
|
||||
expect(container.querySelector('p').innerHTML).toBe('101');
|
||||
});
|
||||
|
||||
it('点击触发', () => {
|
||||
const handleClick = jest.fn();
|
||||
Horizon.render(<button onClick={handleClick}>Click Me</button>, container)
|
||||
container.querySelector('button').click();
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
for (let i = 0; i < 5; i++) {
|
||||
container.querySelector('button').click();
|
||||
}
|
||||
expect(handleClick).toHaveBeenCalledTimes(6);
|
||||
})
|
||||
})
|
||||
|
||||
const test = (name, config) => {
|
||||
const node = Horizon.render(config, container);
|
||||
let event = new MouseEvent(name, {
|
||||
relatedTarget: null,
|
||||
bubbles: true,
|
||||
screenX: 1
|
||||
});
|
||||
node.dispatchEvent(event);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
`${name} capture`,
|
||||
`${name} bubble`
|
||||
]);
|
||||
|
||||
event = new MouseEvent(name, {
|
||||
relatedTarget: null,
|
||||
bubbles: true,
|
||||
screenX: 2
|
||||
});
|
||||
node.dispatchEvent(event);
|
||||
|
||||
// 再次触发新事件
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
`${name} capture`,
|
||||
`${name} bubble`
|
||||
]);
|
||||
}
|
||||
|
||||
describe('合成鼠标事件', () => {
|
||||
it('onMouseMove', () => {
|
||||
const onMouseMove = () => {
|
||||
LogUtils.log('mousemove bubble');
|
||||
};
|
||||
const onMouseMoveCapture = () => {
|
||||
LogUtils.log('mousemove capture');
|
||||
};
|
||||
test('mousemove', <div
|
||||
onMouseMove={onMouseMove}
|
||||
onMouseMoveCapture={onMouseMoveCapture}
|
||||
/>)
|
||||
});
|
||||
|
||||
it('onMouseDown', () => {
|
||||
const onMousedown = () => {
|
||||
LogUtils.log('mousedown bubble');
|
||||
};
|
||||
const onMousedownCapture = () => {
|
||||
LogUtils.log('mousedown capture');
|
||||
};
|
||||
test('mousedown', <div
|
||||
onMousedown={onMousedown}
|
||||
onMousedownCapture={onMousedownCapture}
|
||||
/>)
|
||||
});
|
||||
|
||||
it('onMouseUp', () => {
|
||||
const onMouseUp = () => {
|
||||
LogUtils.log('mouseup bubble');
|
||||
};
|
||||
const onMouseUpCapture = () => {
|
||||
LogUtils.log('mouseup capture');
|
||||
};
|
||||
test('mouseup', <div
|
||||
onMouseUp={onMouseUp}
|
||||
onMouseUpCapture={onMouseUpCapture}
|
||||
/>)
|
||||
});
|
||||
|
||||
it('onMouseOut', () => {
|
||||
const onMouseOut = () => {
|
||||
LogUtils.log('mouseout bubble');
|
||||
};
|
||||
const onMouseOutCapture = () => {
|
||||
LogUtils.log('mouseout capture');
|
||||
};
|
||||
test('mouseout', <div
|
||||
onMouseOut={onMouseOut}
|
||||
onMouseOutCapture={onMouseOutCapture}
|
||||
/>)
|
||||
});
|
||||
|
||||
it('onMouseOver', () => {
|
||||
const onMouseOver = () => {
|
||||
LogUtils.log('mouseover bubble');
|
||||
};
|
||||
const onMouseOverCapture = () => {
|
||||
LogUtils.log('mouseover capture');
|
||||
};
|
||||
test('mouseover', <div
|
||||
onMouseOver={onMouseOver}
|
||||
onMouseOverCapture={onMouseOverCapture}
|
||||
/>)
|
||||
});
|
||||
})
|
||||
})
|
|
@ -0,0 +1,52 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../jest/logUtils';
|
||||
|
||||
describe('合成滚轮事件', () => {
|
||||
it('onWheel', () => {
|
||||
const realNode = Horizon.render(
|
||||
<div
|
||||
onWheel={event => LogUtils.log(`onWheel: ${event.type}`)}
|
||||
onWheelCapture={event => LogUtils.log(`onWheelCapture: ${event.type}`)}
|
||||
/>, container);
|
||||
|
||||
realNode.dispatchEvent(
|
||||
new MouseEvent('wheel', {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'onWheelCapture: wheel',
|
||||
'onWheel: wheel'
|
||||
]);
|
||||
});
|
||||
|
||||
it('可以执行preventDefault和 stopPropagation', () => {
|
||||
const eventHandler = e => {
|
||||
expect(e.isDefaultPrevented()).toBe(false);
|
||||
e.preventDefault();
|
||||
expect(e.isDefaultPrevented()).toBe(true);
|
||||
|
||||
expect(e.isPropagationStopped()).toBe(false);
|
||||
e.stopPropagation();
|
||||
expect(e.isPropagationStopped()).toBe(true);
|
||||
LogUtils.log(e.type + ' handle');
|
||||
};
|
||||
const realNode = Horizon.render(
|
||||
<div onWheel={eventHandler}/>,
|
||||
container
|
||||
);
|
||||
|
||||
realNode.dispatchEvent(
|
||||
new MouseEvent('wheel', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
'wheel handle'
|
||||
]);
|
||||
});
|
||||
|
||||
})
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from '../../../libs/horizon/src/external/Horizon';
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as LogUtils from '../jest/logUtils';
|
||||
|
||||
const Text = (props) => {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { allDelegatedNativeEvents } from '../../../libs/horizon/src/event/EventCollection';
|
||||
import * as LogUtils from './logUtils';
|
||||
|
||||
export const stopBubbleOrCapture = (e, value) => {
|
||||
LogUtils.log(value)
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
export const getEventListeners = (dom) => {
|
||||
let ret = true
|
||||
let keyArray = [];
|
||||
for (var key in dom) {
|
||||
keyArray.push(key);
|
||||
}
|
||||
try {
|
||||
allDelegatedNativeEvents.forEach(event => {
|
||||
if (!keyArray.includes(event)) {
|
||||
ret = false;
|
||||
throw new Error('没有挂载全量事件');
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
return ret;
|
||||
};
|
|
@ -45,11 +45,11 @@ const cjs = {
|
|||
...plugins,
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.join(__dirname, '../../libs/index.js'),
|
||||
from: path.join(__dirname, '../../libs/horizon/index.js'),
|
||||
to: path.join(__dirname, '../../build/horizon/index.js'),
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../../libs/package.json'),
|
||||
from: path.join(__dirname, '../../libs/horizon/package.json'),
|
||||
to: path.join(__dirname, '../../build/horizon/package.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"]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue