From 040b787e8f138759e2acfed4071ffa7cf70eea59 Mon Sep 17 00:00:00 2001 From: roymondchen Date: Tue, 14 Jan 2025 16:27:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(editor):=20=E4=BC=98=E5=8C=96=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E6=94=B6=E9=9B=86=EF=BC=8C=E6=95=B0=E6=8D=AE=E6=BA=90?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=9D=97=E5=88=86=E5=BC=80=E6=94=B6=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor/src/initService.ts | 631 +++++++++++++------------ packages/editor/src/services/dep.ts | 6 +- packages/editor/src/utils/idle-task.ts | 2 +- packages/utils/src/index.ts | 2 +- 4 files changed, 336 insertions(+), 305 deletions(-) diff --git a/packages/editor/src/initService.ts b/packages/editor/src/initService.ts index 52e020ba..cbc7ea24 100644 --- a/packages/editor/src/initService.ts +++ b/packages/editor/src/initService.ts @@ -1,4 +1,4 @@ -import { computed, onBeforeUnmount, reactive, toRaw, watch } from 'vue'; +import { onBeforeUnmount, reactive, toRaw, watch } from 'vue'; import { cloneDeep } from 'lodash-es'; import type TMagicCore from '@tmagic/core'; @@ -8,7 +8,7 @@ import type { EventOption, Id, MApp, - MNode, + MComponent, MPage, MPageFragment, } from '@tmagic/core'; @@ -22,7 +22,7 @@ import { Target, } from '@tmagic/core'; import { ChangeRecord } from '@tmagic/form'; -import { getNodes, isPage, isValueIncludeDataSource, traverseNode } from '@tmagic/utils'; +import { getNodes, isPage, isValueIncludeDataSource } from '@tmagic/utils'; import PropsPanel from './layouts/PropsPanel.vue'; import { isIncludeDataSource } from './utils/editor'; @@ -106,11 +106,12 @@ export const initServiceState = ( const eventsList: Record = {}; const methodsList: Record = {}; - eventMethodList && - Object.keys(eventMethodList).forEach((type: string) => { + if (eventMethodList) { + for (const type of Object.keys(eventMethodList)) { eventsList[type] = eventMethodList[type].events; methodsList[type] = eventMethodList[type].methods; - }); + } + } eventsService.setEvents(eventsList); eventsService.setMethods(methodsList); @@ -123,10 +124,13 @@ export const initServiceState = ( watch( () => props.datasourceConfigs, (configs) => { - configs && - Object.entries(configs).forEach(([key, value]) => { - dataSourceService.setFormConfig(key, value); - }); + if (!configs) { + return; + } + + for (const [key, value] of Object.entries(configs)) { + dataSourceService.setFormConfig(key, value); + } }, { immediate: true, @@ -136,10 +140,13 @@ export const initServiceState = ( watch( () => props.datasourceValues, (values) => { - values && - Object.entries(values).forEach(([key, value]) => { - dataSourceService.setFormValue(key, value); - }); + if (!values) { + return; + } + + for (const [key, value] of Object.entries(values)) { + dataSourceService.setFormValue(key, value); + } }, { immediate: true, @@ -152,18 +159,20 @@ export const initServiceState = ( const eventsList: Record = {}; const methodsList: Record = {}; - eventMethodList && - Object.keys(eventMethodList).forEach((type: string) => { + if (eventMethodList) { + for (const type of Object.keys(eventMethodList)) { eventsList[type] = eventMethodList[type].events; methodsList[type] = eventMethodList[type].methods; - }); + } + } - Object.entries(eventsList).forEach(([key, value]) => { + for (const [key, value] of Object.entries(eventsList)) { dataSourceService.setFormEvent(key, value); - }); - Object.entries(methodsList).forEach(([key, value]) => { + } + + for (const [key, value] of Object.entries(methodsList)) { dataSourceService.setFormMethod(key, value); - }); + } }, { immediate: true, @@ -203,6 +212,113 @@ export const initServiceEvents = ( ((event: 'update:modelValue', value: MApp | null) => void), { editorService, codeBlockService, dataSourceService, depService }: Services, ) => { + const getTMagicApp = () => { + const renderer = editorService.get('stage')?.renderer; + if (!renderer) { + return undefined; + } + + if (renderer.runtime) { + return renderer.runtime.getApp?.(); + } + + return new Promise((resolve) => { + // 设置 10s 超时 + const timeout = globalThis.setTimeout(() => { + resolve(undefined); + }, 10000); + + renderer.on('runtime-ready', () => { + if (timeout) { + globalThis.clearTimeout(timeout); + } + resolve(renderer.runtime?.getApp?.()); + }); + }); + }; + + const updateStageNodes = (nodes: MComponent[]) => { + for (const node of nodes) { + updateStageNode(node); + } + }; + + const updateStageNode = (node: MComponent) => { + const root = editorService.get('root'); + if (!root) return; + + return editorService.get('stage')?.update({ + config: cloneDeep(node), + parentId: editorService.getParentById(node.id)?.id, + root: cloneDeep(root), + }); + }; + + const updateDataSourceSchema = async () => { + const root = editorService.get('root'); + const app = await getTMagicApp(); + + if (!app || !root) { + return; + } + + if (app.dsl) { + app.dsl.dataSources = root.dataSources; + } + + if (root.dataSources) { + app.dataSourceManager?.updateSchema(root.dataSources); + } + }; + + const dsDepCollectedHandler = async () => { + const root = editorService.get('root'); + const app = await getTMagicApp(); + + if (root && app?.dsl) { + app.dsl.dataSourceDeps = root.dataSourceDeps; + } + }; + + const collectIdle = (nodes: MComponent[], deep: boolean, type?: DepTargetType) => + Promise.all( + nodes.map((node) => { + let pageId: Id | undefined; + + if (isPage(node)) { + pageId = node.id; + } else { + const info = editorService.getNodeInfo(node.id); + pageId = info.page?.id; + } + return depService.collectIdle([node], { pageId }, deep, type); + }), + ); + + watch( + () => editorService.get('stage'), + (stage) => { + if (!stage) { + return; + } + + stage.on('rerender', async () => { + const node = editorService.get('node'); + + if (!node) return; + + await collectIdle([node], true, DepTargetType.DATA_SOURCE); + updateStageNode(node); + }); + }, + ); + + const initDataSourceDepTarget = (ds: DataSourceSchema) => { + depService.addTarget(createDataSourceTarget(ds, reactive({}))); + depService.addTarget(createDataSourceMethodTarget(ds, reactive({}))); + depService.addTarget(createDataSourceCondTarget(ds, reactive({}))); + }; + const rootChangeHandler = async (value: MApp | null, preValue?: MApp | null) => { if (!value) return; @@ -214,17 +330,19 @@ export const initServiceEvents = ( depService.removeTargets(DepTargetType.CODE_BLOCK); - Object.entries(value.codeBlocks).forEach(([id, code]) => { + for (const [id, code] of Object.entries(value.codeBlocks)) { depService.addTarget(createCodeBlockTarget(id, code)); - }); + } - dataSourceService.get('dataSources').forEach((ds) => { + for (const ds of dataSourceService.get('dataSources')) { initDataSourceDepTarget(ds); - }); + } if (Array.isArray(value.items)) { depService.clearIdleTasks(); - collectIdle(value.items, true); + collectIdle(value.items, true).then(() => { + updateStageNodes(value.items); + }); } else { depService.clear(); delete value.dataSourceDeps; @@ -252,61 +370,203 @@ export const initServiceEvents = ( } }; - const stage = computed(() => editorService.get('stage')); + // 新增节点,收集依赖 + const nodeAddHandler = async (nodes: MComponent[]) => { + await collectIdle(nodes, true); - watch(stage, (stage) => { - if (!stage) { - return; - } - - stage.on('rerender', () => { - const node = editorService.get('node'); - - if (!node) return; - - collectIdle([node], true); - updateStage([node]); - }); - }); - - const getApp = () => { - const renderer = stage.value?.renderer; - if (!renderer) { - return undefined; - } - - if (renderer.runtime) { - return renderer.runtime.getApp?.(); - } - - return new Promise((resolve) => { - const timeout = globalThis.setTimeout(() => { - resolve(undefined); - }, 10000); - - renderer.on('runtime-ready', () => { - if (timeout) { - globalThis.clearTimeout(timeout); - } - resolve(renderer.runtime?.getApp?.()); - }); - }); + updateStageNodes(nodes); }; - const updateDataSourceSchema = async () => { - const root = editorService.get('root'); - const app = await getApp(); + // 节点更新,收集依赖 + // 仅当修改到数据源相关的才收集 + const nodeUpdateHandler = async ( + data: { newNode: MComponent; oldNode: MComponent; changeRecords?: ChangeRecord[] }[], + ) => { + const needRecollectNodes: MComponent[] = []; + const normalNodes: MComponent[] = []; + for (const { newNode, oldNode, changeRecords } of data) { + if (changeRecords?.length) { + // eslint-disable-next-line no-restricted-syntax + forChangeRecords: for (const record of changeRecords) { + if (!record.propPath) { + needRecollectNodes.push(newNode); + break forChangeRecords; + } - if (root && app?.dsl) { - app.dsl.dataSourceDeps = root.dataSourceDeps; - app.dsl.dataSources = root.dataSources; + // NODE_CONDS_KEY为显示条件key + if ( + new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond`).test(record.propPath) || + new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond.(\\d)+.value`).test(record.propPath) || + record.propPath === NODE_CONDS_KEY || + isValueIncludeDataSource(record.value) + ) { + needRecollectNodes.push(newNode); + break forChangeRecords; + } + + // 修改的key在收集的依赖中,则需要触发重新收集 + for (const target of Object.values(depService.getTargets(DepTargetType.DATA_SOURCE))) { + if (!target.deps[newNode.id]) { + continue; + } + if (target.deps[newNode.id].keys.includes(record.propPath)) { + needRecollectNodes.push(newNode); + break forChangeRecords; + } + } + + normalNodes.push(newNode); + } + } else if (isIncludeDataSource(newNode, oldNode)) { + needRecollectNodes.push(newNode); + } else { + normalNodes.push(newNode); + } } - if (root?.dataSources) { + if (needRecollectNodes.length) { + // 有数据源依赖,需要等依赖重新收集完才更新stage + await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE); + await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE_COND); + updateStageNodes(needRecollectNodes); + } else { + updateStageNodes(normalNodes); + // 在上面判断是否需要收集数据源依赖中已经更新stage + Promise.all([ + collectIdle(normalNodes, true, DepTargetType.CODE_BLOCK), + collectIdle(normalNodes, true, DepTargetType.DATA_SOURCE_METHOD), + ]); + } + }; + + // 节点删除,清除对齐的依赖收集 + const nodeRemoveHandler = (nodes: MComponent[]) => { + depService.clear(nodes); + }; + + // 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖 + const historyChangeHandler = async (page: MPage | MPageFragment) => { + await collectIdle([page], true); + updateStageNode(page); + }; + + editorService.on('history-change', historyChangeHandler); + editorService.on('root-change', rootChangeHandler); + editorService.on('add', nodeAddHandler); + editorService.on('remove', nodeRemoveHandler); + editorService.on('update', nodeUpdateHandler); + + const dataSourceAddHandler = async (config: DataSourceSchema) => { + initDataSourceDepTarget(config); + const app = await getTMagicApp(); + app?.dataSourceManager?.addDataSource(config); + }; + + const dataSourceUpdateHandler = async ( + config: DataSourceSchema, + { changeRecords }: { changeRecords: ChangeRecord[] }, + ) => { + let needRecollectDep = false; + let isModifyField = false; + let isModifyMock = false; + let isModifyMethod = false; + for (const changeRecord of changeRecords) { + if (!changeRecord.propPath) { + continue; + } + + isModifyField = + changeRecord.propPath === 'fields' || + /fields.(\d)+.name/.test(changeRecord.propPath) || + /fields.(\d)+$/.test(changeRecord.propPath); + + isModifyMock = changeRecord.propPath === 'mocks'; + + isModifyMethod = + changeRecord.propPath === 'methods' || + /methods.(\d)+.name/.test(changeRecord.propPath) || + /methods.(\d)+$/.test(changeRecord.propPath); + + needRecollectDep = isModifyField || isModifyMock || isModifyMethod; + + if (needRecollectDep) { + break; + } + } + + const root = editorService.get('root'); + if (needRecollectDep) { + if (Array.isArray(root?.items)) { + depService.clearIdleTasks(); + + removeDataSourceTarget(config.id); + initDataSourceDepTarget(config); + + let collectIdlePromises: Promise[] = []; + if (isModifyField) { + collectIdlePromises = [ + collectIdle(root.items, true, DepTargetType.DATA_SOURCE), + collectIdle(root.items, true, DepTargetType.DATA_SOURCE_COND), + ]; + } else if (isModifyMock) { + collectIdlePromises = [collectIdle(root.items, true, DepTargetType.DATA_SOURCE)]; + } else if (isModifyMethod) { + collectIdlePromises = [collectIdle(root.items, true, DepTargetType.DATA_SOURCE_METHOD)]; + } + Promise.all(collectIdlePromises).then(() => { + updateDataSourceSchema(); + updateStageNodes(root.items); + }); + } + } else if (root?.dataSources) { + const app = await getTMagicApp(); app?.dataSourceManager?.updateSchema(root.dataSources); } }; + const removeDataSourceTarget = (id: string) => { + depService.removeTarget(id, DepTargetType.DATA_SOURCE); + depService.removeTarget(id, DepTargetType.DATA_SOURCE_COND); + depService.removeTarget(id, DepTargetType.DATA_SOURCE_METHOD); + }; + + const dataSourceRemoveHandler = (id: string) => { + const root = editorService.get('root'); + const nodeIds = Object.keys(root?.dataSourceDeps?.[id] || {}); + const nodes = getNodes(nodeIds, root?.items); + + Promise.all([ + collectIdle(nodes, false, DepTargetType.DATA_SOURCE), + collectIdle(nodes, false, DepTargetType.DATA_SOURCE_COND), + collectIdle(nodes, false, DepTargetType.DATA_SOURCE_METHOD), + ]).then(() => { + updateDataSourceSchema(); + updateStageNodes(nodes); + }); + + removeDataSourceTarget(id); + }; + + dataSourceService.on('add', dataSourceAddHandler); + dataSourceService.on('update', dataSourceUpdateHandler); + dataSourceService.on('remove', dataSourceRemoveHandler); + + const codeBlockAddOrUpdateHandler = (id: Id, codeBlock: CodeBlockContent) => { + if (depService.hasTarget(id, DepTargetType.CODE_BLOCK)) { + depService.getTarget(id, DepTargetType.CODE_BLOCK)!.name = codeBlock.name; + return; + } + + depService.addTarget(createCodeBlockTarget(id, codeBlock)); + }; + + const codeBlockRemoveHandler = (id: Id) => { + depService.removeTarget(id, DepTargetType.CODE_BLOCK); + }; + + codeBlockService.on('addOrUpdate', codeBlockAddOrUpdateHandler); + codeBlockService.on('remove', codeBlockRemoveHandler); + const targetAddHandler = (target: Target) => { const root = editorService.get('root'); if (!root) return; @@ -340,239 +600,10 @@ export const initServiceEvents = ( } }; - /** - * 修改dsl后会执行依赖收集并调用此方法 - * 所以这个方法是会被执行两次,当时updateStage应当时一次,所以通过inDeps参数来区分调用时机 - * inDeps为true是则更新依赖收集到的节点,否则则更新没有依赖的节点 - * @param nodes 需要更新的节点 - * @param inDeps 是否依赖收集完成事件中执行的 - * @returns - */ - const updateStage = (nodes: MNode[], inDeps = false) => { - const root = editorService.get('root'); - if (!root) return; - - const update = (node: MNode) => - stage.value?.update({ - config: cloneDeep(node), - parentId: editorService.getParentById(node.id)?.id, - root: cloneDeep(root), - }); - - nodes.forEach((node) => { - const inDepsNodeId: Id[] = []; - const deps = Object.values(root.dataSourceDeps || {}); - deps.forEach((dep) => { - Object.keys(dep).forEach((id) => { - inDepsNodeId.push(id); - }); - }); - - if (inDeps) { - if (inDepsNodeId.includes(node.id)) { - update(node); - } - } else { - if (!inDepsNodeId.includes(node.id)) { - update(node); - } - } - }); - }; - - const dsDepCollectedHandler = async (nodes: MNode[], deep: boolean) => { - const root = editorService.get('root'); - if (!root) return; - const app = await getApp(); - if (app?.dsl) { - app.dsl.dataSourceDeps = root.dataSourceDeps; - } - if (deep) { - nodes.forEach((node) => { - traverseNode( - node, - (node) => { - updateStage([node], true); - }, - [], - true, - ); - }); - } else { - updateStage(nodes, true); - } - }; - depService.on('add-target', targetAddHandler); depService.on('remove-target', targetRemoveHandler); depService.on('ds-collected', dsDepCollectedHandler); - const initDataSourceDepTarget = (ds: DataSourceSchema) => { - depService.addTarget(createDataSourceTarget(ds, reactive({}))); - depService.addTarget(createDataSourceMethodTarget(ds, reactive({}))); - depService.addTarget(createDataSourceCondTarget(ds, reactive({}))); - }; - - const collectIdle = (nodes: MNode[], deep: boolean) => - Promise.all( - nodes.map((node) => { - let pageId: Id | undefined; - - if (isPage(node)) { - pageId = node.id; - } else { - const info = editorService.getNodeInfo(node.id); - pageId = info.page?.id; - } - return depService.collectIdle([node], { pageId }, deep); - }), - ); - - // 新增节点,收集依赖 - const nodeAddHandler = (nodes: MNode[]) => { - collectIdle(nodes, true); - - updateStage(nodes); - }; - - // 节点更新,收集依赖 - // 仅当修改到数据源相关的才收集 - const nodeUpdateHandler = (data: { newNode: MNode; oldNode: MNode; changeRecords?: ChangeRecord[] }[]) => { - const needRecollectNodes: MNode[] = []; - const normalNodes: MNode[] = []; - data.forEach(({ newNode, oldNode, changeRecords }) => { - if (changeRecords?.length) { - for (const record of changeRecords) { - // NODE_CONDS_KEY为显示条件key - if ( - !record.propPath || - new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond`).test(record.propPath) || - new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond.(\\d)+.value`).test(record.propPath) || - record.propPath === NODE_CONDS_KEY || - isValueIncludeDataSource(record.value) - ) { - needRecollectNodes.push(newNode); - } else { - normalNodes.push(newNode); - } - } - } else if (isIncludeDataSource(newNode, oldNode)) { - needRecollectNodes.push(newNode); - } else { - normalNodes.push(newNode); - } - }); - - if (needRecollectNodes.length) { - collectIdle(needRecollectNodes, true); - updateStage(needRecollectNodes); - } else if (normalNodes.length) { - updateStage(normalNodes); - } - }; - - // 节点删除,清除对齐的依赖收集 - const nodeRemoveHandler = (nodes: MNode[]) => { - depService.clear(nodes); - }; - - // 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖 - const historyChangeHandler = (page: MPage | MPageFragment) => { - collectIdle([page], true).then(() => { - updateDataSourceSchema(); - }); - }; - - editorService.on('history-change', historyChangeHandler); - editorService.on('root-change', rootChangeHandler); - editorService.on('add', nodeAddHandler); - editorService.on('remove', nodeRemoveHandler); - editorService.on('update', nodeUpdateHandler); - - const codeBlockAddOrUpdateHandler = (id: Id, codeBlock: CodeBlockContent) => { - if (depService.hasTarget(id, DepTargetType.CODE_BLOCK)) { - depService.getTarget(id, DepTargetType.CODE_BLOCK)!.name = codeBlock.name; - return; - } - - depService.addTarget(createCodeBlockTarget(id, codeBlock)); - }; - - const codeBlockRemoveHandler = (id: Id) => { - depService.removeTarget(id, DepTargetType.CODE_BLOCK); - }; - - codeBlockService.on('addOrUpdate', codeBlockAddOrUpdateHandler); - codeBlockService.on('remove', codeBlockRemoveHandler); - - const dataSourceAddHandler = async (config: DataSourceSchema) => { - initDataSourceDepTarget(config); - const app = await getApp(); - app?.dataSourceManager?.addDataSource(config); - }; - - const dataSourceUpdateHandler = async ( - config: DataSourceSchema, - { changeRecords }: { changeRecords: ChangeRecord[] }, - ) => { - let needRecollectDep = false; - for (const changeRecord of changeRecords) { - if (!changeRecord.propPath) { - continue; - } - - needRecollectDep = - changeRecord.propPath === 'fields' || - changeRecord.propPath === 'methods' || - /fields.(\d)+.name/.test(changeRecord.propPath) || - /fields.(\d)+$/.test(changeRecord.propPath) || - /methods.(\d)+.name/.test(changeRecord.propPath) || - /methods.(\d)+$/.test(changeRecord.propPath); - - if (needRecollectDep) { - break; - } - } - - const root = editorService.get('root'); - if (needRecollectDep) { - if (Array.isArray(root?.items)) { - depService.clearIdleTasks(); - - removeDataSourceTarget(config.id); - initDataSourceDepTarget(config); - - collectIdle(root.items, true).then(() => { - updateDataSourceSchema(); - }); - } - } else if (root?.dataSources) { - const app = await getApp(); - app?.dataSourceManager?.updateSchema(root.dataSources); - } - }; - - const removeDataSourceTarget = (id: string) => { - depService.removeTarget(id, DepTargetType.DATA_SOURCE); - depService.removeTarget(id, DepTargetType.DATA_SOURCE_COND); - depService.removeTarget(id, DepTargetType.DATA_SOURCE_METHOD); - }; - - const dataSourceRemoveHandler = (id: string) => { - const root = editorService.get('root'); - const nodeIds = Object.keys(root?.dataSourceDeps?.[id] || {}); - const nodes = getNodes(nodeIds, root?.items); - collectIdle(nodes, false).then(() => { - updateDataSourceSchema(); - }); - - removeDataSourceTarget(id); - }; - - dataSourceService.on('add', dataSourceAddHandler); - dataSourceService.on('update', dataSourceUpdateHandler); - dataSourceService.on('remove', dataSourceRemoveHandler); - onBeforeUnmount(() => { depService.off('add-target', targetAddHandler); depService.off('remove-target', targetRemoveHandler); diff --git a/packages/editor/src/services/dep.ts b/packages/editor/src/services/dep.ts index 47ddba97..47af5070 100644 --- a/packages/editor/src/services/dep.ts +++ b/packages/editor/src/services/dep.ts @@ -119,10 +119,10 @@ class Dep extends BaseService { idleTask.once('finish', () => { this.emit('collected', nodes, deep); this.set('collecting', false); - resolve(); }); idleTask.once('hight-level-finish', () => { this.emit('ds-collected', nodes, deep); + resolve(); }); }); } @@ -130,11 +130,11 @@ class Dep extends BaseService { public collectNode(node: MNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) { // 先删除原有依赖,重新收集 if (isPage(node)) { - Object.entries(target.deps).forEach(([depKey, dep]) => { + for (const [depKey, dep] of Object.entries(target.deps)) { if (dep.data?.pageId && dep.data.pageId === depExtendedData.pageId) { delete target.deps[depKey]; } - }); + } } else { this.watcher.removeTargetDep(target, node); } diff --git a/packages/editor/src/utils/idle-task.ts b/packages/editor/src/utils/idle-task.ts index 08e70b0e..876b8e97 100644 --- a/packages/editor/src/utils/idle-task.ts +++ b/packages/editor/src/utils/idle-task.ts @@ -80,7 +80,7 @@ export class IdleTask extends EventEmitter { // 动画会占用空闲时间,当任务一直无法执行时,看看是否有动画正在播放 // 根据空闲时间的多少来决定执行的任务数,保证页面不卡死的情况下尽量多执行任务,不然当任务数巨大时,执行时间会很久 // 执行不完不会影响配置,但是会影响画布渲染 - while (deadline.timeRemaining() > 0 && taskList.length) { + while (deadline.timeRemaining() > 0 && (taskList.length || hightLevelTaskList.length)) { const timeRemaining = deadline.timeRemaining(); let times = 0; if (timeRemaining <= 5) { diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 96837571..0a44b016 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -508,7 +508,7 @@ export const traverseNode = ( if (Array.isArray(node.items) && node.items.length) { parents.push(node); node.items.forEach((item) => { - traverseNode(item as T, cb, [...parents]); + traverseNode(item as T, cb, [...parents], evalCbAfter); }); }