背景
微前端架构模式早就在2016年由ThoughtWorks年提出,微前端是模仿服务端微服务的理念而应用于浏览器端,即将多个单一的单体应用组合起来拼凑成唯一应用,各个单体应用还可以独立开发、独立运行、独立部署,这也是微前端比较重要的特点。
我司要开发一个中后台项目,本身toB的中后台项目因为周期时间长会形成项目难以维护和项目过大的缺点,我是体验过要完成一个小需求,本来只需要半天的时间就可以完成,结果代码查找和打包部署直接教育了我的天真想法,而且最重要的原因我们是多个团队共同开发,为了让不同的团队之间可以独立开发和部署,互相不影响,必须选择微前端。
技术选型
微前端是指一种架构模式,他有很多种实现方式,具体有哪些我就不一一介绍了,建议大家参考前端架构从入门到微前端这本书。
我们选择的是前端微服务化的实现模式,前端微服务化是指每个前端应用完全独立,通过模块化的方式组合应用。
技术栈:single-spa + vue
准备工作
- 项目地址
- systemjs、相关模块化的知识
- single-spa框架
- vue相关技术栈
我司基本流程图:
主项目
1.安装依赖:
- Single-spa
- Systemjs
2.子项目注册(microfrontend.js):
import { registerApplication, start } from 'single-spa';
///// 文件的相关路径需要替换成自己项目实际路径
/**
*
* 获取子项目app.js文件
*/
function getApplication(path) {
return window.System.import(`${path}?time=${new Date().getTime()}`).then((res) => {
if (res.default) {
return window.System.import(res.default['app.js']).then((ret) => ret.default);
}
});
}
/**
* name: 第一个参数表示应用名称,name必须是string类型
*
* app: 加载函数 可以是一个Promise类型的 加载函数,返回的Promise resolve之后的结果必须是一个可以被解析的应用,
* 这个应用其实是一个包含single-spa各个生命周期函数 的对象(e.g: vue打包 引入后的app.js)。
*
* activeWhen: 激活函数 一个纯函数 window.loaction作为第一个参数被调用,只有函数返回的值为true时,应用才会被激活。
* 通常情况下,activity function会根据 window.location 的path来决定是否需要被激活(我就是这样玩的)
*
* single-spa根据顶级路由查找应用,而每个应用会处理自身的子路由。以下场景,single-spa会调用应用的activity funtion
* 1.hashchange or popstate 事件触发时(vue-router hash或history路由模式都会触发)
* 2.pushState or replaceState被调用时
* 3.在single-spa 手动调用 triggerAppChange 方法
* 4.checkActivityFunctions 方法被调用时
*
* customProps
* 对象 可以表示自定义字段,子应用生命周期函数可以获取
* 函数 两个参数 应用的名称和window.location
*/
const development = process.env.NODE_ENV === 'development';
/// web 部署根域名
const baseUrl = process.env.VUE_APP_WEB_URL;
const configProject = [
{
name: 'app1',
app: window.System.import('app-demo1').then((res) => res.default), /// 子项目通过插件完成配置的引入方式
activeWhen: (location) => location.pathname.startsWith('/app1'),
customProps: {
// 对象
everything: 'just do it'
}
// customProps: (name, location) => {
// // 函数
// return {everything: 'just do it'};
// }
},
{
name: 'app2',
app: getApplication(development ? 'http://localhost:8993/manifest.json' : `${baseUrl}/app2/manifest.json`),
activeWhen: (location) => location.pathname.startsWith('/app2'),
customProps: {
// 对象
everything: 'just do it'
}
}
];
/// 注册子应用
configProject.forEach((element) => {
registerApplication(element);
});
// 暴露single-spa启动方法 可以控制子应用的激活时机
export function appStart() {
return start();
}
3.修改入口文件(main.js):
相关逻辑代码,全部代码可以查看项目。
// 引入启动方法
import { appStart } from './microfrontend';
// Single-spa启动方法在此处调用,是因为我们项目一般需要前置条件才会容许项目挂载 挂载应用
/// e.g 比如登录
// async function login() {
// await 相关接口请求
// appStart();
// }
appStart();
4.修改router.js文件:
5.import maps:
<!-- 配置导入映射的,模块必须符合以下三种规范之一:
1. System.register - https://github.com/systemjs/systemjs/blob/master/docs/system-register.md
2. UMD ( 推荐 )
3. Global variable
-->
e.g:
<script type="systemjs-importmap">
{
"imports": {
"vue": "https://cdn.jsdelivr.net/npm/vue@2.6.11",
"vue-router": "https://unpkg.com/vue-router@3.2.0/dist/vue-router.js",
"vuex": "https://unpkg.com/vuex@3.4.0",
"lodash": "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js",
"dayjs": "https://unpkg.com/dayjs@1.8.21/dayjs.min.js",
"app-demo1": "http://localhost:8992/js/app.js?time=<%=version%>"
}
}
</script>
子项目
子项目(vue)的改造有两种方法,第一个是通过插件去修改相关配置(不推荐),第二个是自己动手修改相关配置,两种方式都在项目中使用了,分别代表的项目是app-demo1和app-demo2。
手动配置
1.安装相关依赖:
- single-spa-vue
- webpack-manifest-plugin
2.修改入口文件(main.js):
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
// 新增
import singleSpaVue from 'single-spa-vue';
Vue.config.productionTip = false;
const appOptions = {
render: (h) => h(App),
router, // 路由
store // vuex
};
// 判断是微前端加载还是独立运行
if (!window.singleSpaNavigate) {
new Vue(appOptions).$mount('#app');
}
const vueLifecycles = singleSpaVue({
Vue, //(必传项) 主Vue对象
appOptions
});
// single-spa 生命周期函数 三个生命周期函数必须都有
export const bootstrap = vueLifecycles.bootstrap;
export const mount = vueLifecycles.mount;
export const unmount = vueLifecycles.unmount;
3.修改vue.config.js文件(修改webpack配置):
const packageName = require('./package.json').name;
const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); // 资源清单 协助主应用加载资源
module.exports = {
// 设定好publicPath, 端口最好是一个固定值
publicPath: process.env.NODE_ENV === 'production' ? '/app-demo2' : 'http://localhost:8993',
configureWebpack: (config) => {
config.output.libraryTarget = 'umd'; // 打包格式为umd 配合模块加载工具加载项目
config.output.library = packageName;
config.output.jsonpFunction = `webpackJsonp_${packageName}`;// 防止全局变量webpackJsonp冲突
// 删除chunk-vendors.js文件,公共三方模块打包进app.js文件
config.optimization.splitChunks.cacheGroups = {};
//config.optimization.delete('splitChunks');
// 打包时移除这些通用库,配合systemjs从root加载
// 注意⚠️ 和上文importmap相关联
config.externals = ['vue', 'vue-router', 'vuex', 'lodash', 'dayjs'];
/// 主项目加载的xxx.mainifest.json文件
config.plugins.push(
new WebpackManifestPlugin({
fileName: 'manifest.json'
// filter: function(option) {
// return option.isInitial;
// }
})
);
},
devServer: {
headers: {
// 开发模式下解决微前端加载时跨域的问题
'Access-Control-Allow-Origin': '*'
}
},
css: {
// css不单独打包成一个文件,和js打包进一个文件 这个也会造成css污染问题,通过其他方式避免。
extract: false
}
};
插件
前提是通过vuecli创建的项目;通过插件修改的前提是子项目最好是新项目,因为插件会修改main.js文件,会造成代码丢失。
终端执行:
Vue add single-spa
这个插件帮助我们做了哪些事情呢???
注意事项
1.css样式隔离:
2.js隔离:
3.应用之间通信:
参考
- 每日优鲜微前端实践
- 字节跳动微前端实践
- 爱奇艺微前端实践
- 美团微前端实践
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!