欧美人与禽2O2O性论交,秋霞免费视频,国产美女视频免费观看网址,国产成人亚洲综合网色欲网

微前端開發(fā)(Vue)(微前端開發(fā)導(dǎo)致彈出層遮罩層跑到最上方)

前端開發(fā)(Vue)

一、微前端概述

1. 什么是微前端?

  為了解決龐大的一整塊后端服務(wù)帶來(lái)的變更與擴(kuò)展方面的限制,出現(xiàn)了微服務(wù)架構(gòu)。然而,越來(lái)越重的前端工程也面臨同樣的問(wèn)題,自然地想到了將微服務(wù)思想應(yīng)用(照搬)到前端,于是有了“微前端(micro-frontends)”的概念。即,一種由獨(dú)立交付的多個(gè)前端應(yīng)用組成整體的架構(gòu)風(fēng)格。具體的,將前端應(yīng)用分解成一些更小、更簡(jiǎn)單的能夠獨(dú)立開發(fā)、測(cè)試、部署的小塊,而在用戶看來(lái)仍然是內(nèi)聚的單個(gè)產(chǎn)品。

  我們常見后臺(tái)項(xiàng)目通常長(zhǎng)這樣:

微前端開發(fā)(Vue)(微前端開發(fā)導(dǎo)致彈出層遮罩層跑到最上方)

1

  如果我們的項(xiàng)目需要開發(fā)某個(gè)新的功能,而這個(gè)功能另一個(gè)項(xiàng)目已經(jīng)開發(fā)好,我們想直接復(fù)用時(shí)。

  說(shuō)明:我們需要的只是別人項(xiàng)目的這個(gè)功能頁(yè)面的內(nèi)容部分,不需要?jiǎng)e人項(xiàng)目的頂部導(dǎo)航和菜單。

一個(gè)比較笨的辦法就是直接把別人項(xiàng)目這個(gè)頁(yè)面的代碼拷貝過(guò)來(lái),但是萬(wàn)一別人不是 vue 開發(fā)的,或者說(shuō)vue 版本、UI 庫(kù)等不同,以及別人的頁(yè)面加載之前操作(路由攔截,鑒權(quán)等)我們都需要拷貝過(guò)來(lái),更重要的問(wèn)題是,別人代碼有更新,我們?nèi)绾巫龅酵礁隆I踔廉?dāng)別的項(xiàng)目采用其它技術(shù)棧時(shí),如何集成?顯然復(fù)制代碼是行不通的。

  以前端組件的概念作類比,我們可以把每個(gè)被拆分出的子應(yīng)用看作是一個(gè)應(yīng)用級(jí)組件,每個(gè)應(yīng)用級(jí)組件專門實(shí)現(xiàn)某個(gè)特定的業(yè)務(wù)功能(如商品管理、訂單管理等)。這里實(shí)際上談到了微前端拆分的原則:即以業(yè)務(wù)功能為基本單元。經(jīng)過(guò)拆分后,整個(gè)系統(tǒng)的結(jié)構(gòu)也發(fā)生了變化:

微前端開發(fā)(Vue)(微前端開發(fā)導(dǎo)致彈出層遮罩層跑到最上方)

  如上圖所示,左側(cè)是傳統(tǒng)大型單頁(yè)應(yīng)用的前端架構(gòu),所有模塊都在一個(gè)應(yīng)用內(nèi),由應(yīng)用本身負(fù)責(zé)路由管理,是應(yīng)用分發(fā)路由的方式;而右側(cè)是基座模式下的系統(tǒng)架構(gòu),各個(gè)子應(yīng)用互不相關(guān),單獨(dú)運(yùn)行在不同的服務(wù)上,由基座應(yīng)用根據(jù)路由選擇加載哪個(gè)應(yīng)用到頁(yè)面內(nèi),是路由分發(fā)應(yīng)用的方式。這種方式使得各個(gè)模塊的耦合性大大降低,而微前端需要解決的主要問(wèn)題就是如何拆分和組織這些子應(yīng)用。

