micro frontend
2022/1/12 micro
# pnpm & rush.js
- https://github.com/pnpm/pnpm
- https://rushjs.io/
- https://github.com/changesets/changesets
# yarn & leran
- https://yarnpkg.com/
- https://github.com/lerna/lerna
# microrepo
- https://github.com/micro-zoe/micro-app
- https://github.com/modern-js-dev/garfish
- https://github.com/umijs/qiankun
- https://github.com/ice-lab/icestark
- https://github.com/efoxTeam/emp
- https://github.com/vercel/turborepo
# webpack5 module federation
- https://github.com/module-federation/module-federation-examples
- https://github.com/Shenfq/Webpack5-Module-Federation-Demo
- https://github.com/yuzhanglong/mf-lite
# 系列文章
- https://github.com/a1029563229/micro-front-template
- 微前端qiankun从搭建到部署的实践 (opens new window)
- fengxianqi/qiankun-example (opens new window)
- qiankun 微前端实践及常见问题 (opens new window)
- 微前端 | 7. 微应用沙箱实现原理 (opens new window)
- 深入浅出解析阿里成熟的微前端框架 qiankun 源码 (opens new window)
# 初始化配置
- jsconfig.json
{
"include": [
"./src/**/*"
],
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution":"node",
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
},
},
"exclude": [
"node_modules","dist",
"bower_components", "jspm_packages"
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- tsconfig.json
{
"include": [
"./src/**/*"
],
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"exclude": [
"node_modules"
]
}
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
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
- vue.config.js
const path = require("path");
const { name } = require("./package");
function resolve(dir) {
return path.join(__dirname, dir);
}
const port = 7101; // dev port
module.exports = {
/**
* You will need to set publicPath if you plan to deploy your site under a sub path,
* for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
* then publicPath should be set to "/bar/".
* In most cases please use '/' !!!
* Detail: https://cli.vuejs.org/config/#publicpath
*/
outputDir: "dist",
assetsDir: "static",
filenameHashing: true,
// tweak internal webpack configuration.
// see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
devServer: {
// host: '0.0.0.0',
hot: true,
disableHostCheck: true,
port,
overlay: {
warnings: false,
errors: true,
},
headers: {
"Access-Control-Allow-Origin": "*",
},
},
// 自定义webpack配置
configureWebpack: {
resolve: {
alias: {
"@": resolve("src"),
},
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
entry: "./src/main.js",
output: {
// 把子应用打包成 umd 库格式
library: `${name}-[name]`,
libraryTarget: "umd",
jsonpFunction: `webpackJsonp_${name}`,
},
},
css: {
requireModuleExtension: true,
},
};
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
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
- public-path.js
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
1
2
3
4
2
3
4
- main.js
import "./public-path";
import Vue from "vue";
// import VueRouter from "vue-router";
import App from "./App.vue";
// import router from "./router";
import createRouter from "./router";
import store from "./store";
Vue.config.productionTip = false;
let router = null;
let instance = null;
function render(props = {}) {
const { container } = props;
router = createRouter();
instance = new Vue({
router,
store,
render: (h) => h(App),
}).$mount(container ? container.querySelector("#app") : "#app");
}
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
function storeTest(props) {
props.onGlobalStateChange &&
props.onGlobalStateChange(
(value, prev) =>
console.log(
`[onGlobalStateChange - ${props.name}]:`,
value,
prev
),
true
);
props.setGlobalState &&
props.setGlobalState({
ignore: props.name,
user: {
name: props.name,
},
});
}
export async function bootstrap() {
console.log("[vue] vue app bootstraped");
}
export async function mount(props) {
console.log("[vue] props from main framework", props);
storeTest(props);
render(props);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = "";
instance = null;
router = null;
}
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
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
- vue-router
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
},
];
const router = () => {
return new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? "/vue" : "/",
mode: "history",
routes,
});
};
export default router;
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
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