微前端應用及基于qiankun的微前端實踐

示例代碼倉庫:
yl-qiankun-base:https://gitee.com/dongche/yl-qiankun-base.git
yl-qiankun-child-vue:https://gitee.com/dongche/yl-qiankun-child-vue.git

微前端概念起源

微前端概念最早其實是借鑒了微服務的概念,最早是出現在2016年的ThoughtWorks Technology Radar(ThoughtWorks技術雷達)

什么是微前端

MicroFrontends 官方解釋:用來構建能夠讓 多個團隊 獨立交付項目代碼的 現代web app 技術,策略以及實踐方法
MicroFrontends 官網:https://swearer23.github.io/micro-frontends/
所以微前端并不是一個單純的技術點,而是一個為了解決復雜應用,特別時便于以后維護的思路和方法。
核心思路:根據功能模塊拆解應用,然后根據需求組裝,應用之間可通信
特點: 1.模塊可以使用不同技術棧
2.應用隔離,各應用可獨立部署,可獨立運行
3.應用之間松耦合
4.可漸進式遷移

用圖做個簡單說明:


qiankun 圖示.png

擴展理解:組件化的延伸,由項目內的組件化擴展到項目之間的組件化

微前端的應用場景

1.老系統的迭代:復雜的老系統,可能由于各種原因,代碼風格不一,代碼冗雜,質量參差不齊,然后還要不斷加入新功能。
2.復雜應用功能模塊的拆分和細化,便于各團隊同步進行開發和維護
3.有比較大功能模塊需要在不同的項目中使用
4.需求不定,經常有對項目進行拆分和融合
5.平臺型應用。方便刪減功能模塊和外部接入

目前市面上的主流微前端方案及框架

微前端模式

  • 自由組織模式
    沒有形式,自由嵌套
  • 基座模式
    有一個父級主應用為容器基座,其他應用需要注冊接入
  • 去中心模式
    各應用之間各自為政又可以彼此分享資源,沒有基座不存在主次關系

微前端方案

  • iframe 方案

    • 優點:html 提供的標簽,天然隔離,任意嵌套,能加載任意web應用。適合自由組織自由嵌套模式,無需配置,簡單易用

    缺點:參考qiankun技術圓桌Why Not Iframe

    1. url 不同步。瀏覽器刷新 iframe url 狀態丟失、后退前進按鈕無法使用。
    2. UI 不同步,DOM 結構不共享。想象一下屏幕右下角 1/4 的 iframe 里來一個帶遮罩層的彈框,同時我們要求這個彈框要瀏覽器居中顯示,還要瀏覽器 resize 時自動居中..
    3. 全局上下文完全隔離,內存變量不共享。iframe 內外系統的通信、數據同步等需求,主應用的 cookie 要透傳到根域名都不同的子應用中實現免登效果
    4. 慢。每次子應用進入都是一次瀏覽器上下文重建、資源重新加載的過程。
  • web Components 方案
    Web Components 是一套 瀏覽器原生組件,由google發起。
    web Components 三大技術:

    • Custom elements(自定義元素):一組JavaScript API,允許您定義custom elements及其行為,然后可以在您的用戶界面中按照需要使用它們。
    • Shadow DOM(影子DOM):一組JavaScript API,用于將封裝的“影子”DOM樹附加到元素(與主文檔DOM分開呈現)并控制其關聯的功能。通過這種方式,您可以保持元素的功能私有,這樣它們就可以被腳本化和樣式化,而不用擔心與文檔的其他部分發生沖突。
    • HTML templates(HTML模板): <template> 和 <slot> 元素使您可以編寫不在呈現頁面中顯示的標記模板。然后它們可以作為自定義元素結構的基礎被多次重用

    具備實現微前端的特點:
    1.技術棧無關:瀏覽器原生支持,和框架沒有關系
    2.獨立運行,應用隔離:Shadow DOM 特性

    1. web Componets組件獨立開發,相互之間沒有依賴關聯,但是又可以作為組件自由引入組裝

    最大缺點:目前瀏覽器兼容性差
    參照 can i use

    image.png

web Components 偏向基座模式。
目前國內京東的 micro-app 借鑒了web components 的思想

  • 基座模式的基于single-spa 的路由劫持方案
    這是目前主流的微前端方案,single-spa 也是最早成熟的微前端方案,特別適用于單頁面應用。

    single-spa是通過監聽 url change 事件,在路由變化時匹配到渲染的子應用并進行渲染,這個思路也是目前實現微前端的主流方式。同時single-spa要求子應用修改渲染邏輯并暴露出三個方法:bootstrap、mount、unmount,分別對應初始化、渲染和卸載,這也導致子應用需要對入口文件進行修改。single-spa 最大問題是配置麻煩
    qiankun出自阿里,基于single-spa進行封裝,繼承single-spa特性,但是使用簡單,上手相對容易

  • 去中心化模式的 ESM 方案
    ESM 是 ES Module 的縮寫,是 Ecma script 2015 中提出的一種前端模塊化手段。ESM方案webpack5模塊聯邦,多個應用可以互相嵌套,可以深入到組件導入導出
    ESM 方案可視作 ES6 模塊化的擴展,使用遠程加載ESM模塊方式。

    成熟方案參考:
    EMP官網
    EMP github

基座模式:主應用基于vue及qiankun 的微前端實踐

qiankun 基于 single-spa 封裝,是目前市面上最主流的微前端方案,出自阿里。
qiankun 官網:https://qiankun.umijs.org/zh/guide
qinkun 技術圓桌:https://www.yuque.com/kuitos/gky7yw/nwgk5a

image.png

簡單說下qiankun

這里我們不對qiankun做什么深入點剖析,想詳細了解qiankun的可去官網,或者研究下源碼。
我們拿一個做好的微前端項目,用瀏覽器打開控制臺,查看ajax請求,會發現有個請求子應用的ajax請求,這個請求返回了子頁面內容


image.png

這時候我們點擊能訪問子應用頁面的按鈕或菜單,能看到如下內容

image.png

查看html


簡單來說,qiankun就是拿到子應用的訪問路徑(entry配置),并且給每個子應用都起一個獨一無二的名字(name配置),然后通過ajax請求子應用的數據,再解析到主應用設定好的容器里面(比如#micro-app)。至于具體拿到數據后是如何進行解析,如何劫持路由的,這個就不做深討論,主要是我也不知道。

下面我們來做基于qiankun 的 微前端實踐

項目搭建

按照 qiankun 的說法,接入qiankun時,react,vue等單頁面應用使用history模式最好,所以我們這里主應用和子應用暫時都使用 history模式

  • 創建主應用
    這里的主應用我們用vue-ant-design-pro 創建,應用名 yl-qiankun-base
    git clone --depth=1 https://github.com/vueComponent/ant-design-vue-pro.git yl-qiankun-base
    
  • 創建vue子應用,也是基于vue-ant-design-pro 創建,子應用名 yl-qiankun-child-vue
    git clone --depth=1 https://github.com/vueComponent/ant-design-vue-pro.git yl-qiankun-child-vue
    

主應用改造

下載qiankun

npm i qiankun --save

首先將主應用的id="app"改掉,以免和子應用沖突,將public/index.html 里面的id="app"改為獨有的id。這里用 ylQiankunBase


image.png

第二,我們這里假設業務確定為希望從菜單加載子應用,即點擊菜單才加載子應用。那么這里把項目中的 BasicLayout改掉。ant-design-pro 有些東西封裝得比較死,不符合要求的就去掉。
這里首先說下基本思路:主應用除了加載子應用,還要加載自己本身應用里面的路由,所以BasicLayout是不能滿足需求的。除了改造BasicLayout外,我們還要加個裝載子應用的容器組件ChildAppLayout。
1)改造BasicLayout
A.重寫BasicLayout,理由是我們需要在點擊菜單時做更多的事情,而pro-layout沒有提供菜單點擊api,并且a-menu @select 事件 返回的數據也不符合要求,我們可能希望得到點擊的整個菜單數據,而@select事件只是給了key。
B.考慮到菜單組件以后有可能會重用,這里吧菜單組件抽離除了,命名 Slider

改造后的BasicLayout 如下:

<template>
  <a-layout id="components-layout-demo-fixed-sider">
   <slider/>
    <a-layout :style="{ marginLeft: '200px' }">
      <a-layout-header :style="{ background: '#fff', padding: 0 }" />
      <a-layout-content :style="{ margin: '24px 16px 0', overflow: 'initial' }">
       <router-view/>
      </a-layout-content>
    </a-layout>
  </a-layout>
</template>
<script>
import Slider from "../components/slider/slider"
export default {
  components:{
    Slider
  },
}
</script>

Slider 菜單組件如下:

