巧用serverless實現代理轉發解決微前端跨域
前言
痛點
- 多平台窗口之間切換復雜,往往不能准確點到想要的窗口或界面,客戶展示時容易出現問題。
- 不同系統之間的數據不通,老板想要任意頁面數據拼合在一個頁面,展示多種數據,盡可能實現總覽。
- 企業內運營多個后台管理系統,賬號密碼不一致造成額外記憶負擔。
明確需求,尋找最低成本解決方法
如果說不限制成本,后台前台都動起來,以上問題可以解決的很漂亮。但無奈會議上否決了動作較大的方案,此事也就暫時告一段落。經過一段時間的思索我還是有了以下思路。
- 將某一頁面嵌入到另一頁面最常見的莫過於iframe標簽了,但它並不能緩存實例,跳轉頁面后再回來它會重新渲染,不符合需求。
那主流微前端方案qiankun呢?簡單實踐后發現改造確實簡單,但無奈需要改造的子應用太多,且每個改造完畢都需要重新部署,於我一個人做而言成本太高,還是不符合需求。
那有沒有更容易改造子應用的微前端方案呢?有!
microapp。
- 微前端的部署不可避免的就會提到跨域問題。一般情況下在生產環境中使用nginx即可解決,但遺憾我司目前使用的是tomcat,所以只能另辟蹊徑。
如果要求后台去加cors頭或者前端去加nodejs中間件不可避免的都要有服務端的參與,先編碼再部署,涉及多個服務端,工程量大,影響也未知,並不是較好的解決方法。
那有沒有其他方法可以實現代理轉發呢?我找到的方法是serverless!
以下來逐一說明~~~
概念相關
微前端
不得不說微前端框架市面上推出的越來越多,最為流行的莫屬qiankun。固然十分優秀,但並不滿足我司需求,經過多方調研,注意到了與qiankun不同思路的microApp框架。
microApp借鑒了webComponent思想,通過customElement結合自定義的shadowDom,將一個一個子應用封裝成一個一個類webComponent的組件,實現了子應用的組件化渲染。
這里說明一下我使用microapp的原因:
- 接入成本低,需求不高的情況下無需對子應用硬編碼,可節省大量時間和精力。
- 支持應用緩存,非常方便的恢復用戶操作行為。
- 支持預加載,對於我來說這是個非常重要的功能,因為有的服務器帶寬僅開了3M大小,開啟預加載后首屏渲染速度可以得到很大的提升。
- 擴展性強,可以滿足日后可能出現的需求。
(注:具體是否需要改造子應用請
查閱文檔。)
以上僅僅是描述了microapp的個別優勢,更多好用的功能還請前往
文檔了解。
serverless
直白說,serverless是概念,表示開發者無需關心“服務器”相關事項,只編寫業務代碼上傳到運營商的平台調用即可。
我這里采用的是DCloud推出的unicloud服務,它由阿里雲和騰訊雲的serverless服務之上封裝而成。部分人害怕和雲廠商之間形成捆綁關系,unicloud可以自主切換供應商,就較好的解決了這個問題。本文采用的是阿里雲服務。
說明一下我這里使用unicloud的原因:
- 成熟的輪子,基座應用可直接選用名為uni-admin的后台管理模板。功能齊全,拿來即用。(uniapp可開發PC端)
- 配合HBuilderX編碼可以鼠標右鍵一鍵上傳雲函數,開發方便。
- 網頁托管服務網頁和資源可以直上cdn。
- 免費。免費。免費。
具體操作
架構設計
已知API服務是serverless最常用的落地形式。感興趣可以看看
這篇文章。


根據我司具體場景,觸發雲函數的時機只存在於前台,不可通過后台配合調用。所以我所想到的的架構如下圖所示。


也就是說只要能將客戶端真實發送的請求攔截並且替換為url化的雲函數,就可以觸發代理轉發邏輯,返回給客戶端帶有cors頭的響應數據。知道了核心方法接下來逐個解決就好了。
基座應用uni-admin改造
得益於微前端框架技術棧無關這個核心功能,uniapp技術棧也能發揮價值。這里我們對uni-admin進行小小的改造以適合不同的子應用界面。
頁面布局調整
子應用的菜單欄有的在左有的在上,那么基座應用的菜單欄就需要適時的隱藏了。這里我想到的方式是為uniapp項目創建根組件。功能主要有三:1. 為菜單欄包裹抽屜組件。 2. 增加全局懸浮按鈕呼出基座應用菜單。 3. slot插槽占位以便輸出子組件。


