微前端(Micro-Frontends)是一种类似于微服务的架构,它将微服务的理念应用于前端,即将 Web 应用由单一的单页面应用转变为多个小型前端应用聚合为一的应用。然后各个前端应用还可以独立运行、独立开发、独立部署。
一。为什么使用微前端
1.企业级中大型项目比如做了(3年+),项目大,技术老,重构成本高,使用微前端才有新框架做。
2.新项目pc和手机端除了部分业务,其他UI和业务基本重合,做两套重复工作,做一套担心有之后差异性越来越多,代码判断非常冗长以及阅读型极度差。
二。微前端实现方式对比
(1)frame技术
- 技术成熟,支持页面嵌入、运行沙箱隔离、独立运行。
- 缺点:
1.url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用
2.全局上下文完全隔离,dom不共享,内存变量不共享。
3.慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。
(2)qiankun
- 基于single-spa封装,提供了更加开箱即用的 API,技术栈无关,任意技术栈的应用均可接入,样式隔离,JS 沙箱等。
- 缺点:
1.适配成本比较高,工程化、生命周期、静态资源路径、路由等都要做一系列的适配工作
2.css 沙箱采用严格隔离会有各种问题,js 沙箱在某些场景下执行性能下降严重。
3.无法同时激活多个子应用,也不支持子应用保活;
(3) micro-app
- 不需要修改webpack配置,是目前市面上接入微前端成本最低的方案,使用简单.零依赖,框架体积小,兼容所有框架。目前使用最多的框架。
二。microapp示例
micro官网:zeroing.jd.com/micro-app/d…
(一)创建主项目
1.npm install -g @vue/cli
2.创建项目
在控制台
切到项目 cd main-vue2-project
启动项目 npm run serve
- mian-vue2-project的控制台添加micro版本
npm install --save @micro-zoe/micro-app@0.7.1
4.在main.js入口处引入
import microApp from '@micro-zoe/micro-app'
microApp.start()
5.在router/index.js里面添加子项目路由
{
// 👇 vue2-page
path: '/vue2-page/',
name: 'vue2-page',
component: () => import( '../views/vue2-page.vue')
},
6.在views文件添加vue2-page.vue
<template>
<div>
<h1>vue2-page子应用</h1>
<!--
name(必传):应用名称
url(必传):应用地址,会被自动补全为http://localhost:3000/index.html
baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`
-->
<micro-app name='app1' url='http://localhost:8083/' baseroute='/vue2-page'></micro-app>
</div>
</template>
(二)创建子项目
1.先创建项目 vue create child-vue2-project
可以使用vue2-project-preset预设模式快速建站
在控制台
切到项目 cd child-vue2-project
启动项目 npm run serve
2.设置基础路由
(如果基座应用是history路由,子应用是hash路由,这一步可以省略)
在router/index.js里面添加子项目路由
const router = new VueRouter({
mode: 'history',
// 👇 设置基础路由,子应用可以通过window.__MICRO_APP_BASE_ROUTE__获取基座下发的baseroute,如果没有设置baseroute属性,则此值默认为空字符串
base: window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL,
// base: process.env.BASE_URL,
routes
})
3.设置跨域支持
查看项目是否有vue.config.js 文件,没有的话添加文件
module.exports = {
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
}
},
}
如果有vue.config.js 文件,则在module.exports模块里面添加
devServer: { headers: { 'Access-Control-Allow-Origin': '*', } }
(三)主项目添加子项目导航路由
在App.vue里面添加子项目路由以及菜单样式调整
<template>
<div id="app">
<div class="aside">
<div id="nav">
主项目菜单
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<router-link to="/vue2-page">child-vue2</router-link>
</div>
</div>
<div class="main">
<router-view/>
</div>
</div>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
display: flex;
}
.aside{
background: #87ceeb59;
width: 200px;
height: 100%;
}
.main{
width:80%;
}
#nav {
padding: 30px;
}
#nav a {
font-weight: bold;
color: #2c3e50;
display: block;
line-height: 40px;
}
#nav a.router-link-exact-active {
color: #42b983;
}
</style>
效果图:
(四)数据通信
1.主项目向子项目发送数据有
方法1:在 添加data属性
方法2:手动发送数据需要通过name指定接受数据的子应用,microApp.setData('项目名', 对象)
<template>
<div>
<h1>vue2-page子应用</h1>
<input type="text" v-model="inputValue">
<button @click="setData" >主项目像子项目发送数据</button>
<!--
name(必传):应用名称
url(必传):应用地址,会被自动补全为http://localhost:3000/index.html
baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`
-->
<micro-app name='child-vue2' url='http://localhost:8083/' baseroute='/vue2-page'
:data='dataForChild'
></micro-app>
</div>
</template>
<script>
import microApp from '@micro-zoe/micro-app'
export default {
data () {
return {
dataForChild: {name: ' 主项目发送给子应用的name'},
inputValue:''
}
},
methods:{
setData(){
console.log("发送给子应用的数据",this.inputValue)
// 发送数据给子应用 my-app,setData第二个参数只接受对象类型
microApp.setData('child-vue2', {inputValue: this.inputValue})
}
}
}
</script>
2.子项目获取来自主项目的数据
方法1:直接绑定window.microApp.getData() 获取数据
方法2:使用监听函数window.microApp.addDataListener
输入主项目inputValue输入框内容,点击按钮可以查看子项目数据是否变化
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
// new Vue({
// router,
// render: h => h(App)
// }).$mount('#app')
// 与基座进行数据交互
function handleMicroData () {
// 是否是微前端环境
if (window.__MICRO_APP_ENVIRONMENT__) {
// 主动获取基座下发的数据
console.log('child-vue2 getData:', window.microApp.getData())
// 监听基座下发的数据变化
window.microApp.addDataListener((data) => {
console.log(' 子项目收到的数据:', data)
})
}
}
let app = null
// 将渲染操作放入 mount 函数
function mount () {
new Vue({
router,
render: h => h(App)
}).$mount('#app')
console.log('微应用child-vue2渲染了')
handleMicroData()
}
// 将卸载操作放入 unmount 函数
function unmount () {
app = null
console.log('微应用child-vue2卸载了')
}
// 微前端环境下,注册mount和unmount方法
if (window.__MICRO_APP_ENVIRONMENT__) {
window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
} else {
// 非微前端环境直接渲染
mount()
}
3.子项目向主项目传数据
window.microApp.dispatch(对象)
<template>
<div class="about">
<h1>This is an about page</h1>
<div>返回基座下发的data数据{{value}}</div>
<input type="text" v-model="childValue">
<button @click="sendData" >子项目发送主项目数据</button>
</div>
</template>
<script>
export default {
data() {
return {
value: null,
childValue: null,
};
},
methods:{
sendData(){
console.log("子项目发送主项目的数据",this.childValue)
// dispatch只接受对象作为参数
window.microApp.dispatch({childSendValue: '子应用发送的数据'})
}
},
created(){
this.value = window.microApp.getData() // 返回基座下发的data数据
console.log("data",this.value)
},
}
</script>
4.主项目获取子项传的数据
方法1:microApp.getData(appName) // 返回子应用的data数据
方法2:在 添加@datachange='handleDataChange'方法
方法3: microApp.addDataListener(子项目名,方法)
<template>
<div>
<h1>vue2-page子应用</h1>
<input type="text" v-model="inputValue">
<button @click="setData" >主项目像子项目发送数据</button>
<div >
<button @click="getChildValue">获取子项目发送数据</button>
方法1 microApp.getData获取 {{childValue}}
</div>
<!--
name(必传):应用名称
url(必传):应用地址,会被自动补全为http://localhost:3000/index.html
baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`
-->
<hr>
<hr>
<micro-app name='child-vue2' url='http://localhost:8083/' baseroute='/vue2-page'
:data='dataForChild'
@datachange='handleDataChange'
></micro-app>
</div>
</template>
<script>
import microApp from '@micro-zoe/micro-app'
export default {
data () {
return {
dataForChild: {name: ' 主项目发送给子应用的name'},
inputValue:'',
childValue:''
}
},
methods:{
setData(){
console.log("发送给子应用的数据",this.inputValue)
// 发送数据给子应用 my-app,setData第二个参数只接受对象类型
microApp.setData('child-vue2', {inputValue: this.inputValue})
},
getChildValue(){
this.childValue = microApp.getData('child-vue2') // 返回子应用的data数据
},
handleDataChange (e) {
console.log('方法2:datachange来自子应用的数据:', e.detail.data)
}
},
mounted(){
if(!microApp) return
microApp.addDataListener('child-vue2',(data) => {
console.log(' 方法3:绑定监听函数 主项目收到子项目的数据:', data)
})
}
}
</script>