<template>
  <a-layout-sider :style="{ overflow: 'auto', height: '100vh', position: 'fixed', left: 0 }">
    <div class="logo" >
      yl-qiankun-base
    </div>
    <a-menu theme="dark" mode="inline" :default-selected-keys="['4']">
      <a-sub-menu v-for='(menu,index) in menus' :key='menu.name'>
        <span slot="title"><a-icon :type="menu.meta.icon" /><span>{{ menu.meta.title }}</span></span>
        <a-menu-item v-for='(child,index) in menu.children' :key='child.name' @click.native='select(child)'>
          <a-icon :type="child.meta.icon" />
          <span class="nav-text">{{ child.meta.title }}</span>
        </a-menu-item>
      </a-sub-menu>
    </a-menu>
  </a-layout-sider>
</template>

<script>
import { mapState } from 'vuex'

export default {
  name: 'Slider',
  data() {
    return {
      menus:[],//菜單
    }
  },
  computed: {
    ...mapState({
      // 動態主路由
      mainMenu: state => state.permission.addRouters
    }),
  },
  mounted() {
    const routes = this.mainMenu.find(item => item.path === '/')
    this.menus = (routes && routes.children) || []
  },
  methods: {
    /**
     * 選中菜單
     * @param menuInfo
     */
    select(menuInfo){
      console.log(menuInfo)
      const { name } = menuInfo
      this.$router.push({
        name
      })
    },
  }
}
</script>

<style>
#components-layout-demo-fixed-sider .logo {
  height: 32px;
  background: rgba(255, 255, 255, 0.2);
  margin: 16px;
  font-size: 20px;
  color: #ffffff;
  font-weight: bold;
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>

ChildAppLayout.vue,這里我們將注冊微應用的代碼抽成函數registerChildApp放到util中

<template>
  <div>
    <div id="micro-app"></div>
  </div>
</template>
<script>
import Slider from '@/components/slider/slider'
import {registerChildApp} from '@/utils/util'

export default {
  components:{
    Slider
  },
  created () {
    registerChildApp()
  },
}
</script>

在util.js中寫registerChildApp函數。
這里說下幾個參數:
name: 子應用名稱,這個注意必須和 子應用package.json里面的name一致,生產環境打包后,qiankun將會根據這個name查找子應用
entry:子應用入口地址。實際使用時本地開發可使用localhost,生產環境需要更改為子應用的實際訪問路徑,這里可以使用Node環境變量根據環境賦值。在本示例中,子應用訪問不僅不是根路徑,是yl-qiankun-child-vue下,因此entry要攜帶/yl-qiankun-child-vue
container: 子應用加載容器,全局唯一id值
activeRule: 加載子應用的跟路徑。這里確保和子應用訪問路徑的yl-qiankun-child-vue一致。否則有坑

import { initGlobalState, registerMicroApps, runAfterFirstMounted, start } from 'qiankun'
import Vue from 'vue'
/**
 * 注冊子應用
 */
export const registerChildApp = () => {
  //       // 注冊子應用
  registerMicroApps(
    [
      {
        name: 'ylQiankunChildVue', // 微應用應用名稱,同微應用的package.json中的name一致
        entry: '//localhost:6532/yl-qiankun-child-vue/',
        container: '#micro-app', //父級應用裝載子應用的容器id
        activeRule: '/yl-qiankun-child-vue', //加載子應用的跟路徑
      },
    ],
    {
      beforeLoad: [
        app => {
          console.log('beforeLoad');
        }
      ],
      beforeMount: [
        app => {
          console.log('beforeMount');
        }
      ],
      beforeUnmount: [
        app => {
          console.log('beforeUnmount');
        }
      ],
      afterUnmount: [
        app => {
          console.log('afterUnmount');
        }
      ]
    }
  )
// setDefaultMountApp(firstApp)
// 第一個子應用加載完畢后回調
  runAfterFirstMounted(()=>{
    console.log('第一個子應用加載完畢后的回調')
  })
// 通訊
// 將action對象綁到Vue原型上,為了項目中其他地方使用方便
  Vue.prototype.$qiankunActions = initGlobalState({menuClick:'init',})
  start({prefetch :'all'})
}

子應用改造

子應用不需要安裝qiankun

- 基于vue 的子應用改造

注意這里的子應用基于 vue-ant-design-pro創建,所以帶有些默認的文件。另外子應用訪問路徑放在 yl-qiankun-child-vue 下,這里需要配合配置vue.config.js

基于vue的子應用的改造主要有:
添加public-path.js 文件;改寫router路由導出文件;權限文件permission.js;main.js文件;組件容器BasicLayout.vue文件

首先添加public-path.js。在和main.js同級處新建public-path.js,內容如下

if (window.__POWERED_BY_QIANKUN__) {
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

如果這段代碼引發了你的eslient報錯,請將相關規則關閉,我這邊就將camelcase設為了off

第二,改造main.js
這里說下 main.js改造的關鍵地方:
1)頂部引入 public-path.js
2)將路由注冊移植到main.js中。這里影響可能比較大,這樣的話,以前引入router文件的地方,都得改造,比如permission.js
3)增加qiankun 需要導出的三個生命周期函數 bootstrap,mount,unmount

詳細代碼如下:(這里ant-design-pro創建攜帶的代碼無關的已忽略刪除)

// with polyfills
import './public-path' // 引入公共路徑
import Vue from 'vue'
import App from './App.vue'
import store from './store/'
import i18n from './locales'
import Router from 'vue-router'
import antBootstrap from './core/bootstrap'

import { constantRouterMap } from '@/config/router.config'
import routerEach from '@/permission'

const originalPush = Router.prototype.push
Router.prototype.push = function push (location, onResolve, onReject) {
  if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
  return originalPush.call(this, location).catch(err => err)
}
Vue.use(Router)
Vue.config.productionTip = false

let router = null
let instance = null
function render() {
  router = new Router({
    base: '/yl-qiankun-child-vue/',
    routes:[...constantRouterMap],
    mode: 'history',
  })
  routerEach(router) // 路由監聽要在 new Vue 之前
  instance = new Vue({
    store,
    router,
    i18n,
    created: antBootstrap,
    render:h=>h(App),
  }).$mount('#app')

}

// 生命周期 - 掛載前
export async function bootstrap (props) {
  console.log('one bootstrap')
  console.log(props)
}
// 生命周期 - 掛載后
export async function mount(props) {
  // console.log('one mount');
  props.onGlobalStateChange((state,prev)=>{
    console.log(state)

  })
  // 渲染
  render()
}
// 生命周期 - 解除掛載
export async function unmount(){
  instance.$destroy()
  instance.$el.innerHTML = ""
  instance = null
  router = null
}

// 本地調試
if(!window.__POWERED_BY_QIANKUN__){
  render()
}


關于base: '/yl-qiankun-child-vue/'這行,如果不使用qiankun和使用qiankun,路徑不一致,則需判斷環境,可以寫成base: window.POWERED_BY_QIANKUN ? '/yl-qiankun-child-vue/' : '/' 類似

關于routerEach,由于permission.js 已經無法直接引入router文件獲取router對象,這里改為導出函數,傳遞router方式。詳見permission.js改造

第三,改造permission.js(無關的忽略)


import store from './store'
import storage from 'store'
import NProgress from 'nprogress' // progress bar
import '@/components/NProgress/nprogress.less' // progress bar custom style
import notification from 'ant-design-vue/es/notification'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { i18nRender } from '@/locales'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

const allowList = ['login', 'register', 'registerResult'] // no redirect allowList
const loginRoutePath = '/user/login'
const defaultRoutePath = '/list'

const routerEach = (router) => {
  router.beforeEach((to, from, next) => {
   // 全局路由守衛邏輯代碼,太占位置,省略。。。
  })

  router.afterEach(() => {
    NProgress.done() // finish progress bar
  })
}
export default routerEach

重點就是將router.beforeEach和afterEach部分抽成一個導出的函數routerEach,并且傳入router對象參數

第四,改造vue.config.js
這里改造的重點:
配置輸出

 config.output.library = name
    config.output.libraryTarget = 'umd' // 把微應用打包成 umd 庫格式
    config.output.jsonpFunction = `webpackJsonp_${name}`

允許跨域

  headers: {
      'Access-Control-Allow-Origin':'*'
    },

完整代碼:

const path = require('path')
const webpack = require('webpack')
const {name} = require('./package') //引入包名