效果如下圖所示


microapp接入
1. 安裝依賴
查看代碼
npm i @micro-zoe/micro-app --save
2. 在入口處引入
查看代碼
// index.js
// 這里官方文檔沒有特別說明將此語句寫在第一位,但根據qiankun的經驗來看最好是在第一位。
import microApp from '@micro-zoe/micro-app'
microApp.start()
3. 為子應用分配路由
查看代碼
// router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import MyPage from './my-page.vue'
Vue.use(VueRouter)
const routes = [
{
// 👇 非嚴格匹配,/my-page/* 都指向 MyPage 頁面
path: '/my-page/*', // vue-router@4.x path的寫法為:'/my-page/:page*'
name: 'my-page',
component: MyPage,
},
]
export default routes
4. 在MyPage頁面中嵌入子應用
查看代碼
<template>
<div>
<h1>子應用</h1>
<!--
name(必傳):應用名稱
url(必傳):應用地址,會被自動補全為http://localhost:3000/index.html
baseroute(可選):基座應用分配給子應用的基礎路由,就是上面的 `/my-page`
-->
<micro-app name='app1' url='http://localhost:3000/' baseroute='/my-page'></micro-app>
</div>
</template>
fetch攔截fetch-intercept的使用
我司API接口都帶有cors跨域頭,只是文檔類型出於安全問題並不帶有跨域頭,所以我只需要攔截fetch請求即可。
使用方法
// @/api/fetchInterceptor.js
import fetchIntercept from 'fetch-intercept';
const fetchInterceptor = fetchIntercept.register({
request: function (url, config) {
return [url, config];
},
requestError: function (error) {
return Promise.reject(error);
},
response: function (response) {
return response;
},
responseError: function (error) {
return Promise.reject(error);
}
});
function InitFetchInterceptor (Vue) {
Vue.prototype.$fetchInterceptor = fetchInterceptor
}
export default {
install(Vue) {
InitFetchInterceptor(Vue)
}
}
// main.js 全局使用
import fetchIntercept from './api/fetchInterceptor'
Vue.use(fetchIntercept)
注意這里可能會出現一個報錯找不到whatwg-fetch模塊。解決方法是在webpack external添加重啟項目即可。issuse地址:https://github.com/werk85/fetch-intercept/issues/6。
完成上述步驟我們就可以全局攔截fetch請求了,在request鈎子中拋出url化的雲函數,就達到了替換請求url的效果。再將原有請求url以參數的形式一同發送給雲函數,客戶端的工作即結束。
微應用改造
我司需求下微應用無需改造!略!
雲函數怎么寫
上面說到雲函數url化,實際就是以url的方式調用雲函數,這里有兩個小坑說明一下,
1. 阿里雲雲函數url化會自動觸發下載,必須綁定自定義域名才能解決。騰訊雲沒有此問題。
2. url化后取參方式有所變動

具體轉發邏輯也很簡單:雲函數接收參數-->發起請求-->返回數據。
這里提一下雲函數中訪問其他HTTP服務不用裝axios啦!unicloud提供了uniCloud.httpclient.request()這個API。
'use strict';
// 雲函數url化后如何訪問:https://uniapp.dcloud.net.cn/uniCloud/http.html#request-url
async function proxy(event) {
// 客戶端雲函數url化后,這里解構獲取參數
const { url } = event.queryStringParameters
const res = await uniCloud.httpclient.request(url, {
followRedirect: true // 開啟3xx響應時自動重定向
// gzip: true // HttpClient 將自動設置 Accept-Encoding: gzip 請求頭,且會自動解壓帶 Content-Encoding: gzip 響應頭的數據
})
return {
// 代表使用阿里雲集成響應
mpserverlessComposedResponse: true,
// 表示body是否為Base64編碼
isBase64Encoded: true,
headers: res.headers,
body: res.data.toString('base64')
}
}
exports.main = async (event, context) => {
try {
const res = await proxy(event)
return res
} catch (e) {
return {
mpserverlessComposedResponse: true,
isBase64Encoded: false,
statusCode: 500,
headers: {
'content-type': 'text/plain'
},
body: e.message,
}
}
};
補充
我是否用到微前端?
參考這篇。
微前端能否順利實施還得看后台支持。如果有nginx網關和SSO單點登錄存在,將會更加順暢。
總結
這篇文章算是記錄了從技術角度了解到痛點后的處理過程。先根據痛點理清需求,再技術調研獲取最優實現。貫穿的主題就是提效降本。