Webpack 5 增加了一个新的功能 “模块联邦”,它允许多个 webpack 构建一起工作。从运行时的角度来看,多个构建的模块将表现得像一个巨大的连接模块图。从开发者的角度来看,模块可以从指定的远程构建中导入,并以最小的限制来使用。
多个独立的构建可以组成一个应用程序,这些独立的构建之间不应该存在依赖关系,因此可以单独开发和部署它们。这通常被称作微前端,但并不仅限于此。
在这里插入代码片Webpack5 模块联邦让 Webpack 达到了线上 Runtime 的效果,让代码直接在项目间利用 CDN 直接共享,不再需要本地安装 Npm 包、构建再发布了! 我们知道 Webpack 可以通过 DLL 或者 Externals 做代码共享时 Common Chunk, 但不同应用和项目间这个任务就变得困难了,我们几乎无法在项目之间做到按需热插拔。
模块联邦可以将一个应用的包应用于另一个应用,同时具备整体应用一起打包的公共依赖抽取能力。
APP1
components/uploadFile.vue
<template>
<div class="upload_wrap">
<el-upload>示例上传组件</el-upload>
</div>
</template>
components/index.js
import uploadFile from './uploadFile/uploadFile'
export {
uploadFile
}
vue.config.js
const { defineConfig } = require('@vue/cli-service')
const ModuleFederationPlugin =
require('webpack').container.ModuleFederationPlugin
let publicPath
if (process.env.NODE_ENV === 'development') {
publicPath = 'http://localhost:9001'
} else if (process.env.VUE_APP_CURRENTMODE === 'test') {
publicPath = '测试环境域名/xlt-ms-component-library'
} else if (process.env.VUE_APP_CURRENTMODE === 'prod') {
publicPath = '正式环境域名/xlt-ms-component-library'
}
module.exports = defineConfig({
transpileDependencies: true,
publicPath: publicPath,
lintOnSave: process.env.NODE_ENV === 'development',
configureWebpack: {
plugins: [
new ModuleFederationPlugin({
name: 'remoteEntry',
filename: 'remoteEntry.js',
exposes: {
'./components': './src/components/index',
...
}
})
]
},
devServer: {
port: 9001
},
chainWebpack: (config) => {
config.optimization.delete('splitChunks')
}
})
APP2(第一种使用方法,webpack 配置引用)
vue.config.js (配置加载远程库)
const { defineConfig } = require('@vue/cli-service')
const ModuleFederationPlugin =
require('webpack').container.ModuleFederationPlugin
let remoteUrl
if (process.env.NODE_ENV === 'development') {
remoteUrl = 'http://localhost:9001'
} else if (process.env.VUE_APP_CURRENTMODE === 'test') {
remoteUrl = '测试域名/xlt-ms-component-library'
} else if (process.env.VUE_APP_CURRENTMODE === 'prod') {
remoteUrl = '正式域名/xlt-ms-component-library'
}
module.exports = defineConfig({
publicPath: '/fangliang-ms',
transpileDependencies: true,
lintOnSave: process.env.NODE_ENV === 'development',
configureWebpack: {
plugins: [
new ModuleFederationPlugin({
name: 'clitest',
filename: 'remoteEntry.js',
remotes: {
remoteEntry: `remoteEntry@${remoteUrl}/remoteEntry.js`
}
})
]
}
})
views/uploadFile.vue (页面使用示例)
<template>
<uploadFile />
</template>
<script>
import { uploadFile } from 'remoteEntry/components'
export default {
components: {
uploadFile
}
}
</script>
APP2(第二种使用方法 动态引用)
vue.config.js
不做配置引用
utils/moduleFederationPlugin.js
import Vue from 'vue'
import baseURL from '@/env.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 = () => {
reject(false)
}
document.head.appendChild(element)
})
}
const loadComponent = (scope, module) => {
return async () => {
await __webpack_init_sharing__('default')
const container = window[scope]
await container.init(__webpack_share_scopes__.default)
const factory = await window[scope].get(module)
const Module = factory()
return Module
}
}
export const moduleFederationPlugin = () => {
return new Promise(async (resolve, reject) => {
if (!window.EcRootMs) {
await useDynamicScript(`${baseURL.remoteUrl}?date=${Date.now()}`)
const components = await loadComponent('remoteEntry', './components')()
const views = await loadComponent('remoteEntry', './views')()
Object.keys({ ...components, ...views }).forEach(key => {
Vue.component(`xlt${key}`, components[key])
})
await loadComponent('remoteEntry', './publicStyle')()
const publicJs = await loadComponent('remoteEntry', './publicJs')()
const remoteRouter = await loadComponent('remoteEntry', './remoteRouter')()
window.EcRootMs = {
publicJs,
remoteRouter
}
}
resolve(true)
})
}
main.js (此处仅为示例,因为加载远程组件为异步加载,在使用远程库的时候必须在其加载完成)
moduleFederationPlugin().then(async res => {
window.EcRootMs.publicJs.setRem()
const router = await routerRoot()
new Vue({
router,
store,
render: (h) => h(App)
}).$mount('#app')
})
|