module.exports = {
  publicPath:'/yl-qiankun-child-vue/',
  configureWebpack: config => {
    // preview.pro.loacg.com only do not use in your production;
    if (process.env.VUE_APP_PREVIEW === 'true') {
      // add `ThemeColorReplacer` plugin to webpack plugins
      config.plugins.push(createThemeColorReplacerPlugin())
    }
    // 為了讓主應用能正確識別微應用暴露出來的一些信息,微應用的打包工具需要增加如下配置
    config.output.library = name
    config.output.libraryTarget = 'umd' // 把微應用打包成 umd 庫格式
    config.output.jsonpFunction = `webpackJsonp_${name}`
    // if prod, add externals
    config.externals = isProd ? assetsCDN.externals : {}
  },
  devServer: {
    port:6532, // 子應用的端口最好設置特別點,不容易被覆蓋
    // 允許被主應用跨域fetch請求到
    headers: {
      'Access-Control-Allow-Origin':'*'
    },
    open: false, //配置自動啟動瀏覽器
    https: false,
    hotOnly: false,
  },
}

第五,改造BasicLayout.vue
由于本示例中,子應用是點擊菜單加載的,也就是說在微前端模式下運行,子應用是不需要菜單和頂部的,我們根據qiankun環境變量window.POWERED_BY_QIANKUN來做更改。微前端模式下,去掉菜單和頂部,獨自運行時需要菜單和頂部。
改造代碼如下(這里其實改動很小,只需要一個v-if 和 v-else根據環境判斷,為了不浪費篇幅,其他代碼省略)

<template>
  <router-view v-if="isQiankun"/>
  <pro-layout
    :menus="menus"
    :collapsed="collapsed"
    :mediaQuery="query"
    :isMobile="isMobile"
    :handleMediaQuery="handleMediaQuery"
    :handleCollapse="handleCollapse"
    :i18nRender="i18nRender"
    v-bind="settings"
    v-else
  >
  <!--里面代碼省略-->
  </pro-layout>
</template>

<script>
//引用及其他代碼均省略,只展示isQiankun變量

export default {
  name: 'BasicLayout',
  data () {
    return {
      isQiankun:window.__POWERED_BY_QIANKUN__,// 是否時微前端qiankun環境
    }
  },
}
</script>

當然,如此改造比較粗糙,你會發現子應用獨立運行和在微應用下運行時界面表現可能會有些不同。想要保持一致,還是得去掉 pro-layout,自己重構這個容器

一些報錯和坑及注意事項及小知識

下面列舉一些實踐過程中可能出現的報錯和坑

  • 下面是子應用為 vue項目的報錯

1.按照qiankun官網配置 webpack 的 output,報錯 Invalid options in vue.config.js: "output" is not allowed。官網配置示例是這樣的
[圖片上傳失敗...(image-7f071a-1649416618198)]
這是因為比較高版本的webpack已經不允許這種寫法,這個配置要放在configureWebpack中

const {name} = require('./package')
module.exports = {
    configureWebpack: config=>{
        // console.log(config)
        config.output.library=name
        config.output.libraryTarget= 'umd'
        config.output.jsonpFunction=`webpackJsonp_${name}`
    },
}

2.Error: single-spa minified message #20
[圖片上傳失敗...(image-b71d38-1649416618198)]
這個問題多半是主應用 注冊 子應用時,即調用 registerMicroApps 時第一個參數穿得不對,對比官方demo檢查下第一個參數數據

3.跨越 Access to fetch at 'http://localhost:6532/' from origin 'http://localhost:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Uncaught (in promise) TypeError: application 'ylQiankunChildVue' died in status LOADING_SOURCE_CODE: Failed to fetch
ncaught TypeError: application 'ylQiankunChildVue' died in status LOADING_SOURCE_CODE: Failed to fetch

[圖片上傳失敗...(image-8c9ada-1649416618198)]
1)配置子應用的時候,要運行父級應用跨域訪問子應用。否則父級應用拿不到數據無法讀取子應用文件。這個在qiankun快速上手部分沒有說,但是在 qiankun官網 項目實戰部分有配置。要加上devServer 配置 headers配置,設置為 Access-Control-Allow-Origin: '*'

const { name } = require('./package');
module.exports = {
  devServer: {
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },
};

2)檢查子應用是否有public-path文件,main.js里面是否引入pablic-path.js
[圖片上傳失敗...(image-721b16-1649416618198)]
main.js 最頂部,記得要放最頂部

import './public-path' // 引入公共路徑

如果做了兩點配置還有跨越報錯,那就很可能是主應用的entry url 設置不對

4.開發環境下,子應用反向代理無效
因為在微前端模式下,子應用的資源是主應用通過http請求獲取后在主應用解析的,所以開發環境下的反向代理,只有主應用的配置生效。如果子應用的方向代理配置和主應用原本的不一致,需要在主應用配置子應用需要的代理

5.application 'ylQiankunChildVue' died in status NOT_MOUNTED: [qiankun]: Target container with #micro-app not existed after ylQiankunChildVue mounted!
[圖片上傳失敗...(image-4d11f5-1649416618198)]
這個報錯的意思是注冊的時候,容器 #micro-app 不存在,子應用死亡。這個錯誤真的也很常見,造成的原因可能不止一個。反正也很坑。
1)對于主應用是vue的項目,等dom加載完成后再注冊,this.$nextTick里面注冊

  1. 由于vue-cli項目創建的時候,都會有個 id="app",這個是個坑,記得把父級的所有id="app"去掉。否則一直報這個錯誤讓你無解。有些項目并不是直接用vue-cli創建的,而是基于一些做好的布局框架,比如本文的主應用就是基于vue-ant-design-pro 創建點,這個庫創建的項目在App.vue里面還有個id="app",記得去掉,或者改為和main.js里面使用的一致。否則坑的讓你找不著北
    [圖片上傳失敗...(image-c1f435-1649416618198)]
    3)上面兩點都檢查過了,那就看看 容器id ,比如本文的 #micro-app 是否存在重復

    總的一點就是確保各注冊應用id唯一,子應用容器id唯一

更多問題解決參考 qiankun 常見問題,里面有很多踩過的坑,會有不少幫助

6.主應用和子應用使用同一套開發框架,比如都用基于vue 的element-ui,如何保持主題一致?

答案就是如果主應用設定了自己的主題(非element-ui默認主題),那么子應用則不需要設置主題,那么子應用就會使用主應用的主題樣式。當然這個時候如果子應用單獨運行,那主題就是element-ui的默認主題了,想要單獨運行時不使用默認主題,接入微應用時使用主應用的主題,可以根據window.POWERED_BY_QIANKUN判斷環境來加載主題

7.從子項目頁面跳轉到主項目自身的頁面時,主項目頁面的 css 未加載
在全局路由守衛router.beforeEach處做處理,示例:

/**
 * 解決從子項目頁面跳轉到主項目自身的頁面時,主項目頁面的 css 未加載的 bug
 * 產生這個問題的原因是:在子項目跳轉到父項目時,子項目的卸載需要一點點的時間,在這段時間內,父項目加載了,插入了 css,但是被子項目的 css 沙箱記錄了,然后被移除了。父項目的事件監聽也是一樣的,所以需要在子項目卸載完成之后再跳轉。
 *解決方案:先復制 HTMLHeadElement.prototype.appendChild 和 window.addEventListener ,路由鉤子函數 beforeEach 中判斷一下,如果當前路由是子項目,并且去的路由是父項目的,則還原這兩個對象
 */
const rawAppendChild = HTMLHeadElement.prototype.appendChild
const rawAddEventListener = window.addEventListener
router.beforeEach((to, from, next) => {
 
  if(/^\/yl-qiankun-child-vue\//.test(from.path) && !/^\/yl-qiankun-child-vue\//.test(to.path)){
    // 用路徑來判斷是否是從子項目跳轉到主項目
    HTMLHeadElement.prototype.appendChild = rawAppendChild
    window.addEventListener = rawAddEventListener
  }
  next()
  })

你真的需要微前端嗎

最后,還是想談下,我們真的要使用微前端嗎?
個人覺得技術是要選則最合適自身業務的,不是為了使用技術而選技術,也不是為了追隨潮流而使用技術。必須得考慮目前業務,技術團隊情況,以及以后業務和技術團隊可能的發展。拒絕教科書式照搬,還是得實事求是。
參照qiankun技術圓桌:你可能并不需要微前端中的總結:

滿足以下幾點,你可能就不需要微前端

  1. 你/你的團隊 具備系統內所有架構組件的話語權
    簡單來說就是,系統里的所有組件都是由一個小的團隊開發的。
  2. 你/你的團隊 有足夠動力去治理、改造這個系統中的所有組件
    直接改造存量系統的收益大于新老系統混雜帶來的問題。
  3. 系統及組織架構上,各部件之間本身就是強耦合、自洽、不可分離的
    系統本身就是一個最小單元的「架構量子」,拆分的成本高于治理的成本。
  4. 極高的產品體驗要求,對任何產品交互上的不一致零容忍
    不允許交互上不一致的情況出現,這基本上從產品上否決了漸進式升級的技術策略

文章參考:
《對比多種微前端方案》
MicroFrontends

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容