Skip to content

Commit

Permalink
fix(editor): 修复修改页面配置后可能改造页面卡死问题
Browse files Browse the repository at this point in the history
  • Loading branch information
roymondchen authored and jia000 committed Dec 9, 2024
1 parent 5dad9f0 commit fc38fc3
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 113 deletions.
164 changes: 94 additions & 70 deletions packages/editor/src/initService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { onBeforeUnmount, reactive, toRaw, watch } from 'vue';
import { cloneDeep, debounce } from 'lodash-es';
import { cloneDeep } from 'lodash-es';

import type {
CodeBlockContent,
Expand All @@ -19,7 +19,7 @@ import {
DepTargetType,
Target,
} from '@tmagic/core';
import { isPage, traverseNode } from '@tmagic/utils';
import { getNodes, isPage, traverseNode } from '@tmagic/utils';

import PropsPanel from './layouts/PropsPanel.vue';
import { EditorProps } from './editorProps';
Expand Down Expand Up @@ -252,63 +252,24 @@ export const initServiceEvents = (
return stage?.renderer?.runtime?.getApp?.();
};

const updateDataSourceSchema = () => {
const root = editorService.get('root');

if (root?.dataSources) {
getApp()?.dataSourceManager?.updateSchema(root.dataSources);
}
};

const targetAddHandler = (target: Target) => {
const root = editorService.get('root');
if (!root) return;

if (target.type === DepTargetType.DATA_SOURCE) {
if (!root.dataSourceDeps) {
root.dataSourceDeps = {};
}
root.dataSourceDeps[target.id] = target.deps;
}

if (target.type === DepTargetType.DATA_SOURCE_COND) {
if (!root.dataSourceCondDeps) {
root.dataSourceCondDeps = {};
}
root.dataSourceCondDeps[target.id] = target.deps;
}
};

const targetRemoveHandler = (id: string | number) => {
const updateDataSourceSchema = (nodes: MNode[], deep: boolean) => {
const root = editorService.get('root');
const app = getApp();

if (root?.dataSourceDeps) {
delete root.dataSourceDeps[id];
if (root && app?.dsl) {
app.dsl.dataSourceDeps = root.dataSourceDeps;
app.dsl.dataSourceCondDeps = root.dataSourceCondDeps;
app.dsl.dataSources = root.dataSources;
}

if (root?.dataSourceCondDeps) {
delete root.dataSourceCondDeps[id];
if (root?.dataSources) {
getApp()?.dataSourceManager?.updateSchema(root.dataSources);
}
};

const collectedHandler = debounce((nodes: MNode[], deep: boolean) => {
const root = editorService.get('root');
const stage = editorService.get('stage');

if (!root || !stage) return;

const app = getApp();

if (!app) return;

if (app.dsl) {
app.dsl.dataSourceDeps = root.dataSourceDeps;
app.dsl.dataSourceCondDeps = root.dataSourceCondDeps;
app.dsl.dataSources = root.dataSources;
}

updateDataSourceSchema();

const allNodes: MNode[] = [];

if (deep) {
Expand All @@ -335,40 +296,94 @@ export const initServiceEvents = (
});
});
});
}, 300);
};

const afterUpdateNodes = (nodes: MNode[]) => {
const root = editorService.get('root');
if (!root) return;
const stage = editorService.get('stage');
const app = getApp();
if (app?.dsl) {
app.dsl.dataSourceDeps = root.dataSourceDeps;
}
for (const node of nodes) {
stage?.update({
config: cloneDeep(node),
parentId: editorService.getParentById(node.id)?.id,
root: cloneDeep(root),
});
}
};

const targetAddHandler = (target: Target) => {
const root = editorService.get('root');
if (!root) return;

if (target.type === DepTargetType.DATA_SOURCE) {
if (!root.dataSourceDeps) {
root.dataSourceDeps = {};
}
root.dataSourceDeps[target.id] = target.deps;
}

if (target.type === DepTargetType.DATA_SOURCE_COND) {
if (!root.dataSourceCondDeps) {
root.dataSourceCondDeps = {};
}
root.dataSourceCondDeps[target.id] = target.deps;
}
};

const targetRemoveHandler = (id: string | number) => {
const root = editorService.get('root');

if (!root) return;

if (root.dataSourceDeps) {
delete root.dataSourceDeps[id];
}

if (root.dataSourceCondDeps) {
delete root.dataSourceCondDeps[id];
}
};

depService.on('add-target', targetAddHandler);
depService.on('remove-target', targetRemoveHandler);
depService.on('collected', collectedHandler);

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) => {
nodes.forEach((node) => {
let pageId: Id | undefined;

if (isPage(node)) {
pageId = node.id;
} else {
const info = editorService.getNodeInfo(node.id);
pageId = info.page?.id;
}
depService.collectIdle([node], { pageId }, deep);
});
};
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);
collectIdle(nodes, true).then(() => {
afterUpdateNodes(nodes);
});
};

// 节点更新,收集依赖
const nodeUpdateHandler = (nodes: MNode[]) => {
collectIdle(nodes, true);
collectIdle(nodes, true).then(() => {
afterUpdateNodes(nodes);
});
};

// 节点删除,清除对齐的依赖收集
Expand All @@ -378,7 +393,9 @@ export const initServiceEvents = (

// 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖
const historyChangeHandler = (page: MPage | MPageFragment) => {
collectIdle([page], true);
collectIdle([page], true).then(() => {
updateDataSourceSchema([page], true);
});
};

editorService.on('history-change', historyChangeHandler);
Expand Down Expand Up @@ -413,7 +430,9 @@ export const initServiceEvents = (
removeDataSourceTarget(config.id);
initDataSourceDepTarget(config);

collectIdle(root?.items || [], true);
collectIdle(root?.items || [], true).then(() => {
updateDataSourceSchema(root?.items || [], true);
});
};

const removeDataSourceTarget = (id: string) => {
Expand All @@ -423,8 +442,14 @@ export const initServiceEvents = (
};

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(nodes, false);
});

