mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-09-12 23:19:32 +00:00
feat(data-source): 新增数据源 mounted 生命周期
页面渲染完成后由 runtime 触发 mounted 事件,统一管理数据源 mounted 及 timing=mounted 的方法执行;支持异步注册数据源在 init 后补充 mounted。
This commit is contained in:
parent
959c0322b6
commit
092bc2914d
@ -43,6 +43,7 @@ new DataSource(options: DataSourceOptions)
|
|||||||
| `methods` | `CodeBlockContent[]` | 自定义方法配置 |
|
| `methods` | `CodeBlockContent[]` | 自定义方法配置 |
|
||||||
| `data` | `any` | 当前数据 |
|
| `data` | `any` | 当前数据 |
|
||||||
| `isInit` | `boolean` | 是否已初始化 |
|
| `isInit` | `boolean` | 是否已初始化 |
|
||||||
|
| `isMounted` | `boolean` | 页面渲染后的 `mounted` 是否已执行 |
|
||||||
|
|
||||||
## 实例方法
|
## 实例方法
|
||||||
|
|
||||||
@ -172,6 +173,28 @@ ds.onDataChange('user.name', (payload) => {
|
|||||||
|
|
||||||
初始化数据源。
|
初始化数据源。
|
||||||
|
|
||||||
|
### mounted
|
||||||
|
|
||||||
|
- **返回:**
|
||||||
|
- `{Promise<void>}`
|
||||||
|
|
||||||
|
- **详情:**
|
||||||
|
|
||||||
|
页面渲染完成后执行,执行后 `isMounted` 为 `true`。由 `DataSourceManager` 监听到 `mounted` 事件后统一调用,自定义数据源可以重写该方法实现「页面渲染后」的逻辑。
|
||||||
|
|
||||||
|
- **示例:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class CustomDataSource extends DataSource {
|
||||||
|
public async mounted() {
|
||||||
|
// 页面渲染后再拉取数据,避免阻塞首屏
|
||||||
|
this.setData(await fetchSomething());
|
||||||
|
|
||||||
|
await super.mounted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### destroy
|
### destroy
|
||||||
|
|
||||||
- **返回:**
|
- **返回:**
|
||||||
|
|||||||
@ -25,6 +25,7 @@ new DataSourceManager(options: DataSourceManagerOptions)
|
|||||||
| `data` | `DataSourceManagerData` | 所有数据源的数据 |
|
| `data` | `DataSourceManagerData` | 所有数据源的数据 |
|
||||||
| `initialData` | `DataSourceManagerData` | 初始化数据 |
|
| `initialData` | `DataSourceManagerData` | 初始化数据 |
|
||||||
| `useMock` | `boolean` | 是否使用 Mock 数据 |
|
| `useMock` | `boolean` | 是否使用 Mock 数据 |
|
||||||
|
| `isMounted` | `boolean` | 页面是否已渲染完成(收到过 `mounted` 事件) |
|
||||||
|
|
||||||
## 静态方法
|
## 静态方法
|
||||||
|
|
||||||
@ -109,6 +110,27 @@ if (ds) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### mounted
|
||||||
|
|
||||||
|
- **参数:**
|
||||||
|
- `{DataSource} ds` 单个数据源实例(必填)
|
||||||
|
|
||||||
|
- **返回:**
|
||||||
|
- `{Promise<void>}`
|
||||||
|
|
||||||
|
- **详情:**
|
||||||
|
|
||||||
|
页面渲染完成后执行单个数据源的 `mounted`:先调用 `ds.mounted()`,再依次执行 `methods` 中 `timing === 'mounted'` 的方法。当 `ds.isMounted` 为 `true`,或当前 `app.jsEngine` 命中 `ds.schema.disabledInitInJsEngine` 时直接跳过。
|
||||||
|
|
||||||
|
一般不需要手动调用,runtime 在顶层组件渲染完成后触发 `mounted` 事件,由 manager 统一对所有数据源调用该方法。
|
||||||
|
|
||||||
|
- **示例:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// runtime 顶层组件渲染完成后
|
||||||
|
app.dataSourceManager?.emit('mounted');
|
||||||
|
```
|
||||||
|
|
||||||
### get
|
### get
|
||||||
|
|
||||||
- **参数:**
|
- **参数:**
|
||||||
@ -184,7 +206,7 @@ const ds = dataSourceManager.addDataSource({
|
|||||||
|
|
||||||
- **详情:**
|
- **详情:**
|
||||||
|
|
||||||
同步更新数据源 DSL 配置:先按 `id` 移除已有数据源,再以 `cloneDeep` 重新 `addDataSource`,并对新建实例触发 `init`(异步执行,不会被该方法 `await`)。一般在编辑器中修改配置后调用。
|
同步更新数据源 DSL 配置:先按 `id` 移除已有数据源,再以 `cloneDeep` 重新 `addDataSource`,并对新建实例触发 `init`(异步执行,不会被该方法 `await`);若此时 `isMounted` 已为 `true`,`init` 完成后还会补充执行 `mounted`。一般在编辑器中修改配置后调用。
|
||||||
|
|
||||||
### compiledNode
|
### compiledNode
|
||||||
|
|
||||||
@ -325,6 +347,7 @@ DataSourceManager 继承自 EventEmitter,支持以下事件:
|
|||||||
|--------|------|----------|
|
|--------|------|----------|
|
||||||
| `change` | 单个数据源数据变化 | `(dsId: string, changeEvent: ChangeEvent)` |
|
| `change` | 单个数据源数据变化 | `(dsId: string, changeEvent: ChangeEvent)` |
|
||||||
| `init` | 所有数据源初始化完成;现代分支携带 `(data, errors)`,旧 Promise.all 分支为 `(this.data)` | `(data, errors?)` |
|
| `init` | 所有数据源初始化完成;现代分支携带 `(data, errors)`,旧 Promise.all 分支为 `(this.data)` | `(data, errors?)` |
|
||||||
|
| `mounted` | 由 runtime 在顶层组件渲染完成后触发,manager 收到后统一执行各数据源的 `mounted` | 无 |
|
||||||
| `registered-all` | 所有数据源注册完成 | 无 |
|
| `registered-all` | 所有数据源注册完成 | 无 |
|
||||||
| `update-data` | 由 `createDataSourceManager` 在数据变化后发出,用于通知节点重新渲染 | `(newNodes: MNode[], sourceId: string, changeEvent: ChangeEvent, pageId: Id)` |
|
| `update-data` | 由 `createDataSourceManager` 在数据变化后发出,用于通知节点重新渲染 | `(newNodes: MNode[], sourceId: string, changeEvent: ChangeEvent, pageId: Id)` |
|
||||||
|
|
||||||
|
|||||||
@ -57,7 +57,7 @@ class DataSourceManager extends EventEmitter {
|
|||||||
for (let config = list.shift(); config; config = list.shift()) {
|
for (let config = list.shift(); config; config = list.shift()) {
|
||||||
const ds = app.addDataSource(config);
|
const ds = app.addDataSource(config);
|
||||||
if (ds) {
|
if (ds) {
|
||||||
app.init(ds);
|
app.initAndMount(ds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -84,6 +84,8 @@ class DataSourceManager extends EventEmitter {
|
|||||||
public data: DataSourceManagerData = {};
|
public data: DataSourceManagerData = {};
|
||||||
public initialData: DataSourceManagerData = {};
|
public initialData: DataSourceManagerData = {};
|
||||||
public useMock?: boolean = false;
|
public useMock?: boolean = false;
|
||||||
|
/** 页面是否已经渲染完成 */
|
||||||
|
public isMounted = false;
|
||||||
|
|
||||||
constructor({ app, useMock, initialData }: DataSourceManagerOptions) {
|
constructor({ app, useMock, initialData }: DataSourceManagerOptions) {
|
||||||
super();
|
super();
|
||||||
@ -109,6 +111,12 @@ class DataSourceManager extends EventEmitter {
|
|||||||
this.callDsInit();
|
this.callDsInit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 由runtime在顶层组件渲染完成后触发
|
||||||
|
this.on('mounted', () => {
|
||||||
|
this.isMounted = true;
|
||||||
|
this.callDsMounted();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(ds: DataSource) {
|
public async init(ds: DataSource) {
|
||||||
@ -137,6 +145,29 @@ class DataSourceManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面渲染完成后执行单个数据源的mounted
|
||||||
|
* @param {DataSource} ds 数据源实例
|
||||||
|
*/
|
||||||
|
public async mounted(ds: DataSource) {
|
||||||
|
if (ds.isMounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.app.jsEngine && ds.schema.disabledInitInJsEngine?.includes(this.app.jsEngine)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ds.mounted?.();
|
||||||
|
|
||||||
|
for (const method of ds.methods) {
|
||||||
|
if (typeof method.content !== 'function') return;
|
||||||
|
if (method.timing === 'mounted') {
|
||||||
|
await method.content({ params: {}, dataSource: ds, app: this.app });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public get(id: string) {
|
public get(id: string) {
|
||||||
return this.dataSourceMap.get(id);
|
return this.dataSourceMap.get(id);
|
||||||
}
|
}
|
||||||
@ -219,7 +250,7 @@ class DataSourceManager extends EventEmitter {
|
|||||||
this.addDataSource(cloneDeep(schema));
|
this.addDataSource(cloneDeep(schema));
|
||||||
const newDs = this.get(schema.id);
|
const newDs = this.get(schema.id);
|
||||||
if (newDs) {
|
if (newDs) {
|
||||||
this.init(newDs);
|
this.initAndMount(newDs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -390,6 +421,28 @@ class DataSourceManager extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private callDsMounted() {
|
||||||
|
const promises = Array.from(this.dataSourceMap).map(([, ds]) => this.mounted(ds));
|
||||||
|
|
||||||
|
if (typeof Promise.allSettled === 'function') {
|
||||||
|
return Promise.allSettled(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises.map((promise) => promise.catch(() => undefined)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化数据源,如果页面已经渲染完成(如异步注册的数据源类型),则在初始化后补充执行mounted
|
||||||
|
* @param {DataSource} ds 数据源实例
|
||||||
|
*/
|
||||||
|
private async initAndMount(ds: DataSource) {
|
||||||
|
await this.init(ds);
|
||||||
|
|
||||||
|
if (this.isMounted) {
|
||||||
|
await this.mounted(ds);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DataSourceManager;
|
export default DataSourceManager;
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import type { ChangeEvent, DataSourceOptions } from '@data-source/types';
|
|||||||
*/
|
*/
|
||||||
export default class DataSource<T extends DataSourceSchema = DataSourceSchema> extends EventEmitter {
|
export default class DataSource<T extends DataSourceSchema = DataSourceSchema> extends EventEmitter {
|
||||||
public isInit = false;
|
public isInit = false;
|
||||||
|
public isMounted = false;
|
||||||
|
|
||||||
/** @tmagic/core 实例 */
|
/** @tmagic/core 实例 */
|
||||||
public app: TMagicApp;
|
public app: TMagicApp;
|
||||||
@ -152,6 +153,13 @@ export default class DataSource<T extends DataSourceSchema = DataSourceSchema> e
|
|||||||
this.isInit = true;
|
this.isInit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面渲染后执行,由 DataSourceManager 监听到 mounted 事件后统一调用
|
||||||
|
*/
|
||||||
|
public async mounted() {
|
||||||
|
this.isMounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy() {
|
||||||
this.#fields = [];
|
this.#fields = [];
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
|
|||||||
@ -38,6 +38,25 @@ describe('DataSource', () => {
|
|||||||
|
|
||||||
expect(ds.isInit).toBeTruthy();
|
expect(ds.isInit).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('mounted', async () => {
|
||||||
|
const ds = new DataSource({
|
||||||
|
schema: {
|
||||||
|
type: 'base',
|
||||||
|
id: '1',
|
||||||
|
fields: [{ name: 'name' }],
|
||||||
|
methods: [],
|
||||||
|
events: [],
|
||||||
|
},
|
||||||
|
app: new App({}),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(ds.isMounted).toBe(false);
|
||||||
|
|
||||||
|
await ds.mounted();
|
||||||
|
|
||||||
|
expect(ds.isMounted).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DataSource setData', () => {
|
describe('DataSource setData', () => {
|
||||||
|
|||||||
@ -302,6 +302,227 @@ describe('DataSourceManager - init 生命周期', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('DataSourceManager - mounted 生命周期', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
DataSourceManager.clearDataSourceClass();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 等待 mounted 事件触发的异步流程执行完
|
||||||
|
const flush = () => new Promise<void>((resolve) => setTimeout(resolve, 0));
|
||||||
|
|
||||||
|
const createApp = (id: string, dataSources: any[] = [], jsEngine?: any) =>
|
||||||
|
new TMagicApp({
|
||||||
|
...(jsEngine ? { jsEngine } : {}),
|
||||||
|
config: {
|
||||||
|
type: NodeType.ROOT,
|
||||||
|
id,
|
||||||
|
items: [],
|
||||||
|
dataSources,
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
test('mounted 事件会统一执行所有数据源的 mounted', async () => {
|
||||||
|
const app = createApp('app_mounted', [
|
||||||
|
{ type: 'base', id: 'ds_m1', fields: [], methods: [], events: [] },
|
||||||
|
{ type: 'base', id: 'ds_m2', fields: [], methods: [], events: [] },
|
||||||
|
]);
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
|
||||||
|
expect(dsm.isMounted).toBe(false);
|
||||||
|
expect(dsm.get('ds_m1')?.isMounted).toBe(false);
|
||||||
|
|
||||||
|
dsm.emit('mounted');
|
||||||
|
await flush();
|
||||||
|
|
||||||
|
expect(dsm.isMounted).toBe(true);
|
||||||
|
expect(dsm.get('ds_m1')?.isMounted).toBe(true);
|
||||||
|
expect(dsm.get('ds_m2')?.isMounted).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('methods 中 timing=mounted 的 content 会在 ds.mounted 之后调用', async () => {
|
||||||
|
const app = createApp('app_mounted_method');
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
const order: string[] = [];
|
||||||
|
const mountedContent = vi.fn(() => {
|
||||||
|
order.push('method');
|
||||||
|
});
|
||||||
|
const ds = new DataSource({
|
||||||
|
app,
|
||||||
|
schema: {
|
||||||
|
type: 'base',
|
||||||
|
id: 'ds_mounted_method',
|
||||||
|
fields: [],
|
||||||
|
events: [],
|
||||||
|
methods: [{ name: 'onMounted', content: mountedContent, timing: 'mounted', params: [] }],
|
||||||
|
} as any,
|
||||||
|
});
|
||||||
|
const origMounted = ds.mounted.bind(ds);
|
||||||
|
ds.mounted = async () => {
|
||||||
|
order.push('mounted');
|
||||||
|
await origMounted();
|
||||||
|
};
|
||||||
|
|
||||||
|
await dsm.mounted(ds);
|
||||||
|
|
||||||
|
expect(mountedContent).toHaveBeenCalledTimes(1);
|
||||||
|
const arg = mountedContent.mock.calls[0][0] as any;
|
||||||
|
expect(arg.dataSource).toBe(ds);
|
||||||
|
expect(arg.app).toBe(app);
|
||||||
|
expect(order).toEqual(['mounted', 'method']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ds.isMounted 为 true 时直接跳过', async () => {
|
||||||
|
const app = createApp('app_mounted_skip');
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
const content = vi.fn();
|
||||||
|
const ds = new DataSource({
|
||||||
|
app,
|
||||||
|
schema: {
|
||||||
|
type: 'base',
|
||||||
|
id: 'ds_mounted_skip',
|
||||||
|
fields: [],
|
||||||
|
events: [],
|
||||||
|
methods: [{ name: 'onMounted', content, timing: 'mounted', params: [] }],
|
||||||
|
} as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
await dsm.mounted(ds);
|
||||||
|
await dsm.mounted(ds);
|
||||||
|
|
||||||
|
expect(content).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('jsEngine 命中 disabledInitInJsEngine 时跳过 mounted', async () => {
|
||||||
|
const app = createApp('app_mounted_disabled', [], 'nodejs');
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
const ds = new DataSource({
|
||||||
|
app,
|
||||||
|
schema: {
|
||||||
|
type: 'base',
|
||||||
|
id: 'ds_mounted_disabled',
|
||||||
|
fields: [],
|
||||||
|
methods: [],
|
||||||
|
events: [],
|
||||||
|
disabledInitInJsEngine: ['nodejs'],
|
||||||
|
} as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
await dsm.mounted(ds);
|
||||||
|
|
||||||
|
expect(ds.isMounted).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('method.content 非函数时提前返回', async () => {
|
||||||
|
const app = createApp('app_mounted_bad');
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
const content = vi.fn();
|
||||||
|
const ds = new DataSource({
|
||||||
|
app,
|
||||||
|
schema: {
|
||||||
|
type: 'base',
|
||||||
|
id: 'ds_mounted_bad',
|
||||||
|
fields: [],
|
||||||
|
events: [],
|
||||||
|
methods: [
|
||||||
|
{ name: 'bad', content: 'not-a-function', timing: 'mounted', params: [] } as any,
|
||||||
|
{ name: 'onMounted', content, timing: 'mounted', params: [] } as any,
|
||||||
|
],
|
||||||
|
} as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
await dsm.mounted(ds);
|
||||||
|
|
||||||
|
expect(ds.isMounted).toBe(true);
|
||||||
|
expect(content).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mounted 抛错时不会产生未处理的异常', async () => {
|
||||||
|
const mountedSpy = vi.spyOn(DataSource.prototype, 'mounted').mockRejectedValue(new Error('mounted-boom'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const app = createApp('app_mounted_err', [{ type: 'base', id: 'ds_me', fields: [], methods: [], events: [] }]);
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
|
||||||
|
dsm.emit('mounted');
|
||||||
|
await flush();
|
||||||
|
|
||||||
|
expect(dsm.isMounted).toBe(true);
|
||||||
|
} finally {
|
||||||
|
mountedSpy.mockRestore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Promise.allSettled 不可用时走 Promise.all 兼容分支', async () => {
|
||||||
|
const original = Promise.allSettled;
|
||||||
|
(Promise as any).allSettled = undefined;
|
||||||
|
const mountedSpy = vi.spyOn(DataSource.prototype, 'mounted').mockRejectedValueOnce(new Error('compat-boom'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const app = createApp('app_mounted_compat', [
|
||||||
|
{ type: 'base', id: 'ds_mc1', fields: [], methods: [], events: [] },
|
||||||
|
{ type: 'base', id: 'ds_mc2', fields: [], methods: [], events: [] },
|
||||||
|
]);
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
|
||||||
|
dsm.emit('mounted');
|
||||||
|
await flush();
|
||||||
|
|
||||||
|
expect(mountedSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dsm.get('ds_mc2')?.isMounted).toBe(true);
|
||||||
|
} finally {
|
||||||
|
(Promise as any).allSettled = original;
|
||||||
|
mountedSpy.mockRestore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('页面渲染后注册的数据源类型会在 init 之后补充执行 mounted', async () => {
|
||||||
|
const app = createApp('app_mounted_register', [
|
||||||
|
{ type: 'mounted-late', id: 'ds_late', fields: [], methods: [], events: [] },
|
||||||
|
]);
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
|
||||||
|
expect(dsm.get('ds_late')).toBeUndefined();
|
||||||
|
|
||||||
|
dsm.emit('mounted');
|
||||||
|
await flush();
|
||||||
|
|
||||||
|
class LateDataSource extends DataSource {}
|
||||||
|
DataSourceManager.register('mounted-late', LateDataSource as any);
|
||||||
|
await flush();
|
||||||
|
|
||||||
|
expect(dsm.get('ds_late')?.isInit).toBe(true);
|
||||||
|
expect(dsm.get('ds_late')?.isMounted).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('页面渲染后 updateSchema 重建的数据源会执行 mounted', async () => {
|
||||||
|
const app = createApp('app_mounted_update', [
|
||||||
|
{ type: 'base', id: 'ds_up', fields: [{ name: 'a' }], methods: [], events: [] },
|
||||||
|
]);
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
|
||||||
|
dsm.emit('mounted');
|
||||||
|
await flush();
|
||||||
|
|
||||||
|
dsm.updateSchema([{ type: 'base', id: 'ds_up', fields: [{ name: 'b' }], methods: [], events: [] }]);
|
||||||
|
await flush();
|
||||||
|
|
||||||
|
expect(dsm.get('ds_up')?.isMounted).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('页面未渲染时 updateSchema 不会执行 mounted', async () => {
|
||||||
|
const app = createApp('app_update_not_mounted', [
|
||||||
|
{ type: 'base', id: 'ds_nm', fields: [{ name: 'a' }], methods: [], events: [] },
|
||||||
|
]);
|
||||||
|
const dsm = new DataSourceManager({ app });
|
||||||
|
|
||||||
|
dsm.updateSchema([{ type: 'base', id: 'ds_nm', fields: [{ name: 'b' }], methods: [], events: [] }]);
|
||||||
|
await flush();
|
||||||
|
|
||||||
|
expect(dsm.get('ds_nm')?.isInit).toBe(true);
|
||||||
|
expect(dsm.get('ds_nm')?.isMounted).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('DataSourceManager - addDataSource 边界', () => {
|
describe('DataSourceManager - addDataSource 边界', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
DataSourceManager.clearDataSourceClass();
|
DataSourceManager.clearDataSourceClass();
|
||||||
|
|||||||
@ -103,6 +103,7 @@ export const getCodeBlockFormConfig = (options: GetCodeBlockFormConfigOptions =
|
|||||||
const list = [
|
const list = [
|
||||||
{ text: '初始化前', value: 'beforeInit' },
|
{ text: '初始化前', value: 'beforeInit' },
|
||||||
{ text: '初始化后', value: 'afterInit' },
|
{ text: '初始化后', value: 'afterInit' },
|
||||||
|
{ text: '页面渲染后', value: 'mounted' },
|
||||||
];
|
];
|
||||||
if (dataSourceType?.() !== 'base') {
|
if (dataSourceType?.() !== 'base') {
|
||||||
list.push({ text: '请求前', value: 'beforeRequest' });
|
list.push({ text: '请求前', value: 'beforeRequest' });
|
||||||
|
|||||||
@ -222,7 +222,13 @@ describe('CodeBlockEditor', () => {
|
|||||||
});
|
});
|
||||||
const timingItem = capturedConfig.find((c: any) => c.name === 'timing');
|
const timingItem = capturedConfig.find((c: any) => c.name === 'timing');
|
||||||
const opts = timingItem.options();
|
const opts = timingItem.options();
|
||||||
expect(opts.length).toBe(4);
|
expect(opts.map((opt: any) => opt.value)).toEqual([
|
||||||
|
'beforeInit',
|
||||||
|
'afterInit',
|
||||||
|
'mounted',
|
||||||
|
'beforeRequest',
|
||||||
|
'afterRequest',
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('timing options - base 类型', () => {
|
test('timing options - base 类型', () => {
|
||||||
@ -231,7 +237,7 @@ describe('CodeBlockEditor', () => {
|
|||||||
});
|
});
|
||||||
const timingItem = capturedConfig.find((c: any) => c.name === 'timing');
|
const timingItem = capturedConfig.find((c: any) => c.name === 'timing');
|
||||||
const opts = timingItem.options();
|
const opts = timingItem.options();
|
||||||
expect(opts.length).toBe(2);
|
expect(opts.map((opt: any) => opt.value)).toEqual(['beforeInit', 'afterInit', 'mounted']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('changeHandler 触发 changedValue', async () => {
|
test('changeHandler 触发 changedValue', async () => {
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext, useEffect } from 'react';
|
||||||
|
|
||||||
import type TMagicApp from '@tmagic/core';
|
import type TMagicApp from '@tmagic/core';
|
||||||
import { AppContent, useDsl } from '@tmagic/react-runtime-help';
|
import { AppContent, useDsl } from '@tmagic/react-runtime-help';
|
||||||
@ -26,6 +26,10 @@ function App() {
|
|||||||
|
|
||||||
const { pageConfig } = useDsl(app);
|
const { pageConfig } = useDsl(app);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
app?.dataSourceManager?.emit('mounted');
|
||||||
|
}, [app]);
|
||||||
|
|
||||||
const MagicUiPage = app?.resolveComponent('page');
|
const MagicUiPage = app?.resolveComponent('page');
|
||||||
|
|
||||||
return <MagicUiPage config={pageConfig}></MagicUiPage>;
|
return <MagicUiPage config={pageConfig}></MagicUiPage>;
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive } from 'vue';
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
import type { Id, MPage } from '@tmagic/core';
|
import type { Id, MPage } from '@tmagic/core';
|
||||||
import { cloneDeep, DevtoolApi, getNodeInfo, replaceChildNode, setValueByKeyPath } from '@tmagic/core';
|
import { cloneDeep, DevtoolApi, getNodeInfo, replaceChildNode, setValueByKeyPath } from '@tmagic/core';
|
||||||
@ -12,6 +12,10 @@ import { useComponent, useDsl } from '@tmagic/vue-runtime-help';
|
|||||||
const { pageConfig, app } = useDsl();
|
const { pageConfig, app } = useDsl();
|
||||||
const pageComponent = useComponent('page');
|
const pageComponent = useComponent('page');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
app.dataSourceManager?.emit('mounted');
|
||||||
|
});
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
app.devtools = new (class extends DevtoolApi {
|
app.devtools = new (class extends DevtoolApi {
|
||||||
public updateDsl(nodeId: Id, data: any, path: string) {
|
public updateDsl(nodeId: Id, data: any, path: string) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user