@zxiaosi/sdk 1.0.0-beta.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -195
- package/dist/index.d.ts +120 -31
- package/dist/index.js +1 -1
- package/package.json +5 -8
package/README.md
CHANGED
|
@@ -1,205 +1,21 @@
|
|
|
1
|
-
## 路线规划
|
|
2
|
-
|
|
3
|
-
- [规划图地址](https://excalidraw.com/#json=_CghR9MKEcgUt4SaYUXFY,V9n_bmADhi6JXp2TUk-ObA)
|
|
4
|
-
|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
- [更多详情](https://zxiaosi.com/archives/c6c40209.html)
|
|
8
|
-
|
|
9
|
-
## 快速开始
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# 安装 @zxiaosi/sdk 对应的版本 0.5.x
|
|
13
|
-
npm install -g @zxiaosi/create-sdk
|
|
14
|
-
|
|
15
|
-
npx create-sdk
|
|
16
|
-
```
|
|
17
|
-
|
|
18
1
|
## 项目介绍
|
|
19
2
|
|
|
20
|
-
- 整个 `SDK` 都是围绕 `
|
|
21
|
-
|
|
22
|
-
- `getRoutesApi` 接口用于获取应用路由信息,包括主应用和微应用的路由配置。因为 `Qiankun` 的 `entry` 配置比较特殊,所以 `主应用` 需要使用 `vite-plugin-mock` 插件 `mock` 接口
|
|
23
|
-
- 在本地开发是本地服务 `"entry": "http://localhost:5174"`
|
|
24
|
-
- 在生产环境是文件路径 `"entry": "/subapp/"`
|
|
25
|
-
|
|
26
|
-
- `getUserInfoApi` 接口用于获取用户相关数据,以便进行权限控制和个性化设置。
|
|
27
|
-
- `user`:用户信息(必选)
|
|
28
|
-
- `permissions`:权限信息(必选)
|
|
29
|
-
- `role`:角色信息(可选)
|
|
30
|
-
- `setting`:配置信息(可选)
|
|
31
|
-
|
|
32
|
-
## 核心代码
|
|
33
|
-
|
|
34
|
-
### 主应用
|
|
35
|
-
|
|
36
|
-
- 在 `main.ts` 中进行 `SDK` 的挂载
|
|
37
|
-
|
|
38
|
-
```js
|
|
39
|
-
import {
|
|
40
|
-
sdk,
|
|
41
|
-
SdkApiPlugin,
|
|
42
|
-
SdkAppPlugin,
|
|
43
|
-
SdkClientPlugin,
|
|
44
|
-
SdkConfigPlugin,
|
|
45
|
-
SdkI18nPlugin,
|
|
46
|
-
SdkStoragePlugin,
|
|
47
|
-
SdkStorePlugin,
|
|
48
|
-
SdkUIPlugin,
|
|
49
|
-
} from '@zxiaosi/sdk';
|
|
50
|
-
import { createRoot } from 'react-dom/client';
|
|
51
|
-
import App from './App';
|
|
52
|
-
|
|
53
|
-
const getRoutesApi = async () => ({
|
|
54
|
-
code: 0,
|
|
55
|
-
data: [
|
|
56
|
-
{ path: '/home', name: '首页', component: 'Home' },
|
|
57
|
-
{ path: '/subapp', name: '微应用', component: 'Microapp',
|
|
58
|
-
routeAttr: `{"name": "subapp", "entry": "http://localhost:5174", "activeRule": "/subapp", "rootId": "sub-app"}`,
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
const getUserInfoApi = async () => ({
|
|
64
|
-
code: 0,
|
|
65
|
-
data: { user: {}, permissions: ['/home', '/subapp'], roles: [], settings: {} },
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
const loginApi = async () => ({ code: 0, data: { token: 'xxxx' } })
|
|
69
|
-
|
|
70
|
-
/** 挂载 SDK */
|
|
71
|
-
sdk
|
|
72
|
-
.use(SdkApiPlugin, {
|
|
73
|
-
getRoutesApi,
|
|
74
|
-
getUserInfoApi,
|
|
75
|
-
loginApi,
|
|
76
|
-
})
|
|
77
|
-
.use(SdkAppPlugin)
|
|
78
|
-
.use(SdkClientPlugin)
|
|
79
|
-
.use(SdkConfigPlugin, {
|
|
80
|
-
qiankunMode: 'router',
|
|
81
|
-
proLayoutConfig: {
|
|
82
|
-
title: 'Demo',
|
|
83
|
-
layout: 'mix',
|
|
84
|
-
},
|
|
85
|
-
})
|
|
86
|
-
.use(SdkI18nPlugin)
|
|
87
|
-
.use(SdkStoragePlugin)
|
|
88
|
-
.use(SdkStorePlugin)
|
|
89
|
-
.use(SdkUIPlugin, { Home: () => <div>Home</div> })
|
|
90
|
-
.mount('sdk');
|
|
91
|
-
|
|
92
|
-
/** 渲染主应用 */
|
|
93
|
-
createRoot(document.getElementById('root')!).render(<App />);
|
|
94
|
-
```
|
|
3
|
+
- 整个 `SDK` 都是围绕 `sdk.app.user`、 `sdk.app.menus`、`sdk.app.microApps` 进行设计的,旨在简化微前端应用的开发
|
|
95
4
|
|
|
96
|
-
-
|
|
5
|
+
- 主应用 `sdk.use(plugin, opts).mount('sdk')` → 微应用 `sdk.extend('sdk')` → 共享同一套状态与能力
|
|
97
6
|
|
|
98
|
-
|
|
99
|
-
import { sdk } from '@zxiaosi/sdk';
|
|
7
|
+
- 架构图
|
|
100
8
|
|
|
101
|
-
|
|
102
|
-
const Mainapp = sdk.ui.getComponent('Mainapp');
|
|
103
|
-
return <Mainapp />;
|
|
104
|
-
}
|
|
9
|
+

|
|
105
10
|
|
|
106
|
-
|
|
107
|
-
```
|
|
11
|
+
- [架构图地址](https://excalidraw.com/#json=D71Fw3vbEUxoaA-15i2Cj,VMKGJ8x_P3I1MrMbVBjgcw)
|
|
108
12
|
|
|
109
|
-
|
|
13
|
+
- [更多详情](https://zxiaosi.com/archives/113adf2d.html)
|
|
110
14
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
```js
|
|
114
|
-
import { defineConfig } from 'vite';
|
|
115
|
-
import react from '@vitejs/plugin-react';
|
|
116
|
-
import qiankun from 'vite-plugin-qiankun-lite';
|
|
117
|
-
|
|
118
|
-
export default defineConfig({
|
|
119
|
-
plugins: [
|
|
120
|
-
react(),
|
|
121
|
-
qiankun({ name: 'subapp', sandbox: !!process.env.VITE_SANDBOX }),
|
|
122
|
-
],
|
|
123
|
-
server: {
|
|
124
|
-
port: 5174,
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
- 在 `main.ts` 中进行 `SDK` 的继承
|
|
130
|
-
|
|
131
|
-
```js
|
|
132
|
-
import { sdk } from '@zxiaosi/sdk';
|
|
133
|
-
import { createRoot, type Root } from 'react-dom/client';
|
|
134
|
-
import App from './App.tsx';
|
|
135
|
-
import './index.css';
|
|
136
|
-
|
|
137
|
-
let root: Root;
|
|
138
|
-
const render = (props: any = {}) => {
|
|
139
|
-
const container = props?.container
|
|
140
|
-
? props.container.querySelector('#root')
|
|
141
|
-
: document.getElementById('root');
|
|
142
|
-
|
|
143
|
-
root = createRoot(container);
|
|
144
|
-
|
|
145
|
-
root.render(<App />);
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
if (!window.__POWERED_BY_QIANKUN__) {
|
|
149
|
-
render();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export async function mount(props: any) {
|
|
153
|
-
console.log(`Microapp mount`, props);
|
|
154
|
-
sdk.extend('sdk'); // 继承 sdk 功能
|
|
155
|
-
render(props);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export async function unmount(props: any) {
|
|
159
|
-
console.log(`Microapp unmount`, props);
|
|
160
|
-
root.unmount();
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
- [可参考项目](https://github.com/zxiaosi-team/fontend-sdk/tree/master/packages)
|
|
165
|
-
|
|
166
|
-
## 注意事项
|
|
167
|
-
|
|
168
|
-
- `SDK` 不能在 `组件外` 进行使用,否则会报 `SDK 未初始化` 错误
|
|
169
|
-
|
|
170
|
-
- 微应用使用 `sdk.ui.renderComponent('xxx')` 时,会报 `React 多实例` 的错误,需要使用 `vite-plugin-externals` 插件将 `React` 等依赖排除在打包之外。[更多详情](https://zxiaosi.com/archives/bc84a75a.html)
|
|
171
|
-
- 在主/子应用的 `index.html` 中使用 `cdn` 的方式去加载 `react` 和 `react-dom` 包
|
|
172
|
-
|
|
173
|
-
```html
|
|
174
|
-
<!doctype html>
|
|
175
|
-
<html lang="en">
|
|
176
|
-
<head>
|
|
177
|
-
<title>%VITE_APP_TITLE%</title>
|
|
178
|
-
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js"></script>
|
|
179
|
-
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js"></script>
|
|
180
|
-
</head>
|
|
181
|
-
<body>
|
|
182
|
-
<div id="root"></div>
|
|
183
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
184
|
-
</body>
|
|
185
|
-
</html>
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
- 在主/子应用中使用 `vite-plugin-externals` 插件去排除 `react` 和 `react-dom` 包
|
|
189
|
-
|
|
190
|
-
```ts
|
|
191
|
-
import { viteExternalsPlugin } from 'vite-plugin-externals';
|
|
15
|
+
## 快速开始
|
|
192
16
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
viteExternalsPlugin({
|
|
196
|
-
react: 'React',
|
|
17
|
+
```sh
|
|
18
|
+
npm install -g @zxiaosi/create-sdk
|
|
197
19
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
'react-dom': 'ReactDOM',
|
|
201
|
-
'react-dom/client': 'ReactDOM',
|
|
202
|
-
}),
|
|
203
|
-
],
|
|
204
|
-
});
|
|
205
|
-
```
|
|
20
|
+
npx create-sdk
|
|
21
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ComponentType, ReactElement } from "react";
|
|
2
|
-
import { NavigateFunction, UIMatch } from "react-router-dom";
|
|
3
|
-
import { MicroApp, ObjectType, RegistrableApp } from "qiankun";
|
|
2
|
+
import { Location, NavigateFunction, UIMatch } from "react-router-dom";
|
|
4
3
|
import { StateCreator } from "zustand";
|
|
5
4
|
|
|
6
5
|
//#region src/plugins/api/index.d.ts
|
|
@@ -8,24 +7,30 @@ interface ApiOptions {}
|
|
|
8
7
|
/**
|
|
9
8
|
* 请求插件
|
|
10
9
|
*/
|
|
11
|
-
declare const SDKApiPlugin: SDKPlugin
|
|
10
|
+
declare const SDKApiPlugin: SDKPlugin<'api'>;
|
|
12
11
|
//#endregion
|
|
13
12
|
//#region src/plugins/app/index.d.ts
|
|
14
13
|
interface AppOptions {
|
|
15
|
-
/** 菜单数据 */
|
|
16
|
-
menuData: any[];
|
|
17
|
-
/** 所有路由信息 */
|
|
18
|
-
allRoutes: any[];
|
|
19
|
-
/** 微应用信息 */
|
|
20
|
-
microApps: RegistrableApp<ObjectType>[];
|
|
21
|
-
/** 微应用实例 */
|
|
22
|
-
microAppsInstance: Map<string, MicroApp>;
|
|
23
14
|
/** 用户信息 */
|
|
24
15
|
user: UserInfo['user'];
|
|
16
|
+
/** 菜单数据 */
|
|
17
|
+
menus: any[];
|
|
25
18
|
/** 用户权限 */
|
|
26
19
|
permissions: UserInfo['permissions'];
|
|
27
20
|
/** 用户设置 */
|
|
28
21
|
settings: UserInfo['settings'];
|
|
22
|
+
/** 微应用信息 */
|
|
23
|
+
microApps: any[];
|
|
24
|
+
/** 微应用实例 */
|
|
25
|
+
microAppsInstance: Map<string, any>;
|
|
26
|
+
/**
|
|
27
|
+
* 初始化数据(登录跳转不刷新页面用)
|
|
28
|
+
*/
|
|
29
|
+
initData?(): void | Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* 手动加载微应用(与 Qiankun 一致)
|
|
32
|
+
*/
|
|
33
|
+
loadMicroApp?(app: LoadableApp, configuration?: LoadMicroAppConfiguration, lifeCycles?: MicroAppLifeCycles): MicroAppInstance;
|
|
29
34
|
/**
|
|
30
35
|
* 获取国际化默认值
|
|
31
36
|
* - 1. 本地缓存 `sdk.storage.getItem(sdk.storage.localeKey)`
|
|
@@ -49,10 +54,6 @@ interface AppOptions {
|
|
|
49
54
|
* - 3. 最后使用菜单中第一项 `/`
|
|
50
55
|
*/
|
|
51
56
|
getRedirectPath(): string;
|
|
52
|
-
/**
|
|
53
|
-
* 初始化数据
|
|
54
|
-
*/
|
|
55
|
-
initData(): void | Promise<void>;
|
|
56
57
|
/**
|
|
57
58
|
* 跳转登录页
|
|
58
59
|
* - 1. 清除 Token
|
|
@@ -72,10 +73,10 @@ interface AppOptions {
|
|
|
72
73
|
* 应用插件
|
|
73
74
|
*
|
|
74
75
|
* @example
|
|
75
|
-
* sdk.use(SDKAppPlugin, {
|
|
76
|
+
* sdk.use(SDKAppPlugin, { menus: [...] }).mount('xxx');
|
|
76
77
|
* sdk.app.unmountMicroApp();
|
|
77
78
|
*/
|
|
78
|
-
declare const SDKAppPlugin: SDKPlugin
|
|
79
|
+
declare const SDKAppPlugin: SDKPlugin<'app'>;
|
|
79
80
|
//#endregion
|
|
80
81
|
//#region src/plugins/components/index.d.ts
|
|
81
82
|
interface ComponentsOptions {
|
|
@@ -106,12 +107,12 @@ interface ComponentsOptions {
|
|
|
106
107
|
* sdk.components.setComponent(组件, '组件名称');
|
|
107
108
|
* sdk.components.renderComponent('组件名称', props);
|
|
108
109
|
*/
|
|
109
|
-
declare const SDKComponentsPlugin: SDKPlugin
|
|
110
|
+
declare const SDKComponentsPlugin: SDKPlugin<'components'>;
|
|
110
111
|
//#endregion
|
|
111
112
|
//#region src/plugins/config/index.d.ts
|
|
112
113
|
interface ConfigOptions {
|
|
113
114
|
/** 环境变量(主应用共享给微应用变量) */
|
|
114
|
-
env:
|
|
115
|
+
env: ObjectType;
|
|
115
116
|
/** 主题 */
|
|
116
117
|
theme: ThemeProps;
|
|
117
118
|
/** 语言 */
|
|
@@ -121,7 +122,9 @@ interface ConfigOptions {
|
|
|
121
122
|
/** 登录后跳转的路由 */
|
|
122
123
|
defaultPath: string;
|
|
123
124
|
/** 重定向字段 */
|
|
124
|
-
redirectField
|
|
125
|
+
redirectField: string;
|
|
126
|
+
/** 微应用生命周期 */
|
|
127
|
+
lifeCycles: MicroAppLifeCycles;
|
|
125
128
|
}
|
|
126
129
|
/**
|
|
127
130
|
* 配置插件
|
|
@@ -130,7 +133,7 @@ interface ConfigOptions {
|
|
|
130
133
|
* sdk.use(SDKConfigPlugin, { theme: 'light' }).mount('xxx');
|
|
131
134
|
* console.log(sdk.api.theme); // 'light'
|
|
132
135
|
*/
|
|
133
|
-
declare const SDKConfigPlugin: SDKPlugin
|
|
136
|
+
declare const SDKConfigPlugin: SDKPlugin<'config'>;
|
|
134
137
|
//#endregion
|
|
135
138
|
//#region src/plugins/i18n/index.d.ts
|
|
136
139
|
interface I18nOptions {}
|
|
@@ -140,7 +143,7 @@ interface I18nOptions {}
|
|
|
140
143
|
* @example
|
|
141
144
|
* sdk.use(SDKI18nPlugin).mount('xxx');
|
|
142
145
|
*/
|
|
143
|
-
declare const SDKI18nPlugin: SDKPlugin
|
|
146
|
+
declare const SDKI18nPlugin: SDKPlugin<'i18n'>;
|
|
144
147
|
//#endregion
|
|
145
148
|
//#region src/plugins/router/index.d.ts
|
|
146
149
|
interface RouterOptions {
|
|
@@ -160,7 +163,7 @@ interface RouterOptions {
|
|
|
160
163
|
* sdk.router.navigate; // 路由跳转
|
|
161
164
|
* sdk.router.matches; // 面包屑信息
|
|
162
165
|
*/
|
|
163
|
-
declare const SDKRouterPlugin: SDKPlugin
|
|
166
|
+
declare const SDKRouterPlugin: SDKPlugin<'router'>;
|
|
164
167
|
//#endregion
|
|
165
168
|
//#region src/plugins/storage/index.d.ts
|
|
166
169
|
interface StorageOptions {
|
|
@@ -185,7 +188,7 @@ interface StorageOptions {
|
|
|
185
188
|
* @example
|
|
186
189
|
* sdk.use(SDKStoragePlugin).mount('xxx');
|
|
187
190
|
*/
|
|
188
|
-
declare const SDKStoragePlugin: SDKPlugin
|
|
191
|
+
declare const SDKStoragePlugin: SDKPlugin<'storage'>;
|
|
189
192
|
//#endregion
|
|
190
193
|
//#region src/plugins/store/createLocale.d.ts
|
|
191
194
|
interface LocaleStoreProps {
|
|
@@ -252,14 +255,17 @@ type StoreOptions = ReturnType<typeof createGlobalStore>;
|
|
|
252
255
|
* @example sdk.store?.getState()?.setTheme('light')
|
|
253
256
|
* @example sdk.store.subscribe((state) => state.theme, (theme) => { console.log('theme', theme) }, { fireImmediately: true }) // fireImmediately 立即变更
|
|
254
257
|
*/
|
|
255
|
-
declare const SDKStorePlugin: SDKPlugin
|
|
258
|
+
declare const SDKStorePlugin: SDKPlugin<'store'>;
|
|
256
259
|
//#endregion
|
|
257
260
|
//#region src/types.d.ts
|
|
261
|
+
type ObjectType = Record<string, any>;
|
|
258
262
|
type ThemeProps = 'light' | 'dark' | (string & {});
|
|
259
263
|
type LocaleProps = 'zh-CN' | 'en-US' | (string & {});
|
|
260
264
|
interface UserInfo {
|
|
261
265
|
/** 用户信息 */
|
|
262
266
|
user?: any;
|
|
267
|
+
/** 菜单数据 */
|
|
268
|
+
menus?: any[];
|
|
263
269
|
/** 用户权限 */
|
|
264
270
|
permissions?: string[];
|
|
265
271
|
/** 用户设置 */
|
|
@@ -291,16 +297,99 @@ interface SDKPlugins {
|
|
|
291
297
|
/** 状态管理 */
|
|
292
298
|
store: StoreOptions;
|
|
293
299
|
}
|
|
294
|
-
interface SDKPlugin {
|
|
300
|
+
interface SDKPlugin<K extends keyof SDKPlugins> {
|
|
295
301
|
/** 插件名称 */
|
|
296
302
|
name: string;
|
|
297
303
|
/** 插件安装方法 */
|
|
298
|
-
install(sdk: SDKInstance, options?:
|
|
304
|
+
install(sdk: SDKInstance, options?: ObjectType): void;
|
|
299
305
|
/** 插件配置项 */
|
|
300
|
-
options?:
|
|
306
|
+
options?: Partial<SDKPlugins[K]> & ObjectType;
|
|
301
307
|
}
|
|
302
|
-
type SDKPluginOptions = Record<string, any | ((...args: any[]) => any)>;
|
|
303
308
|
type SDKInstance = SDKCore & SDKPlugins;
|
|
309
|
+
/**
|
|
310
|
+
* 微应用状态
|
|
311
|
+
*/
|
|
312
|
+
type MicroAppStatus = 'NOT_LOADED' | 'LOADING_SOURCE_CODE' | 'NOT_BOOTSTRAPPED' | 'BOOTSTRAPPING' | 'NOT_MOUNTED' | 'MOUNTING' | 'MOUNTED' | 'UPDATING' | 'UNMOUNTING' | 'UNLOADING' | 'SKIP_BECAUSE_BROKEN' | 'LOAD_ERROR';
|
|
313
|
+
/**
|
|
314
|
+
* 微应用配置
|
|
315
|
+
*/
|
|
316
|
+
type LoadableApp<T extends object = ObjectType> = {
|
|
317
|
+
/** 微应用唯一名称 */name: string; /** 微应用入口 */
|
|
318
|
+
entry: string | {
|
|
319
|
+
scripts?: string[];
|
|
320
|
+
styles?: string[];
|
|
321
|
+
html?: string;
|
|
322
|
+
}; /** 传递给微应用的数据 */
|
|
323
|
+
props?: T;
|
|
324
|
+
} & ({
|
|
325
|
+
/** 自定义函数 */render?: (props: {
|
|
326
|
+
appContent: string;
|
|
327
|
+
loading: boolean;
|
|
328
|
+
}) => any;
|
|
329
|
+
} | {
|
|
330
|
+
/**
|
|
331
|
+
* 容器节点
|
|
332
|
+
* @example '#root'
|
|
333
|
+
* @example document.querySelector('#root')
|
|
334
|
+
*/
|
|
335
|
+
container: string | HTMLElement;
|
|
336
|
+
});
|
|
337
|
+
/**
|
|
338
|
+
* 生命周期函数
|
|
339
|
+
*/
|
|
340
|
+
type LifeCycleFn = (app: LoadableApp, global: typeof window) => any;
|
|
341
|
+
/**
|
|
342
|
+
* 生命周期钩子
|
|
343
|
+
*/
|
|
344
|
+
interface MicroAppLifeCycles {
|
|
345
|
+
beforeLoad?: LifeCycleFn | Array<LifeCycleFn>;
|
|
346
|
+
beforeMount?: LifeCycleFn | Array<LifeCycleFn>;
|
|
347
|
+
afterMount?: LifeCycleFn | Array<LifeCycleFn>;
|
|
348
|
+
beforeUnmount?: LifeCycleFn | Array<LifeCycleFn>;
|
|
349
|
+
afterUnmount?: LifeCycleFn | Array<LifeCycleFn>;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* 加载配置
|
|
353
|
+
*/
|
|
354
|
+
interface LoadMicroAppConfiguration {
|
|
355
|
+
/** 开启沙箱 */
|
|
356
|
+
sandbox?: boolean | {
|
|
357
|
+
strictStyleIsolation?: boolean;
|
|
358
|
+
experimentalStyleIsolation?: boolean;
|
|
359
|
+
};
|
|
360
|
+
/** 是否为单实例场景,单实例指的是同一时间只会渲染一个微应用 */
|
|
361
|
+
singular?: boolean | ((app: LoadableApp<any>) => Promise<boolean>);
|
|
362
|
+
/** 自定义的 fetch 方法 */
|
|
363
|
+
prefetch?: boolean | 'all' | string[] | ((apps: any[]) => {
|
|
364
|
+
criticalAppNames: string[];
|
|
365
|
+
minorAppsName: string[];
|
|
366
|
+
});
|
|
367
|
+
/** 指定部分特殊的动态加载的微应用资源(css/js) 不被 qiankun 劫持处理 */
|
|
368
|
+
excludeAssetFilter?: (url: string) => boolean;
|
|
369
|
+
fetch?: typeof window.fetch | {
|
|
370
|
+
fn?: typeof window.fetch;
|
|
371
|
+
autoDecodeResponse?: boolean;
|
|
372
|
+
};
|
|
373
|
+
getPublicPath?: (entry: any) => string;
|
|
374
|
+
getTemplate?: (tpl: string) => string;
|
|
375
|
+
postProcessTemplate?: (tplResult: any) => any;
|
|
376
|
+
urlRerouteOnly?: boolean;
|
|
377
|
+
autoStart?: boolean;
|
|
378
|
+
[key: string]: any;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* 微应用实例
|
|
382
|
+
*/
|
|
383
|
+
interface MicroAppInstance<T extends object = ObjectType> {
|
|
384
|
+
mount(): Promise<null>;
|
|
385
|
+
unmount(): Promise<null>;
|
|
386
|
+
update?(customProps: Partial<T>): Promise<any>;
|
|
387
|
+
getStatus(): MicroAppStatus;
|
|
388
|
+
loadPromise: Promise<null>;
|
|
389
|
+
bootstrapPromise: Promise<null>;
|
|
390
|
+
mountPromise: Promise<null>;
|
|
391
|
+
unmountPromise: Promise<null>;
|
|
392
|
+
}
|
|
304
393
|
//#endregion
|
|
305
394
|
//#region src/core/index.d.ts
|
|
306
395
|
/** SDK 类 */
|
|
@@ -325,9 +414,9 @@ declare class SDKCore {
|
|
|
325
414
|
* @param plugin - 插件对象 {@link SDKPlugin}
|
|
326
415
|
* @param options - 插件选项
|
|
327
416
|
*/
|
|
328
|
-
use(plugin: SDKPlugin
|
|
417
|
+
use<K extends keyof SDKPlugins>(plugin: SDKPlugin<K>, options?: SDKPlugin<K>['options']): this;
|
|
329
418
|
}
|
|
330
419
|
/** 创建 SDK 实例 */
|
|
331
420
|
declare const sdk: SDKInstance;
|
|
332
421
|
//#endregion
|
|
333
|
-
export { type ApiOptions, type AppOptions, type ComponentsOptions, type ConfigOptions, type I18nOptions, LocaleProps, type RouterOptions, SDKApiPlugin, SDKAppPlugin, SDKComponentsPlugin, SDKConfigPlugin, SDKCore, SDKI18nPlugin, SDKInstance, SDKPlugin,
|
|
422
|
+
export { type ApiOptions, type AppOptions, type ComponentsOptions, type ConfigOptions, type I18nOptions, LifeCycleFn, LoadMicroAppConfiguration, LoadableApp, LocaleProps, MicroAppInstance, MicroAppLifeCycles, MicroAppStatus, ObjectType, type RouterOptions, SDKApiPlugin, SDKAppPlugin, SDKComponentsPlugin, SDKConfigPlugin, SDKCore, SDKI18nPlugin, SDKInstance, SDKPlugin, SDKPlugins, SDKRouterPlugin, SDKStoragePlugin, SDKStorePlugin, type StorageOptions, type StoreOptions, type StoreProps, type StoreSlice, ThemeProps, UserInfo, sdk };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Suspense as e,createElement as t,memo as n,useEffect as r,useState as i}from"react";import{Outlet as a,useLocation as o,useNavigate as s}from"react-router-dom";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";import{
|
|
1
|
+
import{Suspense as e,createElement as t,memo as n,useEffect as r,useState as i}from"react";import{Outlet as a,useLocation as o,useNavigate as s}from"react-router-dom";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";import{createStore as d,useStore as f}from"zustand";import{useShallow as p}from"zustand/shallow";import{subscribeWithSelector as m}from"zustand/middleware";var h=class{name;_plugins=new Map;mount(e){this.name=e,window[e]=this}extend(e){let t=window[e];if(!t)throw Error(`SDK "${e}" not found`);Object.assign(this,t)}use(e,t={}){let{name:n,install:r}=e;if(!n)throw Error(`SDK - The plugin requires a name`);if(typeof r!=`function`)throw Error(`SDK - The plugin "${n}" requires an install function`);return r(this,t),this._plugins.set(n,{...e,options:t}),this}};const g=new h,_={name:`api`,install(e,t={}){e.api=t}};function v(e){if(!e||typeof e!=`object`)return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype||Object.getPrototypeOf(t)===null?Object.prototype.toString.call(e)===`[object Object]`:!1}function y(e){return e===`__proto__`}function b(e,t){let n=Object.keys(t);for(let r=0;r<n.length;r++){let i=n[r];if(y(i))continue;let a=t[i],o=e[i];x(a)&&x(o)?e[i]=b(o,a):Array.isArray(a)?e[i]=b([],a):v(a)?e[i]=b({},a):(o===void 0||a!==void 0)&&(e[i]=a)}return e}function x(e){return v(e)||Array.isArray(e)}const S={name:`app`,install(e,t={}){e.app=b({user:null,menus:[],permissions:[],settings:{},microApps:[],microAppsInstance:new Map,initData:void 0,loadMicroApp:void 0,getDefaultLocale(){return e.storage.getItem(e.storage.localeKey)||e.config?.locale||navigator.language||`zh-CN`},getDefaultTheme(){let t=e.storage.getItem(e.storage.themeKey);if(t)return t;let n=e.config?.theme;if(n)return n;let r=window.matchMedia(`(prefers-color-scheme: dark)`);return r.matches&&r.matches?`dark`:`light`},getRedirectPath(){let t=e.config.defaultPath;if(t)return t;let n=new URLSearchParams(window.location.search),r=e.config.redirectField||`redirect`;return decodeURIComponent(n.get(r)||``)||`/`},pageToLogin(){e.storage.removeItem(e.storage.tokenKey),e.store.getState().resetUserInfo();let t=location.pathname,n=e.config.loginPath,r=e.config.redirectField||`redirect`,i=t===n?n:`${n}?${r}=${encodeURIComponent(t||`/`)}`;e.router.navigate(i,{replace:!0}),e.app.unmountMicroApp()},unmountMicroApp(t){t?t.forEach(t=>{let n=e.app.microAppsInstance.get(t);n&&(n.unmount(),e.app.microAppsInstance.delete(t),e.app.microApps=e.app.microApps.filter(e=>e.name!==t))}):(e.app.microAppsInstance.forEach(e=>e.unmount()),e.app.microAppsInstance.clear(),e.app.microApps=[])}},t)}},C={layout:{height:`100vh`,display:`flex`,flexDirection:`column`,background:`#f5f7fb`},header:{height:64,padding:`0 24px`,background:`#fff`,borderBottom:`1px solid #e5e7eb`,display:`flex`,alignItems:`center`,justifyContent:`space-between`,boxShadow:`0 2px 8px rgba(0,0,0,0.04)`,zIndex:10},logo:{fontSize:20,fontWeight:700,color:`#1677ff`,cursor:`pointer`,userSelect:`none`},logoutBtn:{height:36,padding:`0 16px`,border:`none`,borderRadius:8,background:`#ff4d4f`,color:`#fff`,cursor:`pointer`,transition:`0.2s`},main:{flex:1,display:`flex`,overflow:`hidden`},sidebar:{width:240,padding:16,background:`#fff`,borderRight:`1px solid #e5e7eb`,overflowY:`auto`},content:{flex:1,padding:20,overflowY:`auto`},contentCard:{minHeight:`100%`,padding:24,background:`#fff`,borderRadius:16,boxShadow:`0 2px 12px rgba(0,0,0,0.04)`},menuItem:{marginBottom:4},menuTitle:{height:42,padding:`0 14px`,display:`flex`,alignItems:`center`,borderRadius:10,color:`#1f2937`,cursor:`pointer`,transition:`0.2s`},menuChildren:{marginLeft:12,paddingLeft:12,borderLeft:`1px solid #e5e7eb`}},w=({items:e=[]})=>{let t=o(),n=s();return e?.length?e.map(e=>{let{key:r,name:i,path:a,children:o,hideInMenu:s=!1}=e;if(s)return null;let c=o&&o.filter(e=>!e.hideInMenu).length>0,d=t.pathname===a,f=e=>{d||(e.currentTarget.style.background=`#f3f4f6`)},p=e=>{d||(e.currentTarget.style.background=`transparent`)},m=()=>{c||n(a)};return u(`div`,{style:C.menuItem,children:[l(`div`,{style:{...C.menuTitle,...d?{background:`#e8f3ff`,color:`#1677ff`}:{},...c?{cursor:`not-allowed`}:{}},onMouseEnter:f,onMouseLeave:p,onClick:m,children:i}),c&&l(`div`,{style:C.menuChildren,children:l(w,{items:o})})]},r)}):null},T=()=>{let t=s(),n=o(),i=()=>{t(`/`)},c=()=>{g.app.pageToLogin()};return r(()=>{if(!g.app.user||Object.keys(g.app.user).length===0)return c();t(n.pathname)},[n.pathname]),u(`div`,{style:C.layout,children:[u(`header`,{style:C.header,children:[l(`div`,{style:C.logo,onClick:i,children:`Logo`}),l(`button`,{style:C.logoutBtn,onClick:c,onMouseEnter:e=>{e.currentTarget.style.opacity=`0.75`},onMouseLeave:e=>{e.currentTarget.style.opacity=`1`},children:`退出登录`})]}),u(`div`,{style:C.main,children:[l(`aside`,{style:C.sidebar,children:l(w,{items:g.app.menus||[]})}),l(`main`,{style:C.content,children:l(`div`,{style:C.contentCard,children:l(e,{fallback:g.components.renderComponent(`Loading`,{isSuspense:!0}),children:l(a,{})})})})]})]})},E=({isInitData:e=!1,isSuspense:t=!1,isMicroApp:n=!1})=>l(`div`,{style:e?{width:`100%`,height:`100%`,display:`flex`,alignItems:`center`,justifyContent:`center`}:{},children:`Loading...`}),D={page:{height:`100vh`,display:`flex`,flexDirection:`column`,justifyContent:`center`,alignItems:`center`,gap:16},btn:{height:36,padding:`0 60px`,border:`none`,borderRadius:8,background:`#1677ff`,color:`#fff`,cursor:`pointer`,transition:`0.2s`}},O=()=>{let[e,t]=i(!1);return u(`div`,{style:D.page,children:[l(`h2`,{children:`欢迎登录系统`}),l(`button`,{style:D.btn,onClick:async()=>{if(e)return;t(()=>!0),await new Promise(e=>setTimeout(()=>e(!0),500)),t(()=>!1),g.storage.setItem(g.storage.tokenKey,`123456`);let n=g.app.getRedirectPath();g.router.navigate(n,{replace:!0}),await g.app.initData?.()},onMouseEnter:e=>{e.currentTarget.style.opacity=`0.75`},onMouseLeave:e=>{e.currentTarget.style.opacity=`1`},children:e?`登录中...`:`登录`})]})};var k=n(({name:e,rootId:t})=>{let[n,i]=f(g.store,p(e=>[e.microAppLoading,e.setMicroAppLoading]));return r(()=>{if(!e)return;let t=g.app.microAppsInstance.get(e);if(t)t?.mount();else{let n=g.app.microApps.find(t=>t.name===e);if(!n)return;i(!0),t=g.app.loadMicroApp?.(n,{},g.config.lifeCycles),t?.mountPromise?.finally(()=>{i(!1)}),g.app.microAppsInstance.set(e,t)}return()=>{t?.unmount()}},[e]),u(c,{children:[n&&g.components.renderComponent(`Loading`,{isMicroApp:!0}),l(`main`,{id:t})]})});const A=`components`,j={name:A,install(e,n={}){e[A]=b({Layout:T,Loading:E,Login:O,Microapp:k,setComponent:(t,n)=>{if(!t){console.error(`SDKComponentsPlugin - Component cannot be empty`);return}let r=n||t.displayName||t.name;if(!r){console.error(`SDKComponentsPlugin - Component name cannot be empty`);return}e.components[r]=t},getComponent:t=>t?e.components[t]:(console.error(`SDKComponentsPlugin - Component name cannot be empty`),null),renderComponent:(n,r={})=>{let i=e.components.getComponent(n);return i?t(i,r):(console.error(`SDKComponentsPlugin - Component ${n} not found`),null)}},n)}},M=`config`,N={name:M,install(e,t){e[M]=b({env:{},theme:``,locale:``,loginPath:`/login`,defaultPath:``,redirectField:`redirect`,lifeCycles:{beforeLoad:e=>console.log(`[LifeCycle] before load`,e.name),beforeMount:e=>console.log(`[LifeCycle] before mount`,e.name),afterUnmount:e=>console.log(`[LifeCycle] after unmount`,e.name)}},t)}},P=`i18n`,F={name:P,install(e,t){e[P]=b({},t)}},I=`router`,L={name:I,install(e,t){e[I]=b({location:null,navigate:null,matches:null},t)}},R=`storage`,z={name:R,install(e,t){e[R]=b({localeKey:`locale`,themeKey:`theme`,tokenKey:`token`,setItem(e,t){localStorage.setItem(e,t)},getItem(e){return localStorage.getItem(e)},removeItem(e){localStorage.removeItem(e)},clear(){localStorage.clear()}},t)}},B=(e,t)=>({locale:``,setLocale:t=>{e(()=>({locale:t})),g.config.locale=t,g.storage.setItem(g.storage.localeKey,t),document.documentElement.setAttribute(`lang`,t)}}),V=(e,t)=>({microAppLoading:!1,setMicroAppLoading:t=>e(()=>({microAppLoading:t}))}),H=(e,t)=>({theme:``,setTheme:t=>{e(()=>({theme:t})),g.config.theme=t,g.storage.setItem(g.storage.themeKey,t),document.documentElement.setAttribute(`theme`,t)}}),U={user:null,menus:[],permissions:[],settings:{}},W=(e,t)=>({userInfo:U,setUserInfo:t=>{e(()=>({userInfo:t})),g.app={...g.app,...t}},resetUserInfo:()=>{e(()=>({userInfo:U})),g.app={...g.app,...U}}}),G=e=>d()(m((...t)=>({...B(...t),...V(...t),...H(...t),...W(...t),...Object.values(e||{}).reduce((e,n)=>({...e,...n(...t)}),{})}))),K=`store`,q={name:K,install(e,t={}){e[K]=G(t)}};export{_ as SDKApiPlugin,S as SDKAppPlugin,j as SDKComponentsPlugin,N as SDKConfigPlugin,h as SDKCore,F as SDKI18nPlugin,L as SDKRouterPlugin,z as SDKStoragePlugin,q as SDKStorePlugin,g as sdk};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zxiaosi/sdk",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "A micro frontend kit",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"micro frontend",
|
|
@@ -31,19 +31,16 @@
|
|
|
31
31
|
"build": "tsdown",
|
|
32
32
|
"dev": "tsdown --watch"
|
|
33
33
|
},
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"axios": "^1.13.6"
|
|
36
|
-
},
|
|
34
|
+
"dependencies": {},
|
|
37
35
|
"devDependencies": {
|
|
38
|
-
"@tsdown/css": "^0.22.
|
|
36
|
+
"@tsdown/css": "^0.22.3",
|
|
39
37
|
"@types/react": "^18.3.12",
|
|
40
38
|
"@types/react-dom": "^18.3.1",
|
|
41
|
-
"es-toolkit": "^1.47.
|
|
42
|
-
"tsdown": "^0.22.
|
|
39
|
+
"es-toolkit": "^1.47.1",
|
|
40
|
+
"tsdown": "^0.22.3",
|
|
43
41
|
"typescript": "^5.9.3"
|
|
44
42
|
},
|
|
45
43
|
"peerDependencies": {
|
|
46
|
-
"qiankun": "^2.10.16",
|
|
47
44
|
"react": "^18.3.1 || >=19.0.0",
|
|
48
45
|
"react-dom": "^18.3.1 || >=19.0.0",
|
|
49
46
|
"react-router-dom": "^6.30.0",
|