背景
公司的我的项目古老,采纳的是传统的 jsp 模式开发页面。短期内没有重构的打算,新性能的新增,是持续采纳 jsp 的模式开发还是寻求其余形式,是一个令人纠结的问题。
恰好此时,网上微前端的声音越来越响,与之相干的 webpack5 module federation 也逐步被推广进去。是否利用 webpack5 的这个新特点,来解决我的窘境呢?
module federation
令我兴奋的特点:
- 反对我的项目导出组件供其余我的项目应用(家喻户晓)
- 导出的组件,是挂在 window 下的,能够通过
window[container].get([module])
获取相应的组件
想法
只在 jsp 我的项目,引入组件,且执行组件的渲染,具体的业务逻辑代码放在一个独自的,相似“基站”的我的项目。
基站跟我的项目的关系大略如下图:
界面形成如下图:
入手
资料
- (提供组件)一个 vue 我的项目
- (应用组件)一个 jsp 我的项目 / 一个简略 html 我的项目
vue 我的项目
采纳 @efox/emp 脚手架搭建,导出了两个组件:table
button
须要留神的是,因为应用组件者是没有 webpack 的我的项目,vue 我的项目没法通过 shared 与之共享组件。于是通过 cdn 的形式引入 vue 和 element-plus,使组件能胜利渲染进去。
vue 我的项目的 emp-config.js
const withVue3 = require('@efox/emp-vue3')
const path = require('path')
const ProjectRootPath = path.resolve('./')
const {getConfig} = require(path.join(ProjectRootPath, './src/config'))
module.exports = withVue3(({config, env, empEnv}) => {
const confEnv = env === 'production' ? 'prod' : 'dev'
const conf = getConfig(empEnv || confEnv)
const port = conf.port
const publicPath = conf.publicPath
// 设置我的项目 URL
config.output.publicPath(publicPath)
config.externals({
'vue': 'Vue',
'element-plus':'ElementPlus'
})
// 设置我的项目端口
config.devServer.port(port)
config.plugin('mf').tap(args => {console.info(args)
args[0] = {...args[0],
name: "wComponent",
// 被近程引入的文件名
filename: 'table.js',
exposes:{
'./table':'./src/components/Layout.vue',
'./button':'./src/components/Button.vue'
}
}
return args
})
// 配置 index.html
config.plugin('html').tap(args => {args[0] = {...args[0],
...{
// head 的 title
title: 'jsp-provider',
// 近程调用我的项目的文件链接
files: {js: ['https://unpkg.com/vue@3.0.7/dist/vue.global.js','https://unpkg.com/element-plus@1.0.2-beta.35/lib/index.full.js'],
css: ['https://unpkg.com/element-plus/lib/theme-chalk/index.css']
},
},
}
return args
})
})
应用组件者 index.html
在这里,只需引入依赖的 vue、element-plus 和下面我的项目提供的组件入口http://localhost:8066/table.js
,便可应用组件了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" />
<!-- EMP inject title -->
<title>jsp-provider</title>
<!-- EMP inject css -->
<link rel="stylesheet" href="https://unpkg.com/element-plus/lib/theme-chalk/index.css" />
<!-- EMP inject js -->
<script src="https://unpkg.com/vue@3.0.7/dist/vue.global.js"></script>
<script src="https://unpkg.com/element-plus@1.0.2-beta.35/lib/index.full.js"></script>
<link rel="icon" href="http://localhost:8066/favicon.ico">
<script src="http://localhost:8066/table.js"></script>
</head>
<body>
<h3> 这里是表格 </h3>
<div id="emp-root"></div>
<h3> 这里是按钮 </h3>
<div id="button"></div>
</body>
<script defer>
window.wComponent.get('./table').then((factory)=>{var module = factory();
var app = Vue.createApp(module.default);
app.use(ElementPlus);
app.mount('#emp-root');
})
window.wComponent.get('./button').then((factory)=>{var module = factory();
var app = Vue.createApp(module.default);
app.use(ElementPlus);
app.mount('#button');
})
</script>
</html>
剖析
module federation (模块联邦)
官网有一个 Example 给了我启发:
源码中上面这个代码,是间接在 window 取被共享的组件容器的。那么,非 webpack 的我的项目,也能够通过同样的形式拿到。
const container = window[scope]; // or get the container somewhere else
查看导出的文件,容器 wComponent
同过 var
定义的。
容器会暴露出两个办法
其中 get
办法能够通过传入模块的名字,获取到一个 promise 对象
在 promise 对象中,返回了一个 factory
,通过执行,失去相应的组件。
上面这个就是咱们在 Vue 我的项目中导出的 table 组件
如愿拿到导出的组件,那接下来,就依照 Vue 组件如何渲染到 DOM 上实现就能够了。
window.wComponent.get('./table').then((factory)=>{var module = factory();
var app = Vue.createApp(module.default);
app.use(ElementPlus);
app.mount('#emp-root');
}