umijs / qiankun

📦 🚀 Blazing fast, simple and complete solution for micro frontends.
https://qiankun.umijs.org
MIT License
15.61k stars 2k forks source link

第三方依赖提取 #1358

Closed ppxu closed 3 years ago

ppxu commented 3 years ago

我在多个微应用里都依赖了某个第三方组件,怎么做可以让这个第三方组件只加载一次

gongshun commented 3 years ago

放在主应用加载就可以了

ppxu commented 3 years ago

放在主应用加载就可以了

不行吧,子应用单独运行的时候也要加载的呀

gongshun commented 3 years ago
  1. 把这些组件打包成一个 js 文件
  2. 主应用使用外链引入
  3. 子应用也用外链引入,但是 script 标签加一个 ignore 属性
HaibaraAiSherry commented 3 years ago

举个例子: 我们的微应用都使用了element-ui,vue,vue-router,vuex等第三方库。

  1. 在打包的时候可以将这些库通过webpack的externals排除打包范围。 webpack.config.js
    module.exports = {
    externals: {
        vue: "Vue",
        "vue-router": "VueRouter",
        vuex: "Vuex",
        "element-ui": "ELEMENT",
        echarts: "echarts",
        moment: "moment",
        axios: "axios"
    }
    };

    vue.config.js

    module.exports = {
    chainWebpack: config => {
        config.externals({
            vue: "Vue",
            "vue-router": "VueRouter",
            vuex: "Vuex",
            "element-ui": "ELEMENT",
            echarts: "echarts",
            moment: "moment",
            axios: "axios"
        });
    }
    };

2.通过html-webpack-plugin将依赖通过cdn的方式进行加载。 webpack.config.js

const HtmlWebpackInjectPlugin = require('html-webpack-inject-plugin').default

module.exports = {
    plugins: [
        // 你的其他配置
        new HtmlWebpackPlugin({
            // 其余配置
            js: [
                // 我在服务器上专门有个文件夹用来存在依赖,vue2指vue2.x的版本,后续可以让微应用使用vue3,react等。或者可以改为公共的cdn服务器。
                '/externals/vue2.js',
                '/externals/vuex3.js',
                '/externals/vue-router3.js',
                '/externals/element-ui.js',
                '/externals/axios.js',
                '/externals/echarts.js'
            ],
            css: [
                '/externals/jq-map.css'
            ]
        })
    ]
}

vue.config.js

module.exports = {
    chainWebpack: config => {
        config
            .plugin('html')
            .tap(args => {
                args[0].js = [
                    '/externals/vue2.js',
                    '/externals/vuex3.js',
                    '/externals/vue-router3.js',
                    '/externals/element-ui.js',
                    '/externals/axios.js',
                    '/externals/jq-map.js',
                    '/externals/echarts.js'
                ]
                args[0].css = [
                    '/externals/jq-map.css'
                ]
                return args
            })
    }
};

3.最后修改html文件。

<!DOCTYPE html>
<html>
<head>
    <title>Document</title>
    <% for (var i in (htmlWebpackPlugin.options.css && htmlWebpackPlugin.options.css)) { %>
    <link href="<%= htmlWebpackPlugin.options.css[i] %>">
    <% } %>
</head>
<body>
    <div id="app"></div>
    <% for (var i in (htmlWebpackPlugin.options.js && htmlWebpackPlugin.options.js)) { %>
    <script src="<%= htmlWebpackPlugin.options.js[i] %>"></script>
    <% } %>
</body>
</html>

注:主应用的依赖最好不要通过这种方式进行提取,只对微应用进行依赖提取。

lhf2 commented 2 years ago

我的子应用一用ignore跳转过去就报错,Uncaught Error: application 'vue-app' died in status SKIP_BECAUSE_BROKEN: [qiankun]: Target container with #vue-app not existed while vue-app mounting! 不用就不报是什么原因啊?

mayunbaba commented 2 years ago

方案:将三方库挂载在主应用的window对象中,子应用可以直接引用。 推荐理由: 1.上文提到的生产使用cdn,本地通过ESM引入的方式。可能会造成开发环境和生产环境引入三方库版本不一致。并且需要手工维护cdn。 2.直接将三方库挂载在window上可以规避此问题 实操:例如的微应用都使用了vue,vue-router,qs等第三方库。

1、主应用入口加载三方库并挂载在window上:

import * as Vue from 'vue';
import * as VueRouter from 'vue-router';
import * as Qs from 'qs';

function loadCommonFile() {
  window.Vue = Vue
  window.VueRouter = VueRouter
  window.Qs = Qs
}

export default loadCommonFile

2、微应用中直接正常引用例如:

import { createApp } from "vue";
import { createRouter, createWebHistory } from 'vue-router';
import Qs from "qs";

3、在微应用(注意:微应用)打包的时候可以将这些库通过webpack的externals排除打包范围。

// webpack.config.js
module.exports = {
  externals: {
    vue: "Vue",
    "vue-router": "VueRouter",
    qs: "Qs",
  },
};
// vue.config.js
module.exports = {
  chainWebpack: (config) => {
    config.externals({
      vue: "Vue",
      "vue-router": "VueRouter",
      qs: "Qs",
    });
  },
};
// vite.config.js
import externalGlobals from "rollup-plugin-external-globals";
import commonjs from "rollup-plugin-commonjs";

module.exports = defineConfig({
  build: {
    rollupOptions: {
      // https://rollupjs.org/guide/en/#big-list-of-options
      external: ["Vue", "VueRouter", "Qs"],
      plugins: [
        commonjs(),
        externalGlobals({
          vue: "Vue",
          "vue-router": "VueRouter",
          qs: "Qs",
        })
      ]
    }
  }
})

DEMO 完整地址: https://gitee.com/github-26497262/micro-fe