微前端micro示例

102 阅读6分钟

微前端(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.创建项目 截屏2023-10-14 下午11.07.10.png

在控制台

切到项目 cd main-vue2-project

启动项目 npm run serve

http://localhost:8082/

  1. 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

http://localhost:8083/

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>

效果图:

截屏2023-10-14 下午11.47.57.png

(四)数据通信

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>