典型的基于vue-Router的Vue應(yīng)用與這種架構(gòu)存在著很大的相似性:

微前端開發(fā)(Vue)(微前端開發(fā)導(dǎo)致彈出層遮罩層跑到最上方)

2. 巨無(wú)霸項(xiàng)目的存在的問(wèn)題

  • 代碼越來(lái)越多,打包越來(lái)越慢,部署升級(jí)麻煩,一些插件的升級(jí)和公共組件的修改需要考慮的更多,很容易牽一發(fā)而動(dòng)全身。
  • 項(xiàng)目太大,參與人員越多,代碼規(guī)范比較難管理,代碼沖突也頻繁。
  • 產(chǎn)品功能齊全,但是客戶往往只需要其中的部分功能。剝離不需要的代碼后,需要獨(dú)立制定版本,獨(dú)立維護(hù),增加人力成本。

  微前端的誕生也是為了解決以上問(wèn)題:

  •   復(fù)用(嵌入)別人的項(xiàng)目頁(yè)面,但是別人的項(xiàng)目運(yùn)行在他自己的環(huán)境之上。
  •   巨無(wú)霸應(yīng)用拆分成一個(gè)個(gè)的小項(xiàng)目,這些小項(xiàng)目獨(dú)立開發(fā)部署,又可以自由組合進(jìn)行售賣。

  使用微前端的好處:

  •   技術(shù)棧無(wú)關(guān),各個(gè)子項(xiàng)目可以自由選擇框架,可以自己制定開發(fā)規(guī)范。
  •   快速打包,獨(dú)立部署,互不影響,升級(jí)簡(jiǎn)單。
  •   可以很方便的復(fù)用已有的功能模塊,避免重復(fù)開發(fā)。

二、常見微前端方案

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

  • iframe
  • 基座模式,主要基于路由分發(fā),qiankun和single-spa就是基于這種模式
  • 組合式集成,即單獨(dú)構(gòu)建組件,按需加載,類似npm包的形式
  • EMP,主要基于Webpack5 Module Federation
  • Web Components

  iframe:是傳統(tǒng)的微前端解決方案,基于iframe標(biāo)簽實(shí)現(xiàn),技術(shù)難度低,隔離性和兼容性很好,但是性能和使用體驗(yàn)比較差,多用于集成第三方系統(tǒng);

  基座模式:主要基于路由分發(fā),即由一個(gè)基座應(yīng)用來(lái)監(jiān)聽路由,并按照路由規(guī)則來(lái)加載不同的應(yīng)用,以實(shí)現(xiàn)應(yīng)用間解耦;

  組合式集成:把組件單獨(dú)打包和發(fā)布,然后在構(gòu)建或運(yùn)行時(shí)組合。

  EMP:基于Webpack5 Module Federation,一種去中心化的微前端實(shí)現(xiàn)方案,它不僅能很好地隔離應(yīng)用,還可以輕松實(shí)現(xiàn)應(yīng)用間的資源共享和通信。

  Web Components:是官方提出的組件化方案,它通過(guò)對(duì)組件進(jìn)行更高程度的封裝,來(lái)實(shí)現(xiàn)微前端,但是目前兼容性不夠好,尚未普及。

  總的來(lái)說(shuō),iframe主要用于簡(jiǎn)單并且性能要求不高的第三方系統(tǒng);組合式集成目前主要用于前端組件化,而不是微前端;基座模式、EMP和Web Components是目前主流的微前端方案。

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

1. iframe方案

  iframe 大家都很熟悉,使用簡(jiǎn)單方便,提供天然的 js/css 隔離,也帶來(lái)了數(shù)據(jù)傳輸?shù)牟槐?,一些?shù)據(jù)無(wú)法共享(主要是本地存儲(chǔ)、全局變量和公共插件),兩個(gè)項(xiàng)目不同源(跨域)情況下數(shù)據(jù)傳輸需要依賴postMessage 。

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

  1. 頁(yè)面加載問(wèn)題

  iframe 和主頁(yè)面共享連接池,而瀏覽器對(duì)相同域的連接有限制,所以會(huì)影響頁(yè)面的并行加載,阻塞onload 事件。每次點(diǎn)擊都需要重新加載,雖然可以采用 display:none 來(lái)做緩存,但是頁(yè)面緩存過(guò)多會(huì)導(dǎo)致電腦卡頓。(無(wú)法解決)

  2. 布局問(wèn)題

  iframe 必須給一個(gè)指定的高度,否則會(huì)塌陷。

  解決辦法:子項(xiàng)目實(shí)時(shí)計(jì)算高度并通過(guò)postMessage 發(fā)送給主頁(yè)面,主頁(yè)面動(dòng)態(tài)設(shè)置 iframe高度。有些情況會(huì)出現(xiàn)多個(gè)滾動(dòng)條,用戶體驗(yàn)不佳。

  3. 彈窗及遮罩層的問(wèn)題

  彈窗只能在 iframe 范圍內(nèi)垂直水平居中,沒法在整個(gè)頁(yè)面垂直水平居中。

  解決辦法1:通過(guò)與框架頁(yè)面消息同步解決,將彈窗消息發(fā)送給主頁(yè)面,主頁(yè)面來(lái)彈窗,對(duì)原項(xiàng)目改動(dòng)大且影響原項(xiàng)目的使用。

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

  4. iframe 內(nèi)的 div 無(wú)法全屏

  彈窗的全屏,指的是在瀏覽器可視區(qū)全屏。這個(gè)全屏指的是占滿用戶的屏幕。

  全屏方案,原生方法使用的是Element.requestFullscreen(),插件:vue-fullscreen。當(dāng)頁(yè)面在 iframe 里面時(shí),全屏?xí)?bào)錯(cuò),且dom 結(jié)構(gòu)錯(cuò)亂。

  解決方案:iframe 標(biāo)簽設(shè)置 allow="fullscreen" 屬性即可

  5. 瀏覽器前進(jìn)/后退問(wèn)題

  iframe 和主頁(yè)面共用一個(gè)瀏覽歷史,iframe 會(huì)影響頁(yè)面的前進(jìn)后退。大部分時(shí)候正常,iframe 多次重定向則會(huì)導(dǎo)致瀏覽器的前進(jìn)后退功能無(wú)法正常使用。并且 iframe 頁(yè)面刷新會(huì)重置(比如說(shuō)從列表頁(yè)跳轉(zhuǎn)到詳情頁(yè),然后刷新,會(huì)返回到列表頁(yè)),因?yàn)闉g覽器的地址欄沒有變化,iframe 的 src 也沒有變化。

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

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

  解決辦法1:onload 事件里面判斷頁(yè)面的標(biāo)題,是否 404 或者 500

  解決辦法2:使用 try catch 解決此問(wèn)題,嘗試獲取 contentDocument 時(shí)將拋出異常。

2. 基座模式方案

  基座模式方案以single-spa和qiankun為代表,這里我選擇qiankun。

  qiankun 是螞蟻金服開源的一款框架,它是基于single-spa 的。他在 single-spa 的基礎(chǔ)上,實(shí)現(xiàn)了開箱即用,除一些必要的修改外,子項(xiàng)目只需要做很少的改動(dòng),就能很容易的接入。

  qiankun框架官網(wǎng):https://qiankun.umijs.org/zh/。

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

  JS entry 的要求比較苛刻:

 ?。?)將 css 打包到 js 里面

 ?。?)去掉 chunk-vendors.js,

 ?。?)去掉文件名的 Hash

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

  建議使用 HTML entry ,使用起來(lái)和 iframe 一樣簡(jiǎn)單,但是用戶體驗(yàn)比 iframe 強(qiáng)很多。qiankun 請(qǐng)求到子項(xiàng)目的 index.html 之后,會(huì)先用正則匹配到其中的 js/css 相關(guān)標(biāo)簽,然后替換掉,它需要自己加載 js并運(yùn)行,然后去掉 html/head/body 等標(biāo)簽,剩下的內(nèi)容原樣插入到子項(xiàng)目的容器中 。

二、微前端方案實(shí)踐

  以“大數(shù)據(jù)分析”項(xiàng)目為例,將客戶特有的需求,如“電子路單”、“數(shù)據(jù)填報(bào)”單獨(dú)提取為可獨(dú)立運(yùn)行的子項(xiàng)目。

  大數(shù)據(jù)分析項(xiàng)目改造為主應(yīng)用基座,代碼倉(cāng)庫(kù)地址:http://192.168.1.102/zouqiongjun/big-data-web.git。

  客戶自定義需求單獨(dú)作為子應(yīng)用項(xiàng)目,代碼倉(cāng)庫(kù)地址:http://192.168.1.102/zouqiongjun/zibo-custom-web.git。

1. 各應(yīng)用工程代碼結(jié)構(gòu)

  sass-base-web:主倉(cāng)庫(kù),主要存放一些批量操作的腳本,用于聚合管理倉(cāng)庫(kù)和一鍵編譯、一鍵部署。

  倉(cāng)庫(kù)代碼結(jié)構(gòu)如下圖所示:

微前端開發(fā)(Vue)(微前端開發(fā)導(dǎo)致彈出層遮罩層跑到最上方)

  big-data-web:大數(shù)據(jù)分析主應(yīng)用

  zibo-custom-web:客戶自定義需求,微應(yīng)用倉(cāng)庫(kù)

  子應(yīng)用可以獨(dú)立運(yùn)行,但是當(dāng)前子應(yīng)用是直接嵌套在主應(yīng)用的main內(nèi)容區(qū)域,所以暫時(shí)并沒有單獨(dú)提供左側(cè)菜單導(dǎo)航,后續(xù)如有需要可以擴(kuò)展和補(bǔ)充此功能。

2. 主應(yīng)用big-data-web改造

  將普通的項(xiàng)目改造成 qiankun 主應(yīng)用基座,需要進(jìn)行三步操作:

  (1) 創(chuàng)建微應(yīng)用容器 – 用于承載微應(yīng)用,渲染顯示微應(yīng)用;

  (2) 注冊(cè)微應(yīng)用 – 設(shè)置微應(yīng)用激活條件,微應(yīng)用地址等等;

  (3) 啟動(dòng) qiankun;

  注意:由于big-data-web主應(yīng)用的路由采用的是hash模式,所以子應(yīng)用的路由也應(yīng)該采用hash模式。

1.1 安裝 qiankun

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

1.2. 在主應(yīng)用中注冊(cè)微應(yīng)用

  為了使用keepAlive緩存,這里我們采用手動(dòng)加載微應(yīng)用的方式。

  當(dāng)微應(yīng)用信息注冊(cè)完之后,一旦瀏覽器的 url 發(fā)生變化,便會(huì)自動(dòng)觸發(fā) qiankun 的匹配邏輯,所有activeRule 規(guī)則匹配上的微應(yīng)用就會(huì)被插入到指定的container 中,同時(shí)依次調(diào)用微應(yīng)用暴露出的生命周期鉤子。

  在views目錄下,新建AppVueHash.vue,作為子應(yīng)用的容器,代碼如下:

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

  這個(gè)id屬性要唯一,最終子應(yīng)用的內(nèi)容將會(huì)掛載在這里。

  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";//子項(xiàng)目路由前綴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 // 開啟樣式隔離 } }];//控制微應(yīng)用手動(dòng)加載 ctrlMicroApp(path){ if (isChildRoute(path)) { this.showAppVueHash = true; this.$nextTick(() => { //手動(dòng)加載 if(!this.mounted){ this.loadApps = apps.map(item => loadMicroApp(item)) this.mounted=true; } }); } else { this.showAppVueHash = false; }

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

  根據(jù)url地址判斷是否是子應(yīng)用,如果是子應(yīng)用,則手動(dòng)加載,否則隱藏子應(yīng)用容器,只加載主應(yīng)用的router-view。

  在ContainerOther第一次加載或路由變化時(shí)手動(dòng)加載微應(yīng)用:

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}`); //之前存在子應(yīng)用頁(yè)簽 const before = oldVal.some(item=>{ return patt.test(item.value); }); //現(xiàn)在存在子應(yīng)用頁(yè)簽 const now = newVal.some(item=>{ return patt.test(item.value); }); if(before && !now){ this.mounted=false; this.loadApps.forEach(app=>{ app.unmount(); }) } }

  監(jiān)聽tab頁(yè)簽變化,關(guān)閉頁(yè)簽時(shí),需要卸載子應(yīng)用。

3. qiankun 子項(xiàng)目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 中項(xiàng)目初始化的容器,不要使用 #app ,避免與其他的項(xiàng)目沖突,建議小駝峰寫法

<div id="appVueHash"></div>

  1. 修改入口文件 main.js:

// -----------子應(yīng)用微前端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();}//測(cè)試全局變量污染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;}// -----------子應(yīng)用微前端end-------------------

  主要改動(dòng)是:引入修改 publicPath 的文件和export 三個(gè)生命周期。

  注意:

  • webpack 的 publicPath 值只能在入口文件修改,之所以單獨(dú)寫到一個(gè)文件并在入口文件最開始引入,是因?yàn)檫@樣做可以讓下面所有的代碼都能使用這個(gè)。
  • 路由文件需要 export 路由數(shù)據(jù),而不是實(shí)例化的路由對(duì)象,路由的鉤子函數(shù)也需要移到入口文件。
  • 在 mount 生命周期,可以拿到父項(xiàng)目傳遞過(guò)來(lái)的數(shù)據(jù),router 用于跳轉(zhuǎn)到主項(xiàng)目/其他子項(xiàng)目的路由,store 是父項(xiàng)目的實(shí)例化的 Vuex(也可以傳遞其他數(shù)據(jù)過(guò)來(lái))。
  1. 修改打包配置 vue.config.js:

const { name } = require("./package");module.exports = { outputDir: "../sass-base-web/cicd-config/test/zibo-custom-web", devServer: { port: 9010, // 關(guān)閉主機(jī)檢查,使微應(yīng)用可以被 fetch disableHostCheck: true, // 配置跨域請(qǐng)求頭,解決開發(fā)環(huán)境的跨域問(wèn)題 headers: { "Access-Control-Allow-Origin": "*", }, proxy: { "/api": { //本地服務(wù)接口地址 target: "http://192.168.10.112:10067", //cas ws: true, pathRewrite: { "^/api": "/", }, }, }, }, // 以下配置可以修復(fù)一些字體文件加載路徑問(wèn)題 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]`, // 微應(yīng)用的包名,這里與主應(yīng)用中注冊(cè)的微應(yīng)用名稱一致 libraryTarget: "umd", // 把子應(yīng)用打包成 umd 庫(kù)格式 jsonpFunction: `webpackJsonp_${name}`, //webpack打包之后保存在window中的key,各個(gè)子應(yīng)用不一致 }, },};

  這里主要就兩個(gè)配置,一個(gè)是允許跨域,另一個(gè)是打包成 umd 格式。為什么要打包成 umd 格式呢?是為了讓 qiankun 拿到其 export 的生命周期函數(shù)。

  注意: 這個(gè) name 默認(rèn)從 package.json 獲取,可以自定義,只要和父項(xiàng)目注冊(cè)時(shí)的 name 保持一致即可。

  vue.config.js中

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

  這里我子應(yīng)用項(xiàng)目編譯后會(huì)將打包文件打包到sass-base-web項(xiàng)目中的zibo-custom-web下。

  1. 路由動(dòng)態(tài)加載

  需要給子項(xiàng)目所有的路由都添加一個(gè)前綴,子項(xiàng)目的路由跳轉(zhuǎn)如果之前使用的是 path 也需要修改,用name 跳轉(zhuǎn)則不用。

  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. 狀態(tài)管理,主應(yīng)用和微應(yīng)用之間的通信

  qiankun 通過(guò) initGlobalState: 定義全局狀態(tài),并返回通信方法,建議在主應(yīng)用使用,微應(yīng)用通過(guò)props 獲取通信方法;

  onGlobalStateChange: 在當(dāng)前應(yīng)用監(jiān)聽全局狀態(tài),有變更觸發(fā) callback;

  setGlobalState: 按一級(jí)屬性設(shè)置全局狀態(tài),微應(yīng)用中只能修改已存在的一級(jí)屬性; 換句話說(shuō)只能修改主用于預(yù)先定義的屬性,后面添加的屬性無(wú)效。

  官方例子:發(fā)布-訂閱的設(shè)計(jì)模式:

  主應(yīng)用:

import { initGlobalState, MicroAppStateActions } from 'qiankun';// 初始化 stateconst actions: MicroAppStateActions = initGlobalState(state);actions.onGlobalStateChange((state, prev) => { // state: 變更后的狀態(tài); prev 變更前的狀態(tài) console.log(state, prev);});actions.setGlobalState(state);actions.offGlobalStateChange();

  子應(yīng)用:

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

  如果主應(yīng)用和子應(yīng)用都是vue技術(shù)棧,可以在子應(yīng)用中把數(shù)據(jù)傳遞給子應(yīng)用,例如store。

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

5. 各應(yīng)用之間的獨(dú)立倉(cāng)庫(kù)以及聚合管理

  實(shí)際開發(fā)中項(xiàng)目存儲(chǔ)在公司倉(cāng)庫(kù)中,以 gitLab 為例, 當(dāng)子應(yīng)用一多,全部放在一個(gè)倉(cāng)庫(kù)下面, 這時(shí)候就顯得很臃腫了,也很龐大,大大的增加了維護(hù)成本,和開發(fā)效率;

我們可以通過(guò) sh 腳本, 初始只需要克隆主倉(cāng)庫(kù)代碼, 然后通過(guò) sh 腳本去一鍵拉取所有子應(yīng)用代碼。

  這里我將單獨(dú)創(chuàng)建一個(gè)用來(lái)編譯和打包的主倉(cāng)庫(kù)項(xiàng)目sass-base-web,倉(cāng)庫(kù)地址:http://192.168.1.102/zouqiongjun/sass-base-web.git。

  項(xiàng)目根目錄下,新建 script/clone-all.sh 文件,內(nèi)容如下:

# 子服務(wù) 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')# 子服務(wù)if [ ! -d "sub-service" ]; then echo '創(chuàng)建sub-service目錄...' mkdir sub-servicefiecho '進(jìn)入sub-service目錄...'cd sub-service# 遍歷克隆微服務(wù)for i in ${!SUB_SERVICE_NAME[@]}do if [ ! -d ${SUB_SERVICE_NAME[$i]} ]; then echo '克隆微服務(wù)項(xiàng)目'${SUB_SERVICE_NAME[$i]} git clone ${SUB_SERVICE_GIT[$i]} fidone echo '腳本結(jié)束...'# 克隆完成

  當(dāng)我們啟動(dòng)主項(xiàng)目的時(shí)候,如果想要使用所有子應(yīng)用的功能,子應(yīng)用,需要一個(gè)一個(gè)啟動(dòng),這樣無(wú)論是開發(fā)還是編譯都十分不便。

  考慮到國(guó)內(nèi)使用npm裝包可能會(huì)由于網(wǎng)絡(luò)的原因安裝失敗,可以讓npm使用國(guó)內(nèi)淘寶鏡像。

  執(zhí)行命令:npm config set registry https://registry.npm.taobao.org。

  在這個(gè)主倉(cāng)庫(kù)項(xiàng)目里面,我們只需要安裝一個(gè)npm-run-all插件。

  運(yùn)行:yarn add npm-run-all -D或者npm i -D。

  該項(xiàng)目下只有一個(gè)package.json文件,用于配置編譯和打包命令,代碼如下:

{ "name": "sass-big-data-web", "version": "1.0.0", "description": "`qiankun`來(lái)實(shí)現(xiàn)`vue`技術(shù)棧的前端微服務(wù)", "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" }}

  要執(zhí)行yarn clone:all,需要用到bash,跳轉(zhuǎn)到sass-base-web目錄,右鍵鼠標(biāo)打開Git bash窗口,如下圖所示:

微前端開發(fā)(Vue)(微前端開發(fā)導(dǎo)致彈出層遮罩層跑到最上方)

  這樣當(dāng)我們把各個(gè)代碼倉(cāng)庫(kù)的代碼都拉取到sub-service這個(gè)目錄下面來(lái),如下圖所示:

微前端開發(fā)(Vue)(微前端開發(fā)導(dǎo)致彈出層遮罩層跑到最上方)

  代碼拉取完成后, 緊接著就是下載各個(gè)項(xiàng)目的依賴及運(yùn)行。

  運(yùn)行npm run serve-all則可以自動(dòng)執(zhí)行package.json中配置的命令,這個(gè)命令最終會(huì)執(zhí)行以下三個(gè)執(zhí)行命令:

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

  這樣就不需要我們自己一個(gè)一個(gè)單獨(dú)的去運(yùn)行各個(gè)項(xiàng)目了。

  總體運(yùn)行步驟: 第一步 clone 主應(yīng)用, 然后依次執(zhí)行 yarn clone:all –> yarn install-all –> yarn start-all 即可運(yùn)行整個(gè)項(xiàng)目。

  build-all:可以編譯整個(gè)項(xiàng)目。

  sub-service目錄,將其添加到.gitignore當(dāng)中,因?yàn)樵趕ass-base-web項(xiàng)目當(dāng)中,我們只需要配置和編譯及打包用,并不需要真正的將所有子應(yīng)用的代碼都提交到sass-base-web項(xiàng)目中,各子應(yīng)用都有自己私有的倉(cāng)庫(kù)。

6. 子項(xiàng)目開發(fā)的一些注意事項(xiàng)

 ?。?)所有的資源(圖片/音視頻等)都應(yīng)該放到src 目錄,不要放在 public 或者static。資源放 src 目錄,會(huì)經(jīng)過(guò) webpack 處理,能統(tǒng)一注入 publicPath。否則在主項(xiàng)目中會(huì)404。

 ?。?)避免 css 污染。組件內(nèi)樣式的 css-scoped是必須的。

 ?。?)給 body 、 document 等綁定的事件,請(qǐng)?jiān)趗nmount 周期清除

 ?。?)謹(jǐn)慎使用 position:fixed

  在父項(xiàng)目中,這個(gè)定位未必準(zhǔn)確,應(yīng)盡量避免使用,確有相對(duì)于瀏覽器窗口定位需求,可以用position: sticky,但是會(huì)有兼容性問(wèn)題(IE不支持)。

  常見問(wèn)題見官網(wǎng):https://qiankun.umijs.org/zh/faq

7. 部署

1. 常規(guī)部署

  主應(yīng)用和微應(yīng)用都是獨(dú)立開發(fā)和部署,即它們都屬于不同的倉(cāng)庫(kù)和服務(wù)。

  場(chǎng)景:主應(yīng)用和微應(yīng)用部署到同一個(gè)服務(wù)器(同一個(gè)IP和端口)

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

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

  • 必須配置 webpack 構(gòu)建時(shí)的 publicPath 為目錄名稱,更多信息請(qǐng)看 webpack 官方說(shuō)明 和vue-cli3 的官方說(shuō)明。
  • history 路由的微應(yīng)用需要設(shè)置 base ,值為目錄名稱,用于獨(dú)立訪問(wèn)時(shí)使用。

  部署之后注意三點(diǎn):

  • activeRule 不能和微應(yīng)用的真實(shí)訪問(wèn)路徑一樣,否則在主應(yīng)用頁(yè)面刷新會(huì)直接變成微應(yīng)用頁(yè)面。
  • 微應(yīng)用的真實(shí)訪問(wèn)路徑就是微應(yīng)用的 entry,entry 可以為相對(duì)路徑。
  • 微應(yīng)用的 entry 路徑最后面的 / 不可省略,否則 publicPath 會(huì)設(shè)置錯(cuò)誤,例如子項(xiàng)的訪問(wèn)路徑是 http://localhost:8080/app1,那么 entry 就是 http://localhost:8080/app1/。

  通過(guò)配置 nginx 端口到目錄的轉(zhuǎn)發(fā)。須要對(duì)外開放子利用對(duì)應(yīng)的端口,將編譯好的利用文件放到對(duì)應(yīng)的配置目錄。

  跳轉(zhuǎn)到sass-base-web目錄,執(zhí)行npm run build-all,會(huì)自動(dòng)執(zhí)行所有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目錄結(jié)構(gòu)如下圖所示:

微前端開發(fā)(Vue)(微前端開發(fā)導(dǎo)致彈出層遮罩層跑到最上方)

  這里是子應(yīng)用和主應(yīng)用部署在同一臺(tái)服務(wù)器上,且IP和端口相同,nginx不需要額外設(shè)置。

  如果子應(yīng)用和主應(yīng)用部署在同一臺(tái)服務(wù)器上, 但是端口不同,需要修改vue.config.js中的outputDir,這個(gè)是打包編譯后代碼存放的路徑,這個(gè)不做配置,默認(rèn)會(huì)將代碼編譯打包到當(dāng)前根目錄下,并生成一個(gè)dist目錄用于存放編譯后的代碼,如下圖所示:

微前端開發(fā)(Vue)(微前端開發(fā)導(dǎo)致彈出層遮罩層跑到最上方)

修改nginx.conf配置:

#gzip on; upstream gateway { server 192.168.31.136:32067;} # 主應(yīng)用 server { listen 32043; server_name web; root /dist; # 關(guān)閉端口重定向 # 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; } } # 子應(yīng)用 server { listen 9010; server_name cus_web; # 子應(yīng)用編譯后的代碼路徑 root /zibo-custom-web; # 允許跨域 add_header Access-Control-Allow-Origin *; # 關(guān)閉端口重定向 # 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 主要作用是用于端口目錄轉(zhuǎn)發(fā),并配置主應(yīng)用訪問(wèn)子應(yīng)用的跨域問(wèn)題。

  使用 docker 配置部署 nginx:

# docker-compose.ymlversion: '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

  將編譯后的主應(yīng)用以及子應(yīng)用放到對(duì)應(yīng)的數(shù)據(jù)卷掛載目錄即可,如主應(yīng)用 /app/micro/portal?! ⊥?,也需要將配置好的 nginx.conf 文件放到指定的數(shù)據(jù)卷掛載目錄,使用 docker-compose up -d 啟動(dòng)即可。

  nginx 端口目錄轉(zhuǎn)發(fā)配置:

# nginx.confuser 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; # 配置跨域訪問(wèn),此處是通配符,嚴(yán)格生產(chǎn)環(huán)境的話可以指定為主應(yīng)用 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; # 配置跨域訪問(wèn),此處是通配符,嚴(yán)格生產(chǎn)環(huán)境的話可以指定為主應(yīng)用 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; } }}

  部署到生產(chǎn),需要修改big-data-web/public/util/config.js中的VUE_APP_ZIBO_CUSTOM_URL配置項(xiàng):

(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",//自定義微應(yīng)用地址 //================================================ VUE_APP_AMAPKEY: "xxxxxx" //高德key };})();

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

8. 總結(jié)

  盡管qiankun框架支持各個(gè)子應(yīng)用使用不同的技術(shù)框架,但是都需要子應(yīng)用做相應(yīng)的改造,而且在其它技術(shù)棧中總是會(huì)時(shí)不時(shí)的出現(xiàn)各種難以預(yù)料的錯(cuò)誤,一旦出現(xiàn)問(wèn)題,都需要我們?nèi)ジ脑齑a。所以如果都是vue技術(shù)棧,建議使用qiankun做微前端。

  如果是第三方公司的項(xiàng)目接入進(jìn)來(lái),由于他們的代碼不受我們控制,需要酌情考慮是否用iframe。

  參考文獻(xiàn):qiankun 微前端方案實(shí)踐及總結(jié)

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號(hào)
公眾號(hào)
在線咨詢
分享本頁(yè)
返回頂部