巧用serverless實現代理轉發解決微前端跨域


巧用serverless實現代理轉發解決微前端跨域

 

前言

痛點

  1. 多平台窗口之間切換復雜,往往不能准確點到想要的窗口或界面,客戶展示時容易出現問題。
  2. 不同系統之間的數據不通,老板想要任意頁面數據拼合在一個頁面,展示多種數據,盡可能實現總覽。
  3. 企業內運營多個后台管理系統,賬號密碼不一致造成額外記憶負擔。

明確需求,尋找最低成本解決方法

如果說不限制成本,后台前台都動起來,以上問題可以解決的很漂亮。但無奈會議上否決了動作較大的方案,此事也就暫時告一段落。經過一段時間的思索我還是有了以下思路。
  • 將某一頁面嵌入到另一頁面最常見的莫過於iframe標簽了,但它並不能緩存實例,跳轉頁面后再回來它會重新渲染,不符合需求。
  那主流微前端方案qiankun呢?簡單實踐后發現改造確實簡單,但無奈需要改造的子應用太多,且每個改造完畢都需要重新部署,於我一個人做而言成本太高,還是不符合需求。
  那有沒有更容易改造子應用的微前端方案呢?有! microapp
  • 微前端的部署不可避免的就會提到跨域問題。一般情況下在生產環境中使用nginx即可解決,但遺憾我司目前使用的是tomcat,所以只能另辟蹊徑。
  如果要求后台去加cors頭或者前端去加nodejs中間件不可避免的都要有服務端的參與,先編碼再部署,涉及多個服務端,工程量大,影響也未知,並不是較好的解決方法。
  那有沒有其他方法可以實現代理轉發呢?我找到的方法是serverless!
 
以下來逐一說明~~~
 

概念相關

微前端

  不得不說微前端框架市面上推出的越來越多,最為流行的莫屬qiankun。固然十分優秀,但並不滿足我司需求,經過多方調研,注意到了與qiankun不同思路的microApp框架。
microApp借鑒了webComponent思想,通過customElement結合自定義的shadowDom,將一個一個子應用封裝成一個一個類webComponent的組件,實現了子應用的組件化渲染。
   這里說明一下我使用microapp的原因:
  1. 接入成本低,需求不高的情況下無需對子應用硬編碼,可節省大量時間和精力。
  2. 支持應用緩存,非常方便的恢復用戶操作行為。
  3. 支持預加載,對於我來說這是個非常重要的功能,因為有的服務器帶寬僅開了3M大小,開啟預加載后首屏渲染速度可以得到很大的提升。
  4. 擴展性強,可以滿足日后可能出現的需求。
(注:具體是否需要改造子應用請 查閱文檔。)
 
以上僅僅是描述了microapp的個別優勢,更多好用的功能還請前往 文檔了解。
 

serverless

  直白說,serverless是概念,表示開發者無需關心“服務器”相關事項,只編寫業務代碼上傳到運營商的平台調用即可。
  我這里采用的是DCloud推出的unicloud服務,它由阿里雲和騰訊雲的serverless服務之上封裝而成。部分人害怕和雲廠商之間形成捆綁關系,unicloud可以自主切換供應商,就較好的解決了這個問題。本文采用的是阿里雲服務。
   說明一下我這里使用unicloud的原因:
  1. 成熟的輪子,基座應用可直接選用名為uni-admin的后台管理模板。功能齊全,拿來即用。(uniapp可開發PC端)
  2. 配合HBuilderX編碼可以鼠標右鍵一鍵上傳雲函數,開發方便。
  3. 網頁托管服務網頁和資源可以直上cdn。
  4. 免費。免費。免費。
 

具體操作

架構設計

  已知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單點登錄存在,將會更加順暢。
 

總結

  這篇文章算是記錄了從技術角度了解到痛點后的處理過程。先根據痛點理清需求,再技術調研獲取最優實現。貫穿的主題就是提效降本。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM