從0實現一個前端微服務(上)

前端微服務的幾種實現方式

什麼是前端微服務,網上大把的介紹,我就不囉嗦了,簡單來講,就是把各個子項目整合到一塊兒。css

《前端架構:從入門到微前端》這本書中介紹,微前端架構通常能夠由如下幾種方式進行:html

  1. 使用 HTTP 服務器的路由來重定向多個應用(也就是連接跳轉)
  2. 在不一樣的框架之上設計通信、加載機制,諸如 Mooa 和 Single-SPA
  3. 經過組合多個獨立應用、組件來構建一個單體應用
  4. iFrame。使用 iFrame 及自定義消息傳遞機制
  5. 使用純 Web Components 構建應用
  6. 結合 Web Components 構建

其中比較常見的就是iframesingle-spa,這二者各有千秋。前端

iframe和single-spa的優缺點

iframe的優缺點

缺點

  1. 頁面加載問題: 影響主頁面加載,阻塞onload事件,自己加載也很慢,頁面緩存過多會致使電腦卡頓。(沒法解決)vue

  2. 佈局問題:iframe必須給一個指定的高度,不然會塌陷。解決辦法:子系統實時計算高度並經過postMessage發送給主頁面,主頁面動態設置高度,修改子系統或者代理插入腳本。有些狀況會出現多個滾動條,用戶體驗不佳。node

  3. 彈窗及遮罩層問題:只能在iframe範圍內垂直水平居中,無法在整個頁面垂直水平居中。react

    • 解決辦法1:經過與框架頁面消息同步解決,將彈窗消息發送給主頁面,主頁面來彈窗,對原項目改動大且影響原項目的使用。
    • 解決辦法2:修改彈窗的樣式:隱藏遮罩層,修改彈窗的位置。修改的辦法就是經過代理服務器插入css樣式。
    • 補充:iframe裏面的內容沒法實現佔滿屏幕的彈窗(非全屏),他只能在iframe範圍內全屏,沒法跳出iframe的限制在主頁面全屏,不過這種狀況也不多。
  4. 瀏覽器前進/後退問題:iframe和主頁面共用一個瀏覽歷史,iframe會影響頁面的前進後退,大部分時候正常,iframe屢次重定向則會致使瀏覽器的前進後退功能沒法正常使用,不是所有頁面都會出現,基本能夠忽略。可是iframe頁面刷新會重置(好比說從列表頁跳轉到詳情頁,而後刷新,會返回到列表頁),由於瀏覽器的地址欄沒有變化。webpack

  5. iframe的頁面跳轉到其餘頁面出問題,好比兩個iframe之間相互跳轉,直接跳轉會只在iframe範圍內跳轉,因此必須經過主頁面來進行跳轉。不過iframe跳轉的狀況不多ios

  6. 系統之間的通信須要經過postMessage,存在必定的安全性git

優勢

  1. 徹底隔離了cssjs,避免了各個系統之間的樣式和js污染
  2. 能夠在子系統徹底不修改的狀況下嵌入進來

single-spa的優缺點

缺點

  1. cssjs須要制定規範,進行隔離。不然容易形成全局污染,尤爲是vue的全局組件,全局鉤子。
  2. 須要子系統配合修改。可是不影響子系統獨立開發部署,路由部分對子系統有一些改動,可是不影響功能。

優勢

  1. 加載快,能夠將全部系統共用的模塊提取出來,實現按需加載,一次加載,其餘的複用。
  2. 修改子系統的樣式,不須要代理服務器,直接修改,由於同屬於一個document
  3. 用戶體驗好、快,內容的改變不須要從新加載整個頁面,避免了沒必要要的跳轉和重複渲染
  4. http請求少,服務器壓力小。

single-spa和iframe對比

對比項 single-spa iframe 補充
加載速度 single-spa能夠將全部系統共用的vue/vuex/vue-router等文件提取出來,只加載一次,各系統複用,加載速度很快,可是必須保證文件版本統一 iframe會佔用主系統的http通道,影響主系統的加載,加載速度很慢 二者均可以經過http緩存提升必定的加載速度,可是對於vue這些通用文件無法作cdn,由於內部系統極可能沒法訪問外網
兼容性 single-spa只適用於vue、react、angular編寫的系統,對一些jq寫的老系統無能爲力 iframe則能夠嵌入任何頁面
技術難度 single-spa須要必定的技術儲備,有一些學習成本 iframe門檻則很低,無需額外學習
侷限性 single-spa能夠嵌入任何部件 iframe只能嵌入頁面,固然了也能夠把一個部件單獨寫成一個頁面
改形成本 single-spa必定要對子系統進行改造,可是改造的內容並很少不少,半小時便可完成 iframe能夠不對原系統進行改造,可是必須藉助代理服務器進行插入腳本和css,增長了代理服務器也增長了系統的不穩定性(兩臺服務器中的任何一臺掛掉都會致使系統不可用),服務器也須要成本。如對原系統進行改造,則工做量和single-spa至關 項目的源文件丟失或者其餘一些沒法改動源文件的狀況,只能使用iframe

補充:github

  1. 對於SEOiframe沒法解決,可是single-spa有辦法解決(谷歌能支持單頁應用的SEO,百度則須要SSR),可是內部系統,SEO的需求比較少。
  2. iframe存在安全隱患,兩個iframe頁面互相引用則會致使無限嵌套bug,會致使頁面卡死,目前只能經過代理服務器檢查iframe頁面內容來處理

iframe和single-spa的實現及簡單原理

iframe很簡單,一個標籤就實現了。single-spa比較陌生,我會詳細介紹。

single-spa初探

vue爲例,vue-cli4生成的項目打包生成的index.html文件內容以下(精簡了一些無關的內容):

<!DOCTYPE html>
<html lang=en>
<head>
  <meta charset=utf-8>
  <title>my-app</title>
  <link href=/js/about.6b1cbb89.js rel=prefetch>
  <link href=/css/app.c8c4d97c.css rel=preload as=style>
  <link href=/js/app.6a6f1dda.js rel=preload as=script>
  <link href=/js/chunk-vendors.164d8230.js rel=preload as=script>
  <link href=/css/app.c8c4d97c.css rel=stylesheet>
</head>
<body>
  <noscript>
    <strong>We're sorry but my-app doesn't work properly without JavaScript enabled. Please enable it to
      continue.</strong>
  </noscript>
  <div id=app></div>
  <script src=/js/chunk-vendors.164d8230.js> </script>
  <script src=/js/app.6a6f1dda.js> </script>
</body>
</html>
複製代碼

其中最核心的部分是:

<link href=/css/app.c8c4d97c.css rel=stylesheet>
<div id=app></div>
<script src=/js/chunk-vendors.164d8230.js> </script>
<script src=/js/app.6a6f1dda.js> </script> 
複製代碼

猜測:可否藉助node服務器,將子系統的index.html獲取到,而後讀取HTML,獲取到這幾個標籤,返回給主系統,主系統直接插入到body中,可否呈現出子系統?

node代理代碼實現,操做DOM使用的是cheerio插件:

const http = require('http')
// 引入cheerio模塊
const cheerio = require('cheerio')
const axios = require('axios')
const server = http.createServer(function (request, response) {
  //請求子系統服務器,獲取到index.html文件
  axios.get('http://localhost/').then(res => {
    response.writeHead(200, { 
      'Content-Type': 'application/xml' ,
      'Access-Control-Allow-Origin': '*'
    })
    // 加載HTML字符串
    const $ = cheerio.load(res.data)
    $('link').each(function () {
      $(this).attr('href', 'http://localhost' + $(this).attr('href'))
    })
    $('script').each(function () {
      $(this).attr('src', 'http://localhost' + $(this).attr('src'))
    })
    const resp = $('body').prepend($('link[rel=stylesheet]')).html();
    response.end(resp)
  }).catch(e => {
    console.log(e)
  })
})
server.listen(8080)
複製代碼

須要注意的是:

  1. 這裏面的引入js/css文件路徑都是相對路徑,須要拼上子項目的前綴。
  2. v-html插入的DOM片斷,外鏈script不會生效,須要手動插入

結果是主系統中#app裏面能渲染出子系統,可是#app裏面動態生成的HTMLimg/video/audio等文件的路徑是相對的,因此會請求到主系統上,可是這些文件並不在主系統,因此會404,一樣,按需加載的路由頁面對應的js/css文件也是相對路徑,會請求出錯。若是路由沒按需加載,則不存在這個問題

結論:能夠實現微服務效果,可是須要解決文件相對路徑的問題,index.html裏面的link/script還能夠手動加上,可是動態生成的html裏面的img/video/audio等,以及按需加載路由頁面對應的js/css沒法經過代理服務器解決。

解決思路:

  1. 這裏面的js/css/img/video等都是相對路徑,可否經過webpack打包,將這些路徑所有打包成絕對路徑?這樣就能夠解決文件請求失敗的問題。

  2. 可否像CDN同樣,一個服務器掛了,會去其餘服務器上請求對應文件。或者說服務器之間的文件共享,主系統上的文件請求失敗會自動去子服務器上找到並返回。

  3. 可否手動(或藉助node)將子系統的文件所有拷貝到主項目服務器上,node監聽子系統文件有更新,就自動拷貝過來,而且按js/css/img文件夾合併

查閱webpackvue-cli3官網後發現:

默認狀況下,Vue CLI 會假設你的應用是被部署在一個域名的根路徑上,例如 https://www.my-app.com/。若是應用被部署在一個子路徑上 https://www.my-app.com/my-app/,而你使用的是history模式的路由,對於url:https://www.my-app.com/my-app/page1,vue沒法區分my-app是真實路徑,而page1是路由參數,這個時候須要設置 publicPath/my-app/,vue才能正確的請求文件資源和匹配路由。

這裏能夠將vue-cli3publicPath設置爲https://www.my-app.com/my-app/,而後代碼裏面的js/css/img/video路徑都會變成絕對路徑,前綴是https://www.my-app.com/my-app/,這樣就解決了url路徑的問題。

這樣就能夠實現一個簡單的single-spa應用,可是加載好的Vue子系統不會在切換到下一個系統的時候卸載掉,子系統過多則會致使卡頓,而且css/js污染的可能性增長,實用性不大。

最後

文章有什麼疑問or錯誤,歡迎評論。下一篇預告:從0實現一個single-spa項目,包含完整的打包/開發調試流程,老項目如何改造等。

相關文章
相關標籤/搜索