
微前端现有的落地方案可以分为三类,自组织模式、基座模式以及模块加载模式。
# 一、为什么需要微前端?
这里我们通过3W(what,why,how)的方式来讲解什么是微前端:
# 1.What?什么是微前端?

微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。
微前端的核心在于拆, 拆完后再合!
# 2.Why?为什么去使用他?
- 不同团队间开发同一个应用技术栈不同怎么破?
- 希望每个团队都可以独立开发,独立部署怎么破?
- 项目中还需要老的应用代码怎么破?
我们是不是可以将一个应用划分成若干个子应用,再将子应用打包成一个个的lib呢?当路径切换时加载不同的子应用,这样每个子应用都是独立的,技术栈也就不用再做限制了!从而解决了前端协同开发的问题。
# 3.How?怎样落地微前端?

- 2018年
Single-SPA诞生了,single-spa是一个用于前端微服务化的JavaScript前端解决方案 (本身没有处理样式隔离、js执行隔离) 实现了路由劫持和应用加载; - 2019年 qiankun基于Single-SPA, 提供了更加开箱即用的 API (
single-spa + sandbox + import-html-entry),它 做到了技术栈无关,并且接入简单(有多简单呢,像iframe一样简单)
总结:子应用可以独立构建,运行时动态加载,主子应用完全解耦,并且技术栈无关,靠的是协议接入(这里提前强调一下:子应用必须导出
bootstrap、mount、unmount三个方法)。
这里先回答一下大家可能会有的疑问:
这不是iframe吗?
如果使用的是
iframe,当iframe中的子应用切换路由时用户刷新页面就尴尬了。
应用间如何通信?
- 基于URL来进行数据传递,但是这种传递消息的方式能力较弱
- 基于CustomEvent实现通信;
-
- 基于props主子应用间通信;
- 使用全局变量、Redux进行通信
如何处理公共依赖?
- CDN -
externals - webpack
联邦模块
# 二、SingleSpa实战
# 1.构建子应用
首先创建一个vue子应用,并通过
single-spa-vue来导出必要的生命周期:
vue create spa-vue
npm install single-spa-vue
@前端进阶之旅: 代码已经复制到剪贴板
// main.js
import singleSpaVue from 'single-spa-vue';
const appOptions = {
el: '#vue',
router,
render: h => h(App)
}
// 在非子应用中正常挂载应用
if(!window.singleSpaNavigate){
delete appOptions.el;
new Vue(appOptions).$mount('#app');
}
const vueLifeCycle = singleSpaVue({
Vue,
appOptions
});
// 子应用必须导出以下生命周期:bootstrap、mount、unmount
export const bootstrap = vueLifeCycle.bootstrap;
export const mount = vueLifeCycle.mount;
export const unmount = vueLifeCycle.unmount;
export default vueLifeCycle;
@前端进阶之旅: 代码已经复制到剪贴板
配置子路由基础路径
// router.js
const router = new VueRouter({
mode: 'history',
base: '/vue', //改变路径配置
routes
})
@前端进阶之旅: 代码已经复制到剪贴板

# 2.配置库打包
将子模块打包成类库
//vue.config.js
module.exports = {
configureWebpack: {
// 把属性挂载到window上方便父应用调用 window.singleVue.bootstrap/mount/unmount
output: {
library: 'singleVue',
libraryTarget: 'umd'
},
devServer:{
port:10000
}
}
}
@前端进阶之旅: 代码已经复制到剪贴板

