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

# 系列文章

# 初始化配置

  • 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
  • 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
  • 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
  • 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
  • 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
  • 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