removeDataSourceTarget(id);
getApp()?.dataSourceManager?.removeDataSource(id);
};

dataSourceService.on('add', dataSourceAddHandler);
Expand All @@ -434,7 +459,6 @@ export const initServiceEvents = (
onBeforeUnmount(() => {
depService.off('add-target', targetAddHandler);
depService.off('remove-target', targetRemoveHandler);
depService.off('collected', collectedHandler);

editorService.off('history-change', historyChangeHandler);
editorService.off('root-change', rootChangeHandler);
Expand Down
11 changes: 11 additions & 0 deletions packages/editor/src/layouts/workspace/viewer/Stage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,20 @@ watch(zoom, (zoom) => {
stage.setZoom(zoom);
});
let timeoutId: NodeJS.Timeout | null = null;
watch(page, (page) => {
if (runtime && page) {
services?.editorService.set('stageLoading', true);
if (timeoutId) {
globalThis.clearTimeout(timeoutId);
}
timeoutId = globalThis.setTimeout(() => {
services?.editorService.set('stageLoading', false);
timeoutId = null;
}, 3000);
runtime.updatePageId?.(page.id);
nextTick(() => {
stage?.select(page.id);
Expand Down
7 changes: 5 additions & 2 deletions packages/editor/src/services/dep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@ class Dep extends BaseService {
);
});

idleTask.once('finish', () => {
this.emit('collected', nodes, deep);
return new Promise<void>((resolve) => {
idleTask.once('finish', () => {
this.emit('collected', nodes, deep);
resolve();
});
});
}

Expand Down
56 changes: 18 additions & 38 deletions packages/editor/src/services/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,19 @@ import { reactive, toRaw } from 'vue';
import { cloneDeep, get, isObject, mergeWith, uniq } from 'lodash-es';
import type { Writable } from 'type-fest';

import type { Id, MApp, MComponent, MContainer, MNode, MPage, MPageFragment, TargetOptions } from '@tmagic/core';
import type { Id, MApp, MContainer, MNode, MPage, MPageFragment, TargetOptions } from '@tmagic/core';
import { NodeType, Target, Watcher } from '@tmagic/core';
import { isFixed } from '@tmagic/stage';
import { calcValueByFontsize, getElById, getNodePath, isNumber, isPage, isPageFragment, isPop } from '@tmagic/utils';
import {
calcValueByFontsize,
getElById,
getNodeInfo,
getNodePath,
isNumber,
isPage,
isPageFragment,
isPop,
} from '@tmagic/utils';

import BaseService from '@editor/services//BaseService';
import propsService from '@editor/services//props';
Expand Down Expand Up @@ -175,36 +184,7 @@ class Editor extends BaseService {
root = toRaw(root);
}

const info: EditorNodeInfo = {
node: null,
parent: null,
page: null,
};

if (!root) return info;

if (id === root.id) {
info.node = root;
return info;
}

const path = getNodePath(id, root.items);

if (!path.length) return info;

path.unshift(root);

info.node = path[path.length - 1] as MComponent;
info.parent = path[path.length - 2] as MContainer;

path.forEach((item) => {
if (isPage(item) || isPageFragment(item)) {
info.page = item as MPage | MPageFragment;
return;
}
});

return info;
return getNodeInfo(id, root);
}

/**
Expand Down Expand Up @@ -586,11 +566,7 @@ class Editor extends BaseService {
nodes.splice(targetIndex, 1, newConfig);
this.set('nodes', [...nodes]);

this.get('stage')?.update({
config: cloneDeep(newConfig),
parentId: parent.id,
root: cloneDeep(root),
});
// update后会触发依赖收集,收集完后会掉stage.update方法

if (isPage(newConfig) || isPageFragment(newConfig)) {
this.set('page', newConfig as MPage | MPageFragment);
Expand Down Expand Up @@ -875,7 +851,11 @@ class Editor extends BaseService {
await stage.select(targetId);

const targetParent = this.getParentById(target.id);
await stage.update({ config: cloneDeep(target), parentId: targetParent?.id, root: cloneDeep(root) });
await stage.update({
config: cloneDeep(target),
parentId: targetParent?.id,
root: cloneDeep(root),
});

await this.select(newConfig);
stage.select(newConfig.id);
Expand Down
5 changes: 3 additions & 2 deletions packages/editor/src/utils/idle-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,14 @@ export class IdleTask<T = any> extends EventEmitter {
}

private runTaskQueue(deadline: IdleDeadline) {
while ((deadline.timeRemaining() > 15 || deadline.didTimeout) && this.taskList.length) {
// 动画会占用空闲时间,当任务一直无法执行时,看看是否有动画正在播放
while ((deadline.timeRemaining() > 10 || deadline.didTimeout) && this.taskList.length) {
const task = this.taskList.shift();
task!.handler(task!.data);
}

if (this.taskList.length) {
this.taskHandle = globalThis.requestIdleCallback(this.runTaskQueue.bind(this), { timeout: 10000 });
this.taskHandle = globalThis.requestIdleCallback(this.runTaskQueue.bind(this), { timeout: 300 });
} else {
this.taskHandle = 0;

Expand Down
Loading

0 comments on commit fc38fc3

Please sign in to comment.