基于vue-cli 5 和webpack 5实现微前端

发布时间 2023-07-05 16:43:39作者: 飞天狼

有这么一个需求,项目里有很多业务模块,它们都有引用一些公共组件,每个业务模块打包后都是一个独立的应用,当公共组件修改时,单独打包公共组件,其他应用能够不需要重新构建,就能直接使用最新的公共组件,要怎么实现?

一开始我想到的是使用网络资源,就是把公共组件打包后的js文件放到服务器,其他应用通过script请求使用,尝试了一下,难以实现,放弃了。

后来找到了webpack 5的ModuleFederationPlugin,看了一下官方文档,完全适合这个需求,记录一下实现过程。

主要使用webpack 5的ModuleFederationPlugin插件实现模块共享和引用。

 

一、先创建两个vue-cli项目,分别是app1和app2

app1作为公共应用,把组件共享给其他应用使用

app2作为业务应用,引用app1共享的组件

 这里就不介绍vue-cli创建步骤了,直接跳过

 

二、app1新增组件button.vue和divider.vue

 index.js提供按需引用

// button/index.js
import Button from './button.vue'
export default {
  install(Vue) {
    Vue.component('PtButton', Button)
  }
}

common.js提供全量注册

// common.js
import './style.css'
import Button from './button/index'
import Divider from './divider/index'
const install = async (vue) => {
  [
    Button,
    Divider
  ].forEach(component => {
    vue.use(component)
  })
}
export default {
  install,
  Button,
  Divider
}

 

vue.config.js 配置

// vue.config.js
module.exports = {
  // 共享模块必须是完整路径,不能是相对路径,否则无法请求额外的css和js资源
  publicPath: process.env.NODE_ENV === 'production' ? "http://localhost:2020/peento" : "http://localhost:8095",
  chainWebpack: (config) => {
    /* splitChunks可能会和 ModuleFederationPlugin 造成冲突,先移除 */
    config.optimization.delete("splitChunks");
    config
      .plugin("module-federation-plugin")
      .use(require("webpack").container.ModuleFederationPlugin, [
        {
          name: "peento", // 模块名称
          filename: "peento.js", // 输出的文件名称
          exposes: {
            // 对外暴露的组件
            "./peento-ui": "./src/components/common.js", // 暴露所有组件,通过use方法使用
            "./button": "./src/components/button/button", // 暴露单独组件,需要注册使用
          },
          remotes: {}
        },
      ]);
  },

  devServer: {
    port: 8095,
    hot: true
  },
};

 

三、app2引用app1共享出来的组件

app2的vue.config.js配置

// app2 vue.config.js
module.exports = {
  chainWebpack(config) {
    config.optimization.delete('splitChunks')
    config
      .plugin("module-federation-plugin")
      .use(require("webpack").container.ModuleFederationPlugin, [
        {
          remotes: {
            // 生产环境所有应用域名相同的情况下,可以使用相对路径,否则需要使用完整路径
            peento: process.env.NODE_ENV === 'production' ? "peento@http://localhost:2020/peento/peento.js"  : "peento@http://localhost:8095/peento.js"
          }
        }
      ])
  },
  devServer: {
    port: 8085,
    hot: true
  },
};

  app2的mian.js引用app1的组件

// app2 main.js
import Vue from 'vue'
import App from './App.vue'
import('peento/peento-ui').then(res => {
  const peento = res.default
  Vue.use(peento) // 全量注册,可直接在组件里使用
  new Vue({
    render: h => h(App),
  }).$mount('#app')
})

  也可以直接在组件中使用

// app2 App.vue
<template>
  <div id="app">
    <PtButton>895621</PtButton>
  </div>
</template>

<script>
export default {
  name: 'App',
  components: {
    PtButton: () => import('peento/button')
  }
}
</script>

  

四、分布运行app1和app2开发环境,app2页面正常显示app1的button组件,实践成功!

 

 下面是我根据上面的需求实现的一个多模块项目,有兴趣的可以看一下;

点击查看:vue-cli-module

项目结构和配置展示: