微前端開發(Vue) 《ASP.NET MVC企業級實戰》

一、微前端概述

1.  什麼是微前端?

  爲了解決龐大的一整塊後端服務帶來的變更與擴展方面的限制,出現了微服務架構。然而,越來越重的前端工程也面臨同樣的問題,自然地想到了將微服務思想應用(照搬)到前端,於是有了“微前端(micro-frontends)”的概念。即,一種由獨立交付的多個前端應用組成整體的架構風格。具體的,將前端應用分解成一些更小、更簡單的能夠獨立開發、測試、部署的小塊,而在用戶看來仍然是內聚的單個產品。

  我們常見後臺項目通常長這樣:

 

  如果我們的項目需要開發某個新的功能,而這個功能另一個項目已經開發好,我們想直接複用時。

  說明我們需要的只是別人項目的這個功能頁面的內容部分,不需要別人項目的頂部導航和菜單。

一個比較笨的辦法就是直接把別人項目這個頁面的代碼拷貝過來,但是萬一別人不是 vue 開發的,或者說 vue 版本、UI 庫等不同,以及別人的頁面加載之前操作(路由攔截,鑑權等)我們都需要拷貝過來,更重要的問題是,別人代碼有更新,我們如何做到同步更新。甚至當別的項目採用其它技術棧時,如何集成?顯然複製代碼是行不通的。

  以前端組件的概念作類比,我們可以把每個被拆分出的子應用看作是一個應用級組件,每個應用級組件專門實現某個特定的業務功能(如商品管理、訂單管理等)。這裏實際上談到了微前端拆分的原則:即以業務功能爲基本單元。經過拆分後,整個系統的結構也發生了變化:

 

  如上圖所示,左側是傳統大型單頁應用的前端架構,所有模塊都在一個應用內,由應用本身負責路由管理,是應用分發路由的方式;而右側是基座模式下的系統架構,各個子應用互不相關,單獨運行在不同的服務上,由基座應用根據路由選擇加載哪個應用到頁面內,是路由分發應用的方式。這種方式使得各個模塊的耦合性大大降低,而微前端需要解決的主要問題就是如何拆分和組織這些子應用。

典型的基於vue-routerVue應用與這種架構存在着很大的相似性:

 

2.  巨無霸項目的存在的問題

  • 代碼越來越多,打包越來越慢,部署升級麻煩,一些插件的升級和公共組件的修改需要考慮的更多,很容易牽一髮而動全身
  • 項目太大,參與人員越多,代碼規範比較難管理,代碼衝突也頻繁。
  • 產品功能齊全,但是客戶往往只需要其中的部分功能。剝離不需要的代碼後,需要獨立制定版本,獨立維護,增加人力成本。

  微前端的誕生也是爲了解決以上問題:

  •   複用(嵌入)別人的項目頁面,但是別人的項目運行在他自己的環境之上。
  •   巨無霸應用拆分成一個個的小項目,這些小項目獨立開發部署,又可以自由組合進行售賣。

  使用微前端的好處:

  •     技術棧無關,各個子項目可以自由選擇框架,可以自己制定開發規範。
  •    快速打包,獨立部署,互不影響,升級簡單。
  •    可以很方便的複用已有的功能模塊,避免重複開發。

二、常見微前端方案

  目前主流的微前端方案包括以下幾個:

  •  iframe
  • 基座模式,主要基於路由分發,qiankunsingle-spa就是基於這種模式
  • 組合式集成,即單獨構建組件,按需加載,類似npm包的形式
  • EMP,主要基於Webpack5 Module Federation
  • Web Components

  iframe:是傳統的微前端解決方案,基於iframe標籤實現,技術難度低,隔離性和兼容性很好,但是性能和使用體驗比較差,多用於集成第三方系統;

  基座模式:主要基於路由分發,即由一個基座應用來監聽路由,並按照路由規則來加載不同的應用,以實現應用間解耦;

  組合式集成:把組件單獨打包和發佈,然後在構建或運行時組合。

  EMP:基於Webpack5 Module Federation,一種去中心化的微前端實現方案,它不僅能很好地隔離應用,還可以輕鬆實現應用間的資源共享和通信

  Web Components:是官方提出的組件化方案,它通過對組件進行更高程度的封裝,來實現微前端,但是目前兼容性不夠好,尚未普及。

  總的來說,iframe主要用於簡單並且性能要求不高的第三方系統;組合式集成目前主要用於前端組件化,而不是微前端;基座模式、EMPWeb Components是目前主流的微前端方案。

  目前微前端最常用的有兩種解決方案:iframe 方案和 基座模式方案

1.  iframe方案

  iframe 大家都很熟悉,使用簡單方便,提供天然的 js/css 隔離,也帶來了數據傳輸的不便,一些數據無法共享(主要是本地存儲、全局變量和公共插件),兩個項目不同源(跨域)情況下數據傳輸需要依賴 postMessage

  iframe 有很多坑,但是大多都有解決的辦法:

  1. 頁面加載問題

  iframe 和主頁面共享連接池,而瀏覽器對相同域的連接有限制,所以會影響頁面的並行加載,阻塞 onload 事件。每次點擊都需要重新加載,雖然可以採用 display:none 來做緩存,但是頁面緩存過多會導致電腦卡頓。(無法解決)

  2. 佈局問題

  iframe 必須給一個指定的高度,否則會塌陷。

  解決辦法:子項目實時計算高度並通過 postMessage 發送給主頁面,主頁面動態設置 iframe 高度。有些情況會出現多個滾動條,用戶體驗不佳。

  3. 彈窗及遮罩層問題

  彈窗只能在 iframe 範圍內垂直水平居中,沒法在整個頁面垂直水平居中。

  解決辦法1:通過與框架頁面消息同步解決,將彈窗消息發送給主頁面,主頁面來彈窗,對原項目改動大且影響原項目的使用。

  解決辦法2:修改彈窗的樣式:隱藏遮罩層,修改彈窗的位置。

  4. iframe 內的 div 無法全屏

  彈窗的全屏,指的是在瀏覽器可視區全屏。這個全屏指的是佔滿用戶屏幕。

  全屏方案,原生方法使用的是 Element.requestFullscreen(),插件:vue-fullscreen。當頁面在 iframe 裏面時,全屏會報錯,且 dom 結構錯亂。

  解決方案:iframe 標籤設置 allow="fullscreen" 屬性即可

  5. 瀏覽器前進/後退問題

  iframe 和主頁面共用一個瀏覽歷史,iframe 會影響頁面的前進後退。大部分時候正常,iframe 多次重定向則會導致瀏覽器的前進後退功能無法正常使用。並且 iframe 頁面刷新會重置(比如說從列表頁跳轉到詳情頁,然後刷新,會返回到列表頁),因爲瀏覽器的地址欄沒有變化,iframe src 也沒有變化。

  6. iframe 加載失敗的情況不好處理

  非同源的 iframe 在火狐及 chorme 都不支持 onerror 事件。

  解決辦法1onload 事件裏面判斷頁面的標題,是否 404 或者 500

  解決辦法2:使用 try catch 解決此問題,嘗試獲取 contentDocument 時將拋出異常。

2.  基座模式方案

  基座模式方案以single-spaqiankun爲代表,這裏我選擇qiankun

  qiankun 是螞蟻金服開源的一款框架,它是基於 single-spa 的。他在 single-spa 的基礎上,實現了開箱即用,除一些必要的修改外,子項目只需要做很少的改動,就能很容易的接入。

  qiankun框架官網:https://qiankun.umijs.org/zh/

  微前端中子項目的入口文件常見的有兩種方式:JS entry HTML entry。 single-spa 採用的是 JS entry,而 qiankun 既支持 JS entry,又支持 HTML entry

  JS entry 的要求比較苛刻:

  (1)將 css 打包到 js 裏面

  (2)去掉 chunk-vendors.js

  (3)去掉文件名的 hash

  (4)將 single-spa 模式的入口文件( app.js )放置到 index.html 目錄,其他文件不變,原因是要截取 app.js 的路徑作爲 publicPath

  建議使用 HTML entry ,使用起來和 iframe 一樣簡單,但是用戶體驗比 iframe 強很多。qiankun 請求到子項目的 index.html 之後,會先用正則匹配到其中的 js/css 相關標籤,然後替換掉,它需要自己加載 js 並運行,然後去掉 html/head/body 等標籤,剩下的內容原樣插入到子項目的容器中

二、微前端方案實踐

  以“大數據分析”項目爲例,將客戶特有的需求,如“電子路單”、“數據填報”單獨提取爲可獨立運行的子項目。

  大數據分析項目改造爲主應用基座,代碼倉庫地址:http://192.168.1.102/zouqiongjun/big-data-web.git

  客戶自定義需求單獨作爲子應用項目,代碼倉庫地址:http://192.168.1.102/zouqiongjun/zibo-custom-web.git

1.  各應用工程代碼結構

  sass-base-web:主倉庫,主要存放一些批量操作的腳本,用於聚合管理倉庫和一鍵編譯、一鍵部署。

  倉庫代碼結構如下圖所示:

 

  big-data-web:大數據分析主應用

  zibo-custom-web:客戶自定義需求,微應用倉庫

  子應用可以獨立運行,但是當前子應用是直接嵌套在主應用的main內容區域,所以暫時並沒有單獨提供左側菜單導航,後續如有需要可以擴展和補充此功能。

2.  主應用big-data-web改造

  將普通的項目改造成 qiankun 主應用基座,需要進行三步操作:

  (1) 創建微應用容器 - 用於承載微應用,渲染顯示微應用;

  (2) 註冊微應用 - 設置微應用激活條件,微應用地址等等;

  (3) 啓動 qiankun

  注意:由於big-data-web主應用的路由採用的是hash模式,所以子應用的路由也應該採用hash模式。

1.1 安裝 qiankun

$ yarn add qiankun # 或者 npm i qiankun -S

1.2. 在主應用中註冊微應用

  爲了使用keepAlive緩存,這裏我們採用手動加載微應用的方式

  當微應用信息註冊完之後,一旦瀏覽器的 url 發生變化,便會自動觸發 qiankun 的匹配邏輯,所有 activeRule 規則匹配上的微應用就會被插入到指定的 container 中,同時依次調用微應用暴露出的生命週期鉤子。

  在views目錄下,新建AppVueHash.vue,作爲子應用的容器,代碼如下:

<template>
<div class="zibo-custom-web">
     <div id="zibo-custom-web" class="app-view-box"></div>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss" scoped>
.zibo-custom-web{
  position: relative;
}
</style>

  這個id屬性要唯一,最終子應用的內容將會掛載在這裏。

  ContainerOther.vue代碼改造:

  <!-- 主體視圖層 -->
        <div class="avue-view-contain" v-show="!isSearch">
          <keep-alive>
            <router-view
              class="avue-view keep-alive"
              v-if="$route.meta.keepAlive && isActiveRoute"
              v-show="!showAppVueHash"
            />
          </keep-alive>
          <router-view
            class="avue-view"
            v-if="!$route.meta.keepAlive && isActiveRoute"
            v-show="!showAppVueHash"
          />
          <AppVueHash v-show="showAppVueHash" />
        </div>

js代碼:

import router from "@/router/router";
import store from "@/store";
import AppVueHash from "@/views/AppVueHash.vue";
import { loadMicroApp } from "qiankun";
//子項目路由前綴
const isChildRoute = path => website.childRoute.some(item => path.startsWith(item));
const apps = [
  {
    name: "/zibo-custom-web",
    entry: window.configs.VUE_APP_ZIBO_CUSTOM_URL,
    container: "#zibo-custom-web",
    props: { data: { store, router } },
    sandbox: {
      strictStyleIsolation: true // 開啓樣式隔離
    }
  }
];
//控制微應用手動加載
    ctrlMicroApp(path){
       if (isChildRoute(path)) {
        this.showAppVueHash = true;
        this.$nextTick(() => {
          //手動加載
          if(!this.mounted){
            this.loadApps = apps.map(item => loadMicroApp(item))
            this.mounted=true;
          }  
        });
      } else {
        this.showAppVueHash = false;
      }

  這裏的container屬性值,必須和AppVueHash.vue組件中的id值保持一致。

  根據url地址判斷是否是子應用,如果是子應用,則手動加載,否則隱藏子應用容器,只加載主應用的router-view

  在ContainerOther第一次加載或路由變化時手動加載微應用:

mounted() {
    this.init();
    setTheme(this.themeName);
    this.ctrlMicroApp(this.$route.path)
  },
  watch: {
    $route(val) {
      this.ctrlMicroApp(val.path);
},
    tagList(newVal,oldVal){
      let starts='';
      const childRoute = website.childRoute;
      childRoute.forEach((n,i)=>{
        if(i<childRoute.length-1){
          starts+=`^${n}|`;
        }else{
          starts+=`^${n}`;
        }
      })
      const patt = new RegExp(`${starts}`);
      //之前存在子應用頁籤
      const before = oldVal.some(item=>{
        return patt.test(item.value);
      });
      //現在存在子應用頁籤
      const now = newVal.some(item=>{
        return patt.test(item.value);
      });
      if(before && !now){
        this.mounted=false;
        this.loadApps.forEach(app=>{
          app.unmount();  
        })
      }
    } 

  監聽tab頁籤變化,關閉頁籤時,需要卸載子應用。

3.  qiankun 子項目zibo-custom-web

  1. src 目錄新增文件 public-path.js
if (window.__POWERED_BY_QIANKUN__) {
    // eslint-disable-next-line no-undef
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
  }
  1. 修改 index.html 中項目初始化的容器,不要使用 #app ,避免與其他的項目衝突,建議小駝峯寫法
    <div id="appVueHash"></div>
  1. 修改入口文件 main.js
// -----------子應用微前端start-------------------
let router = null;
let instance = null;

function render({ data = {} , container } = {}) {
  router = new VueRouter({
    routes,
  });
  instance = new Vue({
    router,
    store,
    data(){
      return {
        parentRouter: data.router,
        parentVuex: data.store,
      }
    },
    render: h => h(App),
  }).$mount(container ? container.querySelector('#appVueHash') : '#appVueHash');
}
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}
//測試全局變量污染
console.log('window.a',window.a)
export async function bootstrap() {
  console.log('vue app bootstraped');
}
export async function mount(props) {
  console.log('props from main framework', props.data);
  render(props);
}
export async function unmount() {
  instance.$destroy();
  instance.$el.innerHTML = "";
  instance = null;
  router = null;
}
// -----------子應用微前端end-------------------

  主要改動是:引入修改 publicPath 的文件和 export 三個生命週期。

  注意:

  • webpack publicPath 值只能在入口文件修改,之所以單獨寫到一個文件並在入口文件最開始引入,是因爲這樣做可以讓下面所有的代碼都能使用這個。
  • 路由文件需要 export 路由數據,而不是實例化的路由對象,路由的鉤子函數也需要移到入口文件。
  • mount 生命週期,可以拿到父項目傳遞過來的數據,router 用於跳轉到主項目/其他子項目的路由,store 是父項目的實例化的 Vuex(也可以傳遞其他數據過來)。
  1. 修改打包配置 vue.config.js:
const { name } = require("./package");
module.exports = {
  outputDir: "../sass-base-web/cicd-config/test/zibo-custom-web",
  devServer: {
    port: 9010,
    // 關閉主機檢查,使微應用可以被 fetch
    disableHostCheck: true,
    // 配置跨域請求頭,解決開發環境的跨域問題
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
    proxy: {
      "/api": {
        //本地服務接口地址
        target: "http://192.168.10.112:10067", //cas
        ws: true,
        pathRewrite: {
          "^/api": "/",
        },
      },
    },
  },
   // 以下配置可以修復一些字體文件加載路徑問題
  chainWebpack: (config) => {
    //忽略的打包文件
    config.externals({
      vue: "Vue",
      "vue-router": "VueRouter",
      vuex: "Vuex",
      axios: "axios",
      "element-ui": "ELEMENT",
    });
    config
      .plugin('html')
      .tap(args => {
        args[0].name = name;
        return args
      });
    config.module
      .rule("fonts")
      .test(/.(ttf|otf|eot|woff|woff2)$/)
      .use("url-loader")
      .loader("url-loader")
      .tap((options) => ({ name: "/fonts/[name].[hash:8].[ext]" }))
      .end();
  },
  // 自定義webpack配置
  configureWebpack: {
    output: {
      library: `${name}-[name]`, // 微應用的包名,這裏與主應用中註冊的微應用名稱一致
      libraryTarget: "umd", // 把子應用打包成 umd 庫格式
      jsonpFunction: `webpackJsonp_${name}`, //webpack打包之後保存在window中的key,各個子應用不一致
    },
  },
};

  這裏主要就兩個配置,一個是允許跨域,另一個是打包成 umd 格式。爲什麼要打包成 umd 格式呢?是爲了讓 qiankun 拿到其 export 的生命週期函數。

  注意 這個 name 默認從 package.json 獲取,可以自定義,只要和父項目註冊時的 name 保持一致即可。

  vue.config.js中

  outputDir: "../sass-base-web/cicd-config/test/zibo-custom-web",

  這裏我子應用項目編譯後會將打包文件打包到sass-base-web項目中的zibo-custom-web下。

  1. 路由動態加載

  需要給子項目所有的路由都添加一個前綴,子項目的路由跳轉如果之前使用的是 path 也需要修改,用 name 跳轉則不用

  avue-router.js:

 const oRouter = {
          path: "/zibo-custom-web",
          name: "RouterView",
          component(resolve) {
            require(["@/components/RouterView.vue"], resolve);
          },

          children: [
            {
              path: path,
              component(resolve) {
                require([`../${component}.vue`], resolve);
              },
              name: name,
              meta: meta,
            },
          ],
        };
        aRouter.push(oRouter);

4.  狀態管理,主應用和微應用之間的通信

  qiankun 通過 initGlobalState: 定義全局狀態,並返回通信方法,建議在主應用使用,微應用通過 props 獲取通信方法;

  onGlobalStateChange: 在當前應用監聽全局狀態,有變更觸發 callback;

  setGlobalState: 按一級屬性設置全局狀態,微應用中只能修改已存在的一級屬性; 換句話說只能修改主用於預先定義的屬性,後面添加的屬性無效

  官方發佈-訂閱的設計模式:

  主應用:

import { initGlobalState, MicroAppStateActions } from 'qiankun';
// 初始化 state
const actions: MicroAppStateActions = initGlobalState(state);

actions.onGlobalStateChange((state, prev) => {
  // state: 變更後的狀態; prev 變更前的狀態
  console.log(state, prev);
});
actions.setGlobalState(state);
actions.offGlobalStateChange();

  子應用:

// 從生命週期 mount 中獲取通信方法,使用方式和 master 一致
export function mount(props) {
    props.onGlobalStateChange((state, prev) => {
      // state: 變更後的狀態; prev 變更前的狀態
      console.log(state, prev);
    });
    props.setGlobalState(state);
  }

  如果主應用和子應用都是vue技術棧,可以在子應用中把數據傳遞給子應用,例如store

   props: { data: { store, router } },

5.  各應用之間的獨立倉庫以及聚合管理

  實際開發中項目存儲在公司倉庫中,以 gitLab 爲例, 當子應用一多,全部放在一個倉庫下面, 這時候就顯得很臃腫了,也很龐大,大大的增加了維護成本,和開發效率;

我們可以通過 sh 腳本, 初始只需要克隆主倉庫代碼, 然後通過 sh 腳本去一鍵拉取所有子應用代碼。

  這裏我將單獨創建一個用來編譯和打包的主倉庫項目sass-base-web,倉庫地址:http://192.168.1.102/zouqiongjun/sass-base-web.git

  項目根目錄下,新建 script/clone-all.sh 文件內容如下

# 子服務 gitLab 地址
SUB_SERVICE_GIT=('http://192.168.1.102/zouqiongjun/big-data-web.git' 'http://192.168.1.102/zouqiongjun/zibo-custom-web.git')
SUB_SERVICE_NAME=('big-data-web' 'zibo-custom-web')

# 子服務
if [ ! -d "sub-service" ]; then
  echo '創建sub-service目錄...'
  mkdir sub-service
fi
echo '進入sub-service目錄...'
cd sub-service

# 遍歷克隆微服務
for i in ${!SUB_SERVICE_NAME[@]}
do
  if [ ! -d ${SUB_SERVICE_NAME[$i]} ]; then
    echo '克隆微服務項目'${SUB_SERVICE_NAME[$i]}
    git clone ${SUB_SERVICE_GIT[$i]}
  fi
done
 
echo '腳本結束...'
# 克隆完成

  當我們啓動主項目的時候,如果想要使用所有子應用的功能,子應用,需要一個一個啓動,這樣無論是開發還是編譯都十分不便。

  考慮到國內使用npm裝包可能會由於網絡的原因安裝失敗,可以讓npm使用國內淘寶鏡像。

  執行命令:npm config set registry https://registry.npm.taobao.org。

  在這個主倉庫項目裏面,我們只需要安裝一個npm-run-all插件。

  運行:yarn add npm-run-all -D或者npm i -D

  該項目下只有一個package.json文件,用於配置編譯和打包命令,代碼如下:

{
  "name": "sass-big-data-web",
  "version": "1.0.0",
  "description": "`qiankun`來實現`vue`技術棧的前端微服務",
  "main": "index.js",
  "scripts": {
    "clone:all": "bash ./scripts/clone-all.sh",
    "install:zibo": "cd ./sub-service/zibo-custom-web && npm install",
    "install:main": "cd ./sub-service/big-data-web && npm install",
    "install-all": "npm-run-all install:*",
    "start:zibo": "cd ./sub-service/zibo-custom-web && npm run serve ",",
    "start:main": "cd ./sub-service/big-data-web && npm run serve",
    "start-all": "npm-run-all --parallel start:*",
    "serve-all": "npm-run-all --parallel start:*",
    "build:zibo": "cd ./sub-service/zibo-custom-web && npm run build",
    "build:main": "cd ./sub-service/big-data-web && npm run build",
    "build-all": "npm-run-all --parallel build:*"
  },
  "repository": {
    "type": "git",
    "url": "http://192.168.1.102/zouqiongjun/sass-big-data-web.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "npm-run-all": "^4.1.5"
  }
}

  要執行yarn clone:all,需要用到bash,跳轉到sass-base-web目錄,右鍵鼠標打開Git bash窗口,如下圖所示:

 

  這樣當我們把各個代碼倉庫的代碼都拉取到sub-service這個目錄下面來,如下圖所示:

 

  代碼拉取完成後, 緊接着就是下載各個項目的依賴及運行。

  運行npm run serve-all則可以自動執行package.json中配置的命令,這個命令最終會執行以下三個執行命令:

    "start:zibo": "cd ../zibo-custom-web && npm run serve ",
    "start:main": "cd ../big-data-web && npm run serve",

  這樣就不需要我們自己一個一個單獨的去運行各個項目了。

  總體運行步驟: 第一步 clone 主應用, 然後依次執行 yarn clone:all --> yarn install-all --> yarn start-all 即可運行整個項目

  build-all:可以編譯整個項目。

  sub-service目錄,將其添加到.gitignore當中,因爲在sass-base-web項目當中,我們只需要配置和編譯及打包用,並不需要真正的將所有子應用的代碼都提交到sass-base-web項目中,各子應用都有自己私有的倉庫。

6.  子項目開發的一些注意事項

  (1所有的資源(圖片/音視頻等)都應該放到 src 目錄,不要放在 public 或者static資源放 src 目錄,會經過 webpack 處理,能統一注入 publicPath。否則在主項目中會404

  (2避免 css 污染。組件內樣式的 css-scoped 是必須的。

  (3 body document 等綁定的事件,請在 unmount 週期清除

  (4謹慎使用 positionfixed

  在父項目中,這個定位未必準確,應儘量避免使用,確有相對於瀏覽器窗口定位需求,可以用 position: sticky,但是會有兼容性問題(IE不支持)。

  常見問題見官網:https://qiankun.umijs.org/zh/faq

7.  部署

1.  常規部署

  主應用和微應用都是獨立開發和部署,即它們都屬於不同的倉庫和服務

  場景:主應用和微應用部署到同一個服務器(同一個IP和端口)

  如果服務器數量有限,或不能跨域等原因需要把主應用和微應用部署到一起。通常的做法是主應用部署在一級目錄,微應用部署在二/三級目錄。

  若微應用想部署在非根目錄,在微應用打包之前需要做兩件事:

  • 必須配置 webpack 構建時的 publicPath 爲目錄名稱,更多信息請看 webpack 官方說明 和 vue-cli3 的官方說明
  • history 路由的微應用需要設置 base ,值爲目錄名稱,用於獨立訪問時使用。

  部署之後注意三點:

  • activeRule 不能和微應用的真實訪問路徑一樣,否則在主應用頁面刷新會直接變成微應用頁面。
  • 微應用的真實訪問路徑就是微應用的 entryentry 可以爲相對路徑。
  • 微應用的 entry 路徑最後面的 / 不可省略,否則 publicPath 會設置錯誤,例如子項的訪問路徑是 http://localhost:8080/app1,那麼 entry 就是 http://localhost:8080/app1/

  通過配置 nginx 端口到目錄的轉發。須要對外開放子利用對應的端口,將編譯好的利用文件放到對應的配置目錄。

  跳轉到sass-base-web目錄,執行npm run build-all,會自動執行所有build:開頭的命令:

    "build:zibo": "cd ../zibo-custom-web && npm run build",
    "build:control": "cd ../control-center && npm run build",
    "build:main": "cd ../big-data-web && npm run build",
    "build-all": "npm-run-all --parallel build:*"

  zibo-custom-web目錄結構如下圖所示:

 

  這裏是子應用和主應用部署在同一臺服務器上,且IP和端口相同,nginx不需要額外設置。

  如果子應用和主應用部署在同一臺服務器上, 但是端口不同,需要修改vue.config.js中的outputDir,這個是打包編譯後代碼存放的路徑,這個不做配置,默認會將代碼編譯打包到當前根目錄下,並生成一個dist目錄用於存放編譯後的代碼,如下圖所示:

 

修改nginx.conf配置:

 #gzip  on;
      upstream gateway { server 192.168.31.136:32067;}
     # 主應用
        server {
        listen   32043;
        server_name  web;
        root  /dist;
        # 關閉端口重定向
        # port_in_redirect off;
        #charset koi8-r;
        access_log /var/log/nginx/nginx.log;

        location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header REMOTE-HOST $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location ^~/api/ {  
            proxy_read_timeout 600s;
            proxy_set_header Host $host; 
            proxy_set_header X-Real-IP $remote_addr; 
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_buffering off; 
            rewrite ^/api/(.*)$ /$1 break; 
            proxy_pass http://gateway; 
            }
        }

   # 子應用
    server {
        listen   9010;
        server_name  cus_web;
        # 子應用編譯後的代碼路徑
        root  /zibo-custom-web;  
        # 允許跨域
        add_header Access-Control-Allow-Origin *;
        # 關閉端口重定向
        # port_in_redirect off;
        # charset koi8-r;
        access_log /var/log/nginx/nginx.log;
        location ^~/zibo-custom-web/ {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header REMOTE-HOST $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location ^~/api/ {  
            proxy_read_timeout 600s;
            proxy_set_header Host $host; 
            proxy_set_header X-Real-IP $remote_addr; 
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_buffering off; 
            rewrite ^/api/(.*)$ /$1 break; 
            proxy_pass http://gateway; 
            }
    }

2.  docker nginx 配置

  此處 nginx 主要作用是用於端口目錄轉發,並配置主應用訪問子應用的跨域問題。

  使用 docker 配置部署 nginx:

# docker-compose.yml
version: '3.1'
services:
  nginx:
    restart: always
    image: nginx
    container_name: nginx
    ports:
      - 8888:80
      - 8889:8889
      - 7100:7100
      - 7101:7101
    volumes:
      - /app/volumes/nginx/nginx.conf:/etc/nginx/nginx.conf
      - /app/volumes/nginx/html:/usr/share/nginx/html
      - /app/micro/portal:/app/micro/portal
      - /app/micro/app1:/app/micro/app1
      - /app/micro/app2:/app/micro/app2

  將編譯後的主應用以及子應用放到對應的數據卷掛載目錄即可,如主應用 /app/micro/portal。
  同理,也需要將配置好的 nginx.conf 文件放到指定的數據卷掛載目錄,使用 docker-compose up -d 啓動即可。

  nginx 端口目錄轉發配置:

# nginx.conf
user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;

    #gzip  on;
    include /etc/nginx/conf.d/*.conf;
    server {
      listen    8889;
      server_name 192.168.2.192;
      location / {
        root /app/micro/portal;
        index index.html;
        try_files $uri $uri/ /index.html;
      }
    }
    server {
      listen    7100;
      server_name 192.168.2.192;

      # 配置跨域訪問,此處是通配符,嚴格生產環境的話可以指定爲主應用 192.168.2.192:8889
      add_header Access-Control-Allow-Origin *;
      add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
      add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
      location / {
        root /app/micro/app1;
        index index.html;    
        try_files $uri $uri/ /index.html;
      }
    }
    server {
      listen    7101;
      server_name 192.168.2.192;
     
      # 配置跨域訪問,此處是通配符,嚴格生產環境的話可以指定爲主應用 192.168.2.192:8889
      add_header Access-Control-Allow-Origin *;
      add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
      add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
      
      location / {
        root /app/micro/app2;
        index index.html;   
        try_files $uri $uri/ /index.html;
      }
    }}

  部署到生產,需要修改big-data-web/public/util/config.js中的VUE_APP_ZIBO_CUSTOM_URL配置項:

(function() {
  window.configs = {
    VUE_APP_CASLOGINURL: "http://192.168.1.99:32080/cas", //cas登錄地址
    VUE_APP_REDIRECTURL: "http://192.168.51.61:8888/big-data/", //前端部署地址
    VUE_APP_SOCKET: "ws://192.168.31.136:32061", //websocket地址'
    VUE_APP_AMAPURLPREFIX: "https://webapi.amap.com", //高德地圖地址
    VUE_APP_ZIBO_CUSTOM_URL:"http://localhost:9010",//自定義微應用地址
    //================================================

    VUE_APP_AMAPKEY: "xxxxxx" //高德key
  };
})();
 

  這裏config.js是爲了配置文件外置,不需要編譯。

8.  總結

  儘管qiankun框架支持各個子應用使用不同的技術框架,但是都需要子應用做相應的改造,而且在其它技術棧中總是會時不時的出現各種難以預料的錯誤,一旦出現問題,都需要我們去改造代碼。所以如果都是vue技術棧,建議使用qiankun做微前端。

  如果是第三方公司的項目接入進來,由於他們的代碼不受我們控制,需要酌情考慮是否用iframe

  參考文獻:qiankun 微前端方案實踐及總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章