webpack微前端动态引入(笔记)

395 阅读2分钟

学习笔记 webpack模块联邦 必须项:ModuleFederationPlugin

1.引入服务

服务一(console_home)

/*
 * @Description: 配置文件
 * @Author: xiao di
 * @Date: 2021-07-14 08:36:52
 * @LastEditTime: 2022-08-27 17:34:53
 */
/**
 * @file 本文件为部署不同环境等全局变量配置文件
 */
module.exports = {
    auth_core: {
        url: '127.0.0.1',
        post: 3000
    }
}

服务引入文件

/*
 * @Description: 服务映射 
 * @Author: xiao di
 * @Date: 2022-08-24 19:08:07
 * @LastEditTime: 2022-08-27 17:43:34
 */
const host = require('./public/conf/app-conf.js')
const fileName = 'remoteEntry.js'
let publicPath = null
if (process.env.NODE_ENV === 'development') {
  // 开发环境
  publicPath = 'http://localhost' // webpack 公共路径
}

// 动态加载远程 js 文件
const useDynamicScript = (url) => {
  return new Promise((resolve, reject) => {
    const element = document.createElement('script')
    element.src = url
    element.type = 'text/javascript'
    element.async = true
    element.onload = (e) => {
      resolve(true)
    }
    element.onerror = (e) => {
      console.error('加载远程服务失败,具体原因', e);
      reject(false)
    }
    document.head.appendChild(element)
  })
}
// 加载组件
const loadComponent = (scope, module) => {
  return async () => {
    // 初始化共享作用域。使用此版本和所有远程提供的已知模块填充它
     __webpack_init_sharing__('default')
    const container = window[scope] // 或者到别的地方去拿容器
    // 初始化容器,它可以提供共享模块
    container.init(__webpack_share_scopes__.default)
    const factory = await window[scope].get(module)
    const Module = factory()
    return Module
  }
} 
// 获取指定组件或者视图
export async function get(scope, module, name) {
  var components =  await loadComponent(scope, module)()
  return components[name]
}

// 处理联邦组件所有导出项
export const moduleFederationPlugin = () => {
  return new Promise(async (resolve, reject) => {
    if (!window.EcRootMs) {
      // 加载远程库 TODO: 后期放在 npm 包内
      for (const key in host) {
        await useDynamicScript(`${ (publicPath || host[key].url) + ":" + host[key].post}/${fileName}?date=${Date.now()}`) // 远程模块地址
      }
      window.EcRootMs = {
        publicJS: false
      }
    }
    resolve(true)
  })
}

VUE加载文件(此处仅为示例,因为加载远程组件为异步加载,在使用远程库的时候必须在其加载完成)**


moduleFederationPlugin().then(() => {
    new Vue({
        router,
        store,
        render: h => h(App)
    }).$mount('#app');
})

调用方式

<template>
	<div id="app">
		<new-header></new-header>
		<router-view />
	</div>
</template>
<script>
import {get} from '../serviceName'
export default {
	components:{
		newHeader: () => get('auth_core', './components', 'NewHeader')
	},
    data() {
        return {};
    },
	mounted () {
	}
}
</script>

服务二 (auth_core)

统一抛出文件

module.exports = {
    './components': './share/components',
    './publicJS': './share/public',
    './remoRouter': './share/remoRouter',
    './views': './share/views'
}

抛出公共组件

/*
 * @Description: 共享公共组件内容
 * @Author: xiao di
 * @Date: 2022-08-27 15:50:52
 * @LastEditTime: 2022-08-27 17:14:03
 */
import NewHeader from '../src/components/Header.vue'
export {
    NewHeader
}

webpack.config.js

/*
 * @Description: 
 * @Author: xiao di
 * @Date: 2021-07-13 09:43:58
 * @LastEditTime: 2022-08-27 16:04:43
 */
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HTMLWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require("webpack").container;
const share = require('./share/index.js');
console.log(share);
module.exports = {
    devtool: false,
    entry: './src/main.js',
    mode: "development",
    devServer: {
      port: 3000,
      contentBase: path.join(__dirname, "dist"),
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/
            },
          {
            test: /\.vue$/,
            loader: 'vue-loader'
          }
        ]
    },
    plugins: [
        // 请确保引入这个插件!
        new VueLoaderPlugin(),
        new HTMLWebpackPlugin({
            template: path.resolve(__dirname, './public/index.html')
        }),
        new ModuleFederationPlugin({
            // 提供给其他服务加载的文件
            filename: "remoteEntry.js",
            // 唯一ID,用于标记当前服务
            name: "auth_core",
            library: { type: "var", name: "auth_core" },
            // 需要暴露的模块,使用时通过 `${name}/${expose}` 引入
            exposes: share
          })
      ]
}