项目介绍

  • 整个 SDK 都是围绕 sdk.app.usersdk.app.menus 进行设计的,旨在简化微前端应用的开发

  • 主应用 sdk.use(plugin, opts).mount('sdk') → 微应用 sdk.extend('sdk') → 共享同一套状态与能力

  • 架构图

  • 架构图地址

  • 可参考项目

具体使用

主应用

  • 创建 Vite 项目

    1
    npm create vite@latest
  • 安装 @zxiaosi/sdkreact-router-dom@6qiankun 依赖

    1
    npm install @zxiaosi/sdk react-router-dom@6 qiankun
  • src 文件夹下创建 sdk.config.ts 文件,并进行 SDK 的配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import {
    sdk,
    SDKApiPlugin,
    SDKAppPlugin,
    SDKComponentsPlugin,
    SDKConfigPlugin,
    SDKI18nPlugin,
    SDKRouterPlugin,
    SDKStoragePlugin,
    SDKStorePlugin,
    } from "@zxiaosi/sdk";

    import { loadMicroApp } from "qiankun";

    sdk
    .use(SDKApiPlugin)
    .use(SDKAppPlugin, { loadMicroApp })
    .use(SDKComponentsPlugin)
    .use(SDKConfigPlugin)
    .use(SDKI18nPlugin)
    .use(SDKRouterPlugin)
    .use(SDKStoragePlugin)
    .use(SDKStorePlugin)
    .mount("sdk");
  • src/main.ts 中引入 sdk.config.ts 文件

    1
    2
    3
    4
    5
    import { createRoot } from "react-dom/client";
    import App from "./App.tsx";
    import "./sdk.config.ts";

    createRoot(document.getElementById("root")!).render(<App />);
  • 运行项目 npm run dev,并在控制台输入 sdk 查看 SDK 是否初始化成功

  • src/App.tsx 中使用 sdk

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    import { sdk } from "@zxiaosi/sdk";
    import { useEffect, useState } from "react";
    import {
    Navigate,
    RouterProvider,
    createBrowserRouter,
    useLocation,
    useMatches,
    useNavigate,
    } from "react-router-dom";

    const loader = (loading: boolean) => sdk.store.getState().setMicroAppLoading(loading);

    const microApps = [
    {
    name: "subapp1",
    container: "#sub-app",
    entry: "http://localhost:5174",
    loader: loader,
    props: { sdk },
    },
    {
    name: "subapp2",
    container: "#sub-app",
    entry: "http://localhost:5175",
    loader: loader,
    props: { sdk },
    },
    ];

    /** 记录路由信息 */
    const WithRouter = ({ children }: any) => {
    sdk.router.location = useLocation();
    sdk.router.matches = useMatches();
    sdk.router.navigate = useNavigate();
    return children;
    };

    function App() {
    const loginPath = sdk.config.loginPath;
    const renderComponent = sdk.components.renderComponent;

    const defaultRoutes = [
    { path: loginPath, element: renderComponent("Login") },
    { path: "*", element: <div>404</div> },
    ].map((_) => ({ ..._, element: <WithRouter>{_.element}</WithRouter> }));

    const [routes, setRoutes] = useState(defaultRoutes);

    const initData = () => {
    sdk.app.user = { name: "zxiaosi" };
    sdk.app.microApps = microApps;

    sdk.app.menus = [
    { key: "Home", name: "Home", path: "/home", element: <div>Home</div> },
    {
    key: "Subapp1",
    name: "Subapp1",
    path: "/subapp1",
    element: renderComponent("Microapp", { name: "subapp1", rootId: "sub-app" }),
    },
    {
    key: "Subapp2",
    name: "Subapp2",
    path: "/subapp2",
    element: renderComponent("Microapp", { name: "subapp2", rootId: "sub-app" }),
    },
    ];

    const allRoutes = [
    ...defaultRoutes,
    { path: "/", element: <Navigate to={"/home"} replace /> },
    {
    path: "/",
    element: <WithRouter>{renderComponent("Layout")}</WithRouter>,
    children: sdk.app.menus,
    },
    ];

    setRoutes(allRoutes);
    };

    useEffect(() => {
    sdk.app.initData = initData;

    const pathName = window.location.pathname;

    if ([loginPath].includes(pathName)) return;
    initData();
    }, []);

    return (
    <RouterProvider router={createBrowserRouter(routes)} future={{ v7_startTransition: false }} />
    );
    }

    export default App;

微应用

  • 创建 Vite 项目

    1
    npm create vite@latest
  • 安装 @zxiaosi/sdkvite-plugin-qiankun-lite 依赖

    1
    2
    npm install @zxiaosi/sdk
    npm install vite-plugin-qiankun-lite -d
  • vite.config.ts 中配置 vite-plugin-qiankun-lite 插件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { defineConfig } from "vite";
    import react from "@vitejs/plugin-react";
    import qiankun from "vite-plugin-qiankun-lite";

    export default defineConfig({
    plugins: [react(), qiankun({ name: "subapp", sandbox: !!process.env.VITE_SANDBOX })],
    server: {
    port: 5174,
    },
    });
  • src/main.ts 中进行 SDK 的继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    import { sdk } from "@zxiaosi/sdk";
    import { createRoot, type Root } from "react-dom/client";
    import App from "./App.tsx";
    import { name } from "../package.json";

    let root: Root;
    const render = (props: any = {}) => {
    const container = props?.container
    ? props.container.querySelector("#root")
    : document.getElementById("root");

    root = createRoot(container);

    root.render(<App />);
    };

    if (!window.__POWERED_BY_QIANKUN__) {
    render();
    }

    export async function bootstrap() {
    console.log(`${name} bootstrap`);
    }

    export async function mount(props: any) {
    console.log(`${name} mount`, props);
    sdk.extend(props?.sdk?.name); // 继承 sdk 功能
    render(props);
    }

    export async function unmount(props: any) {
    console.log(`${name} unmount`, props);
    root.unmount();
    }

    export async function update(props: any) {
    console.log(`${name} update`, props);
    }

自定义插件

  • 插件内容 nameinstall

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import type { SDKPlugin } from "@zxiaosi/sdk";

    /** 插件名称 */
    const pluginName = "test";

    const SDKTestPlugin: SDKPlugin<"test"> = {
    name: pluginName,
    install(sdk, options = {}) {
    sdk[pluginName] = options;
    },
    };

    export { SDKTestPlugin };
  • 插件类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // vite-env.d.ts

    import "@zxiaosi/sdk";

    declare module "@zxiaosi/sdk" {
    interface SDKPlugins {
    /** 自定义插件 **/
    test: {};
    }
    }
  • 使用插件

    1
    sdk.use(SDKTestPlugin).mount("sdk");

注意事项

SDK 未初始化

  • SDK 不能直接进行使用,否则会报 SDK 未初始化 错误

  • 可以在 组件中 或者 方法中 中使用

    1
    2
    3
    4
    5
    6
    const handle = () => { sdk.xxx };

    const App = () => {
    sdk.xxx
    return <></>
    }

React 多实例

  • 微应用使用 sdk.components.renderComponent('xxx') 时,会报 React 多实例 的错误,需要使用 vite-plugin-externals 插件将 React 等依赖排除在打包之外。更多详情

  • 主应用index.html 中使用 cdn 的方式去加载 reactreact-dom

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!doctype html>
    <html lang="en">
    <head>
    <title>%VITE_APP_TITLE%</title>
    <script src="https://unpkg.com/react@18.3.1/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js"></script>
    </head>
    <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
    </body>
    </html>
  • 主/微应用 中使用 vite-plugin-externals 插件去排除 reactreact-dom

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import { viteExternalsPlugin } from "vite-plugin-externals";

    export default defineConfig({
    plugins: [
    viteExternalsPlugin({
    react: "React",

    // 开发环境不排除 react-dom 依赖, 防止热更新失效
    // 或者 浏览器安装 React Developer Tools 插件
    "react-dom": "ReactDOM",
    "react-dom/client": "ReactDOM",
    }),
    ],
    });

相关技术

仓库地址