# 3.主应用搭建
<div id="nav">
<router-link to="/vue">vue项目router-link>
<div id="vue">div>
div>
@前端进阶之旅: 代码已经复制到剪贴板
将子应用挂载到
id="vue"标签中
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import {registerApplication,start} from 'single-spa'
Vue.config.productionTip = false
async function loadScript(url) {
return new Promise((resolve,reject)=>{
let script = document.createElement('script')
script.src = url
script.onload = resolve
script.onerror = reject
document.head.appendChild(script)
})
}
// 注册应用
registerApplication('myVueApp',
async ()=>{
console.info('load')
// singlespa问题
// 加载文件需要自己构建script标签 但是不知道应用有多少个文件
// 样式不隔离
// 全局对象没有js沙箱的机制 比如加载不同的应用 每个应用都用同一个环境
// 先加载公共的
await loadScript('http://localhost:10000/js/chunk-vendors.js')
await loadScript('http://localhost:10000/js/app.js')
return window.singleVue // bootstrap mount unmount
},
// 用户切换到/vue下 我们需要加载刚才定义的子应用
location=>location.pathname.startsWith('/vue'),
)
start()
new Vue({
router,
render: h => h(App)
}).$mount('#app')
@前端进阶之旅: 代码已经复制到剪贴板

# 4.动态设置子应用publicPath
if(window.singleSpaNavigate){
__webpack_public_path__ = 'http://localhost:10000/'
}
@前端进阶之旅: 代码已经复制到剪贴板
# 三、qiankun实战
qiankun是目前比较完善的一个微前端解决方案,它已在蚂蚁内部经受过足够大量的项目考验及打磨,十分健壮。这里附上官网。
# 1.主应用编写
<template>
<!--注意这里不要写app 否则跟子应用的加载冲突
<div id="app">-->
<div>
<el-menu :router="true" mode="horizontal">
<!-- 基座中可以放自己的路由 -->
<el-menu-item index="/">Home</el-menu-item>
<!-- 引用其他子应用 -->
<el-menu-item index="/vue">vue应用</el-menu-item>
<el-menu-item index="/react">react应用</el-menu-item>
</el-menu>
<router-view />
<!-- 其他子应用的挂载节点 -->
<div id="vue" />
<div id="react" />
</div>
</template>
<style>
</style>
@前端进阶之旅: 代码已经复制到剪贴板
注册子应用
import { registerMicroApps,start } from 'qiankun'
// 基座写法
const apps = [
{
name: 'vueApp', // 名字
// 默认会加载这个HTML,解析里面的js动态执行 (子应用必须支持跨域)
entry: '//localhost:10000',
container: '#vue', // 容器
activeRule: '/vue', // 激活的路径 访问/vue把应用挂载到#vue上
props: { // 传递属性给子应用接收
a: 1,
}
},
{
name: 'reactApp',
// 默认会加载这个HTML,解析里面的js动态执行 (子应用必须支持跨域)
entry: '//localhost:20000',
container: '#react',
activeRule: '/react' // 访问/react把应用挂载到#react上
},
]
// 注册
registerMicroApps(apps)
// 开启
start({
prefetch: false // 取消预加载
})
@前端进阶之旅: 代码已经复制到剪贴板
# 2.子Vue应用
// src/router.js
const router = new VueRouter({
mode: 'history',
// base里主应用里面注册的保持一致
base: '/vue',
routes
})
@前端进阶之旅: 代码已经复制到剪贴板
这里不要忘记子应用的钩子导出。
// vue.config.js
module.exports = {
devServer:{
port:10000,
headers:{
'Access-Control-Allow-Origin':'*' //允许访问跨域
}
},
configureWebpack:{
// 打umd包
output:{
library:'vueApp',
libraryTarget:'umd'
}
}
}
@前端进阶之旅: 代码已经复制到剪贴板
# 3.子React应用
再起一个子应用,为了表明技术栈无关特性,这里使用了一个
React项目:
重写react中的webpack配置文件 (config-overrides.js)
yarn add react-app-rewired --save-dev
@前端进阶之旅: 代码已经复制到剪贴板
修改package.json文件
// react-scripts 改成 react-app-rewired
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
@前端进阶之旅: 代码已经复制到剪贴板
在根目录新建配置文件
// 配置文件重写
touch config-overrides.js
@前端进阶之旅: 代码已经复制到剪贴板
配置.env文件
根目录新建
.env
React路由配置

# 四、飞冰微前端实战
# 4.1 react主应用编写
# 4.2 vue子应用接入

