├── .gitignore
├── .npmrc
├── README.md
├── package.json
├── packages
├── main
│ ├── .env
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .husky
│ │ ├── commit-msg
│ │ └── pre-commit
│ ├── .lintstagedrc
│ ├── .npmrc
│ ├── .prettierignore
│ ├── .prettierrc
│ ├── .stylelintrc.js
│ ├── .umirc.ts
│ ├── README.md
│ ├── config
│ │ └── routes.tsx
│ ├── mock
│ │ └── userAPI.ts
│ ├── package.json
│ ├── src
│ │ ├── access.ts
│ │ ├── app.css
│ │ ├── app.tsx
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── components
│ │ │ ├── CustomErrorBoundary
│ │ │ │ └── index.tsx
│ │ │ └── Guide
│ │ │ │ ├── Guide.less
│ │ │ │ ├── Guide.tsx
│ │ │ │ └── index.ts
│ │ ├── constants
│ │ │ └── index.ts
│ │ ├── hooks
│ │ │ └── useBrowserHistory.ts
│ │ ├── models
│ │ │ └── global.ts
│ │ ├── pages
│ │ │ ├── Access
│ │ │ │ └── index.tsx
│ │ │ ├── Exception404
│ │ │ │ └── index.tsx
│ │ │ ├── Home
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Theme
│ │ │ │ ├── index.module.less
│ │ │ │ └── index.tsx
│ │ │ ├── global.ts
│ │ │ └── load
│ │ │ │ └── index.tsx
│ │ ├── services
│ │ │ └── demo
│ │ │ │ ├── UserController.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── typings.d.ts
│ │ └── utils
│ │ │ ├── format.ts
│ │ │ └── helper.ts
│ ├── tsconfig.json
│ └── typings.d.ts
├── sub-app-1
│ ├── .editorconfig
│ ├── .env
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .prettierrc
│ ├── .umirc.ts
│ ├── README.md
│ ├── mock
│ │ └── .gitkeep
│ ├── package.json
│ ├── src
│ │ ├── app.css
│ │ ├── app.ts
│ │ ├── layouts
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── pages
│ │ │ ├── index.less
│ │ │ ├── index.tsx
│ │ │ ├── one
│ │ │ │ └── index.tsx
│ │ │ ├── theme
│ │ │ │ ├── index.js
│ │ │ │ └── index.module.less
│ │ │ └── two
│ │ │ │ └── index.tsx
│ │ └── utils
│ │ │ └── qiankunConfig.ts
│ ├── tsconfig.json
│ └── typings.d.ts
├── sub-app-2
│ ├── .env
│ ├── .gitignore
│ ├── README.md
│ ├── craco.config.js
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── robots.txt
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── public-path.js
│ │ ├── reportWebVitals.js
│ │ └── setupTests.js
└── sub-app-3
│ ├── .env
│ ├── .gitignore
│ ├── README.md
│ ├── craco.config.js
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ └── src
│ ├── App.js
│ ├── App.less
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── pages
│ ├── 404
│ │ └── index.js
│ ├── one
│ │ └── index.js
│ ├── theme
│ │ ├── index.js
│ │ └── index.module.less
│ ├── three
│ │ └── index.js
│ └── two
│ │ └── index.js
│ ├── public-path.js
│ ├── reportWebVitals.js
│ └── setupTests.js
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # misc
7 | .DS_Store
8 |
9 | # ide
10 | .idea
11 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | auto-install-peers=true
2 | strict-peer-dependencies=false
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # qiankun+umi4+react+antd 微前端构建指南
2 |
3 | ### 前言
4 | 随时umi4的到来, 我把qiankun学习计划也提上了日程, 在系统的学习一周后, 下面通过umi4+qiankun+react 展开说说我的学习成果吧~
5 |
6 | ### 项目结构
7 | - 主应用(基于umi4构建)
8 | - 微应用(基于umi3构建)
9 | - 微应用2(基于react-create-app构建)
10 | - 微应用3(react-create-app + router构建)
11 |
12 | ### 主要完成以下功能:
13 |
14 | #### 挂载
15 | - 基于qiankun提供的loadMicroApp方法
16 | - 基于umi-plugin-qiankun提供的MicroApp , MicroAppWithMemoHistory 组件
17 | - 基于umi-plugin-qiankun提供的路由声明形式绑定挂载
18 | - 微应用嵌套微应用挂载
19 |
20 | #### 通信
21 | - 基于qiankun-apps注册时的props属性透传
22 | - 基于qiankun提供的全局状态-initGlobalState
23 | - 基于umi-plugin-qiankun提供的hooks-useQiankunStateForSlave
24 | - umi微应用使用hooks-useModel, hoc-connectMaster方式获取内容
25 |
26 | #### 样式隔离
27 | - 基于qiankun提供的sandbox-experimentalStyleIsolation实现样式隔离
28 | - 基于css-loader添加前缀
29 |
30 | #### 错误处理
31 | - 基于qiankun提供的autoCaptureError属性开启组件异常捕获
32 | - 基于qiankun提供的errorBoundary属性实现自定义异常页面
33 | - 基于qiankun提供的addGlobalUncaughtErrorHandler全局异常捕获
34 |
35 | ### 环境准备
36 |
37 | clone
38 | ```
39 | https://github.com/xoptimal/qiankun-demo.git
40 | ```
41 | 项目使用pnpm管理依赖, 请务先安装pnpm
42 | ```
43 | npm install -g pnpm
44 | ```
45 | 安装依赖
46 | ```
47 | pnpm i
48 | ```
49 | 启动项目
50 | ```
51 | pnpm dev
52 |
53 | http://localhost:5000
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 | * [qiankun官网](https://qiankun.umijs.org/)
79 | * [umi4-微前端配置指引](https://umijs.org/docs/max/micro-frontend)
80 | * [umi3-微前端配置指引](https://v3.umijs.org/zh-CN/plugins/plugin-qiankun)
81 | * [在 create-react-app 中使用 - Ant Design](https://ant-design.gitee.io/docs/react/use-with-create-react-app-cn)
82 |
83 | ### 最后
84 |
85 | 欢迎交流, 如果本项目对你有帮助的话, 欢迎━(*`∀´*)ノ亻! ⭐⭐⭐ ~
86 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "qiankun-demo-monorepo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "author": "",
7 | "license": "ISC",
8 | "scripts": {
9 | "dev": "pnpm -r --parallel --filter=./packages/* run dev",
10 | "dev:main": "pnpm --filter=./packages/main run dev",
11 | "dev:sub1": "pnpm --filter=./packages/sub-app-1 run dev",
12 | "dev:sub2": "pnpm --filter=./packages/sub-app-2 run dev",
13 | "dev:sub3": "pnpm --filter=./packages/sub-app-3 run dev"
14 | },
15 | "devDependencies": {
16 | "pnpm": "^7.8.0"
17 | },
18 | "packageManager": "pnpm@7.8.0"
19 | }
20 |
--------------------------------------------------------------------------------
/packages/main/.env:
--------------------------------------------------------------------------------
1 | PORT=5000
2 | BROWSER=true
3 |
--------------------------------------------------------------------------------
/packages/main/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@umijs/max/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/main/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /.env.local
3 | /.umirc.local.ts
4 | /config/config.local.ts
5 | /src/.umi
6 | /src/.umi-production
7 | /.umi
8 | /.umi-production
9 | /.umi-test
10 | /dist
11 | /.mfsu
12 |
--------------------------------------------------------------------------------
/packages/main/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx --no-install max verify-commit $1
5 |
--------------------------------------------------------------------------------
/packages/main/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx --no-install lint-staged --quiet
5 |
--------------------------------------------------------------------------------
/packages/main/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.{md,json}": [
3 | "prettier --cache --write"
4 | ],
5 | "*.{js,jsx}": [
6 | "max lint --fix --eslint-only",
7 | "prettier --cache --write"
8 | ],
9 | "*.{css,less}": [
10 | "max lint --fix --stylelint-only",
11 | "prettier --cache --write"
12 | ],
13 | "*.ts?(x)": [
14 | "max lint --fix --eslint-only",
15 | "prettier --cache --parser=typescript --write"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/main/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmmirror.com
2 | strict-peer-dependencies=false
3 |
--------------------------------------------------------------------------------
/packages/main/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .umi
3 | .umi-production
4 |
--------------------------------------------------------------------------------
/packages/main/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "trailingComma": "all",
5 | "proseWrap": "never",
6 | "overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }],
7 | "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-packagejson"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/main/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@umijs/max/stylelint'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/main/.umirc.ts:
--------------------------------------------------------------------------------
1 | import {defineConfig} from '@umijs/max';
2 | import routes from "./config/routes";
3 |
4 | export default defineConfig({
5 | antd: {
6 | configProvider: {
7 | prefixCls: 'mainAnt',
8 | },
9 | },
10 | access: {},
11 | model: {},
12 | initialState: {},
13 | request: {},
14 | layout: {
15 | title: '@umijs/max',
16 | },
17 | lessLoader: {
18 | modifyVars: {
19 | '@ant-prefix': 'mainAnt',
20 | "primary-color": "#004FD9"
21 | },
22 | javascriptEnabled: true,
23 | },
24 | routes,
25 | npmClient: 'pnpm',
26 | qiankun: {
27 | master: {
28 | prefetch: false
29 | }
30 | },
31 | mfsu: false
32 | });
33 |
34 |
--------------------------------------------------------------------------------
/packages/main/README.md:
--------------------------------------------------------------------------------
1 | # umi
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | A framework in react community ✨
11 |
12 | > Please consider following this project's author, [sorrycc](https://github.com/sorrycc), and consider starring the project to show your ❤️ and support.
13 |
14 | ### [🚀 Read the launch post →](https://umijs.org/blog/umi-4-rc)
15 |
16 | ### [📚 Learn Umi →](https://umijs.org/)
17 |
18 | ## Contribution
19 |
20 | See [Contributing Guide](https://umijs.org/docs/introduce/contributing).
21 |
22 | ### Core Maintainers
23 |
24 | Core Maintainres are community members who have contributed a significant amount of time and energy to the project through issues, bug fixes, implementing enhancements/features.
25 |
26 | * [sorrycc](https://github.com/sorrycc)
27 | * [xiaohuoni](https://github.com/xiaohuoni)
28 |
29 | ### Maintainers
30 |
31 | Maintainers are community members who have 10 or more PRs merged in umi, or have spend lot's of time on umi community or issues.
32 |
33 | * [PeachScript](https://github.com/PeachScript)
34 | * [YdreamW](https://github.com/YdreamW)
35 | * [yuaanlin](https://github.com/yuaanlin)
36 | * [fz6m](https://github.com/fz6m)
37 | * [stormslowly](https://github.com/stormslowly)
38 | * [xierenyuan](https://github.com/xierenyuan)
39 | * [siyi98](https://github.com/siyi98)
40 | * [txp1035](https://github.com/txp1035)
41 |
42 | ### Contributors
43 |
44 | Contributors are community members who have 1 or more PR merged in umi. Contributors can contact me[[sorrycc](https://github.com/sorrycc)] to join the Contributor Group.
45 |
46 |
47 |
48 | ## Community
49 |
50 | * [交流和反馈群](https://fb.umijs.org/)
51 |
52 | ## LICENSE
53 |
54 | [MIT](./LICENSE)
55 |
--------------------------------------------------------------------------------
/packages/main/config/routes.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default [
4 | {
5 | path: '/',
6 | redirect: '/home',
7 | },
8 | {
9 | path: '/home',
10 | name: 'home',
11 | component: '/Home',
12 | icon: 'HomeOutlined'
13 | },
14 | {
15 | name: 'react-micro-app',
16 | path: '/sub-app-2',
17 | component: '/load',
18 | icon: 'SmileOutlined',
19 | },
20 | {
21 | name: 'react-router-micro-app',
22 | path: '/sub-app-3',
23 | microApp: 'sub-app-3',
24 | icon: 'SmileOutlined',
25 | routes: [
26 | {
27 | name: '嵌套路由1',
28 | path: '/sub-app-3/one',
29 | },
30 | {
31 | name: '嵌套路由2',
32 | path: '/sub-app-3/three',
33 | },
34 | {
35 | path: '/sub-app-3',
36 | redirect: '/sub-app-3/one',
37 | },
38 | ]
39 | },
40 | {
41 | name: 'umi3-micro-app',
42 | path: '/sub-app-1',
43 | layout: true,
44 | microApp: 'sub-app-1',
45 | icon: 'SmileOutlined',
46 | routes: [
47 | {
48 | name: '应用间通信',
49 | path: '/sub-app-1/one',
50 | },
51 | {
52 | name: '应用间嵌套',
53 | path: '/sub-app-1/two',
54 | },
55 | {
56 | name: '应用间通信',
57 | path: '/sub-app-1/sub-app-3',
58 | routes: [
59 | {
60 | name: '嵌套路由1',
61 | path: '/sub-app-1/sub-app-3/one',
62 | },
63 | {
64 | name: '嵌套路由2',
65 | path: '/sub-app-1/sub-app-3/three',
66 | },
67 | ]
68 | }
69 | ]
70 | },
71 | {
72 | path: '/theme',
73 | name: 'theme',
74 | component: '/Theme',
75 | icon: 'SmileOutlined'
76 | },
77 | {
78 | path: '/404',
79 | component: '/Exception404',
80 | },
81 | ]
82 |
--------------------------------------------------------------------------------
/packages/main/mock/userAPI.ts:
--------------------------------------------------------------------------------
1 | const users = [
2 | { name: 'Umi', nickName: 'U', gender: 'MALE' },
3 | { name: 'Fish', nickName: 'B', gender: 'FEMALE' },
4 | ];
5 |
6 | export default {
7 | 'GET /api/v1/queryUserList': (req: any, res: any) => {
8 | res.json({
9 | success: true,
10 | data: { list: users },
11 | errorCode: 0,
12 | });
13 | },
14 | 'PUT /api/v1/user/': (req: any, res: any) => {
15 | res.json({
16 | success: true,
17 | errorCode: 0,
18 | });
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/packages/main/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "author": "",
4 | "scripts": {
5 | "dev": "max dev",
6 | "build": "max build",
7 | "format": "prettier --cache --write .",
8 | "prepare": "husky install",
9 | "postinstall": "max setup",
10 | "setup": "max setup",
11 | "start": "npm run dev"
12 | },
13 | "dependencies": {
14 | "@ant-design/icons": "^4.7.0",
15 | "@ant-design/pro-components": "^1.1.3",
16 | "@umijs/max": "^4.0.9",
17 | "antd": "^4.20.7",
18 | "qiankun": "^2.7.4"
19 | },
20 | "devDependencies": {
21 | "@types/react": "^18.0.0",
22 | "@types/react-dom": "^18.0.0",
23 | "husky": "^8.0.1",
24 | "lint-staged": "^13.0.3",
25 | "prettier": "^2.7.1",
26 | "prettier-plugin-organize-imports": "^2",
27 | "prettier-plugin-packagejson": "^2",
28 | "typescript": "^4.1.2"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/main/src/access.ts:
--------------------------------------------------------------------------------
1 | export default (initialState: API.UserInfo) => {
2 | // 在这里按照初始化数据定义项目中的权限,统一管理
3 | // 参考文档 https://next.umijs.org/docs/max/access
4 | const canSeeAdmin = !!(
5 | initialState && initialState.name !== 'dontHaveAccess'
6 | );
7 | return {
8 | canSeeAdmin,
9 | };
10 | };
11 |
--------------------------------------------------------------------------------
/packages/main/src/app.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | .link {
4 | color: #2572E6;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/main/src/app.tsx:
--------------------------------------------------------------------------------
1 | // 运行时配置
2 | import {addGlobalUncaughtErrorHandler, initGlobalState, MicroAppStateActions} from "qiankun";
3 | import React, {useState} from "react";
4 | import {notification} from 'antd';
5 |
6 | import './app.css'
7 | import CustomErrorBoundary from "@/components/CustomErrorBoundary";
8 | import {getMicroAppRouteComponent} from "@@/plugin-qiankun-master/getMicroAppRouteComponent";
9 | import {customFetch} from "@/utils/helper";
10 |
11 | // 全局初始化数据配置,用于 Layout 用户信息和权限初始化
12 | // 更多信息见文档:https://next.umijs.org/docs/api/runtime-config#getinitialstate
13 | export async function getInitialState(): Promise<{ name: string }> {
14 | return {name: '@umijs/max'};
15 | }
16 |
17 | export const layout = () => {
18 |
19 | const [openKeys, setOpenKeys] = useState([])
20 |
21 | return {
22 | logo: 'https://img.alicdn.com/tfs/TB1YHEpwUT1gK0jSZFhXXaAtVXa-28-27.svg',
23 | menu: {
24 | locale: false,
25 | },
26 | menuProps: {
27 | openKeys
28 | },
29 | onOpenChange: setOpenKeys,
30 | rightContentRender: false,
31 | token: {
32 | sider: {
33 | menuBackgroundColor: '#004FD9',
34 | menuTextColor: 'rgba(255,255,255,0.85)',
35 | subMenuSelectedTextColor: '#fff',
36 | menuTextColorSecondary: 'rgba(255,255,255,0.65)',
37 | menuSelectedTextColor: '#fff',
38 | menuTitleTextColor: 'rgba(255,255,255,0.95)',
39 | menuItemHoverBgColor: 'rgba(0,0,0,0.06)',
40 | menuItemCollapsedHoverBgColor: 'rgba(0,0,0,0.06)',
41 | menuItemSelectedBgColor: 'rgba(0,0,0,0.15)',
42 | menuItemCollapsedSelectedBgColor: 'rgba(0,0,0,0.15)',
43 | menuItemDividerColor: 'rgba(255,255,255,0.15)',
44 | collapsedButtonBgColor: '#fff',
45 | collapsedButtonTextColor: 'rgba(0,0,0,0.45)',
46 | collapsedButtonHoverTextColor: 'rgba(0,0,0,0.65)',
47 | menuSubArrowColor: 'rgba(255,255,255,0.15)',
48 | },
49 | appListIconTextColor: 'rgba(255,255,255,0.85)',
50 | appListIconHoverTextColor: 'rgba(255,255,255,0.95)',
51 | appListIconHoverBgColor: 'rgba(0,0,0,0.06)',
52 | },
53 | };
54 | };
55 |
56 | const callback = (data: { name: string, message: string }) => {
57 | notification.open({
58 | type: 'info',
59 | message: `来自${data.name}的Reply`,
60 | description: data.message,
61 | onClick: () => {
62 | console.log('Notification Clicked!');
63 | },
64 | });
65 | }
66 |
67 | export function patchClientRoutes({routes}: any) {
68 | routes[0].children.forEach((item: any, index: number) => {
69 | if (item.microApp) {
70 | console.log("item", item)
71 | routes[0].children[index].element = getMicroAppRouteComponent({
72 | appName: item.microApp,
73 | base: item.microAppProps?.base || '/',
74 | routePath: item.path,
75 | masterHistoryType: item.microAppProps?.hisory || 'browser',
76 | routeProps: {
77 | errorBoundary: (error: any) =>
78 | }
79 | })()
80 | }
81 | })
82 | }
83 |
84 | const state = {
85 | slogan: 'Hello MicroFrontend from qiankun-initGlobalState',
86 | callback
87 | }
88 |
89 | // 初始化 state
90 | const actions: MicroAppStateActions = initGlobalState(state);
91 |
92 | // 监听数据变化
93 | actions.onGlobalStateChange((state, prev) => {
94 | // update
95 | //actions.setGlobalState(state);
96 | });
97 |
98 | // 取消监听
99 | // actions.offGlobalStateChange();
100 |
101 | // export function patchClientRoutes({ routes }) {
102 | // console.log('routes', routes)
103 | // }
104 |
105 | export const qiankun: any = {
106 | apps: [
107 | {
108 | name: 'sub-app-1',
109 | entry: '//localhost:5001',
110 | activeRule: '/sub-app-1',
111 | container: '#micro-app-1',
112 | props: {
113 | autoCaptureError: true,
114 | base: '/sub-app-1',
115 | defaultProps: {
116 | slogan: 'Hello MicroFrontend from qiankun-apps-props',
117 | callback
118 | }
119 | },
120 | },
121 | {
122 | name: 'sub-app-2',
123 | entry: '//localhost:5002',
124 | activeRule: '/sub-app-2',
125 | container: '#micro-app-2',
126 | sandbox: {
127 | experimentalStyleIsolation: true
128 | },
129 | },
130 | {
131 | name: 'sub-app-3',
132 | entry: '//localhost:5003',
133 | activeRule: '/sub-app-3',
134 | container: '#micro-app-3',
135 | sandbox: {
136 | experimentalStyleIsolation: true
137 | },
138 | },
139 | ],
140 | lifeCycles: {
141 | afterMount: (props: any) => {
142 | // 这里需要做一次set才能保证微应用能触发到change, 以便能拿到state
143 | actions.setGlobalState(state);
144 | },
145 | beforeLoad: (props: any) => {
146 | },
147 | beforeMount: (props: any) => {
148 | },
149 | beforeUnmount: (props: any) => {
150 | },
151 | },
152 | fetch: customFetch
153 | };
154 |
155 | // umi下自带的父子通信方式
156 | export function useQiankunStateForSlave() {
157 | return {
158 | slogan: 'Hello MicroFrontend from umi-useQiankunStateForSlave',
159 | callback
160 | };
161 | }
162 |
163 | // 捕获全局微应用错误
164 | addGlobalUncaughtErrorHandler((event) => {
165 | // 这里会频繁触发, 注意使用
166 | })
167 |
--------------------------------------------------------------------------------
/packages/main/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoptimal/qiankun-demo/343568f12490d33967acff0636bfac9fe25de293/packages/main/src/assets/.gitkeep
--------------------------------------------------------------------------------
/packages/main/src/components/CustomErrorBoundary/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {Button, Result} from "antd";
3 | import {history} from '@umijs/max';
4 |
5 | export default function CustomErrorBoundary(props: { error?: any }) {
6 |
7 | const handleClick = () => {
8 | history.push('/home')
9 | }
10 |
11 | let status: any = 500, title = "500", subTitle = "Sorry, something went wrong.";
12 |
13 | return (
14 | Back Home}
19 | />
20 | )
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/packages/main/src/components/Guide/Guide.less:
--------------------------------------------------------------------------------
1 | .title {
2 | margin: 0 auto;
3 | font-weight: 200;
4 | }
5 |
--------------------------------------------------------------------------------
/packages/main/src/components/Guide/Guide.tsx:
--------------------------------------------------------------------------------
1 | import { Layout, Row, Typography } from 'antd';
2 | import React from 'react';
3 | import styles from './Guide.less';
4 |
5 | interface Props {
6 | name: string;
7 | }
8 |
9 | // 脚手架示例组件
10 | const Guide: React.FC = (props) => {
11 | const { name } = props;
12 | return (
13 |
14 |
15 |
16 | 欢迎使用 {name} !
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default Guide;
24 |
--------------------------------------------------------------------------------
/packages/main/src/components/Guide/index.ts:
--------------------------------------------------------------------------------
1 | import Guide from './Guide';
2 | export default Guide;
3 |
--------------------------------------------------------------------------------
/packages/main/src/constants/index.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_NAME = 'Umi Max';
2 |
--------------------------------------------------------------------------------
/packages/main/src/hooks/useBrowserHistory.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { history } from '@umijs/max';
3 |
4 | // 处理子应用push操作,父应用的菜单高亮不正确BUG
5 | export function useBrowserHistory() {
6 | useEffect(() => {
7 | const handlePopState = (event: any) => {
8 | const { href } = event.target.location; // eg: http://localhost:1200/#/bps/producMange/imageAlbum?a=b
9 | // const pathNameWithSearch = href.split('#')[1]; // eg: /bps/producMange/imageAlbum?a=b
10 | // const pathname = pathNameWithSearch.split('?')[0];
11 | // console.group('---handlePopState---');
12 | //
13 | // console.log('event=>pathname-->', pathname);
14 | // console.log('event=>pathNameWithSearch-->', pathNameWithSearch);
15 | // console.groupEnd();
16 |
17 | console.log('history.pathname-->', history.location.pathname);
18 | console.log('href-->', href);
19 |
20 | //
21 | // // 如果2个不一样,说明是子应用自己push的路由
22 | // if (history.location.pathname !== pathname) {
23 | // history.replace(pathNameWithSearch);
24 | // }
25 | };
26 | window.addEventListener('popstate', handlePopState);
27 | return () => {
28 | window.removeEventListener('popstate', handlePopState);
29 | };
30 | }, []);
31 | }
32 |
--------------------------------------------------------------------------------
/packages/main/src/models/global.ts:
--------------------------------------------------------------------------------
1 | // 全局共享数据示例
2 | import { DEFAULT_NAME } from '@/constants';
3 | import { useState } from 'react';
4 |
5 | const useUser = () => {
6 | const [name, setName] = useState(DEFAULT_NAME);
7 | return {
8 | name,
9 | setName,
10 | };
11 | };
12 |
13 | export default useUser;
14 |
--------------------------------------------------------------------------------
/packages/main/src/pages/Access/index.tsx:
--------------------------------------------------------------------------------
1 | import {PageContainer} from '@ant-design/pro-components';
2 | import {Access, MicroAppWithMemoHistory, useAccess} from '@umijs/max';
3 | import {Button} from 'antd';
4 |
5 | const AccessPage: React.FC = () => {
6 | const access = useAccess();
7 | return (
8 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default AccessPage;
23 |
--------------------------------------------------------------------------------
/packages/main/src/pages/Exception404/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CustomErrorBoundary from "@/components/CustomErrorBoundary";
3 |
4 | export default function () {
5 |
6 | return
7 | }
8 |
--------------------------------------------------------------------------------
/packages/main/src/pages/Home/index.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoptimal/qiankun-demo/343568f12490d33967acff0636bfac9fe25de293/packages/main/src/pages/Home/index.less
--------------------------------------------------------------------------------
/packages/main/src/pages/Home/index.tsx:
--------------------------------------------------------------------------------
1 | import Guide from '@/components/Guide';
2 | import { trim } from '@/utils/format';
3 | import { PageContainer } from '@ant-design/pro-components';
4 | import { useModel } from '@umijs/max';
5 | import styles from './index.less';
6 | import React from "react";
7 |
8 | const HomePage: React.FC = () => {
9 | const { name } = useModel('global');
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default HomePage;
20 |
--------------------------------------------------------------------------------
/packages/main/src/pages/Theme/index.module.less:
--------------------------------------------------------------------------------
1 |
2 |
3 | .link {
4 | color: #2572E6;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/main/src/pages/Theme/index.tsx:
--------------------------------------------------------------------------------
1 | import {Button, Divider, Space} from "antd";
2 | import {PageContainer} from "@ant-design/pro-components";
3 | import {MicroApp} from "@umijs/max";
4 | import React from "react";
5 | import CustomErrorBoundary from "@/components/CustomErrorBoundary";
6 | import styles from './index.module.less'
7 |
8 | const defaultParams = {
9 | base: '/',
10 | url: '/theme',
11 | settings: {
12 | sandbox: {
13 | experimentalStyleIsolation: true
14 | }
15 | },
16 | // 自动捕获错误, 吊起ant
17 | // autoCaptureError: true
18 | // 自定义异常页面
19 | errorBoundary: (error: any) =>
20 | }
21 |
22 | const text = `
23 | // CSS Modules to index.module.less
24 | .link { color: red }
25 | 我是Link
26 |
27 | // 内联样式
28 | 我是Link
29 |
30 | // 外联样式 to src/app.css
31 | 我是Link
32 |
33 | // 默认样式
34 | 我是Link
35 | `
36 |
37 | export default function Theme() {
38 |
39 | return (
40 |
44 | {text}
45 |
46 |
47 | 主应用(main)
48 |
49 |
antd-组件库样式
50 |
51 |
52 |
53 |
54 |
55 |
56 |
CSS Modules
57 |
我是Link
58 |
59 |
63 |
67 |
71 |
72 |
73 |
74 | 微应用(sub-app-1)
75 |
76 |
77 | 微应用(sub-app-3)
78 |
79 |
80 |
81 | )
82 | }
83 |
--------------------------------------------------------------------------------
/packages/main/src/pages/global.ts:
--------------------------------------------------------------------------------
1 |
2 | console.log("============global")
3 |
--------------------------------------------------------------------------------
/packages/main/src/pages/load/index.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useRef, useState} from "react";
2 | import {PageContainer} from '@ant-design/pro-components';
3 | import {MicroApp} from '@umijs/max';
4 | import {loadMicroApp} from "qiankun";
5 | import {customFetch} from "@/utils/helper";
6 | import {Divider, Modal, Typography} from "antd";
7 |
8 | const {Title, Text, Paragraph} = Typography;
9 |
10 | let microApp: any = null;
11 |
12 | const LoadMicroApp: React.FC = () => {
13 |
14 | const [replyMessage, setReplyMessage] = useState('')
15 |
16 | const containerRef = useRef(null)
17 |
18 | const handleOk = (type: number) => {
19 | if (type === 2) {
20 | setReplyMessage("you're welcome")
21 | } else {
22 | microApp.update({
23 | base: '/',
24 | replyMessage: "you're welcome",
25 | message: 'hello sub-app-3, load as loadMicroApp function',
26 | callback: (message: string) => callback(message, 3)
27 | })
28 | }
29 | }
30 |
31 | const callback = (message: string, type: number) => {
32 | Modal.success({
33 | title: `来自微应用sub-app-${type}的信息`,
34 | content: message,
35 | okText: "reply you're welcome",
36 | onOk: () => handleOk(type)
37 | });
38 | }
39 |
40 | useEffect(() => {
41 | if (containerRef.current) {
42 | microApp = loadMicroApp({
43 | name: 'sub-app-3',
44 | entry: '//localhost:5003',
45 | container: containerRef.current,
46 | props: {
47 | base: '/',
48 | message: 'hello sub-app-3, load as loadMicroApp function',
49 | callback: (message: string) => callback(message, 3)
50 | },
51 | }, {
52 | // @ts-ignore
53 | fetch: customFetch
54 | });
55 | }
56 | return () => {
57 | microApp?.unmount()
58 | microApp = null
59 | }
60 | }, [containerRef.current])
61 |
62 | return (
63 |
67 |
68 |