效率前端微應用推進之微前端研發提效|得物技術
目錄
一、背景
1. 業務背景
2. 名詞解釋
二、研發費力度痛點
1. 費力點 1
2. 費力點 2
3. 費力點 3
三、技術調研
1. Shared 通訊
2. Mock 父應用環境
3. Chrome 代理外掛
四、Chrome 代理外掛
1. 產品設計
2. 技術設計
五、推進情況
1. 專案覆蓋率
2. 研發效率
3. 使用者反饋
六、總結
一
背景
業務背景
得物效率前端所在的效率工程為提升企業協作效率而生,面臨大量的 PC 側的中後臺應用場景。
在之前的微信公眾號《得物效率前端微應用推進過程與思考》中詳細介紹了效率前端推進微應用落地的思路和部分效果。
這篇文章將著重介紹得物效率前端微應用推進中,微前端的研發效率遇到的挑戰和解決方案。
名詞解釋
微應用
「微應用」是得物效率前端內部稱謂,是一個基於“monorepo & 微前端 & 基座與業務分離”的、包括“文件 & 工具”的一套體系化降低研發成本和提升使用者體驗的技術產品。
微前端
「微前端」是得物效率前端微應用推進的重要一環,尤其是父子應用技術棧不同時,利用 iframe / qiankun / wujie / micro-app
等工具進行微前端化改造,能顯著增強業務擴充套件性。
基座應用/父應用
微前端中,基座應用(父應用)、子應用是常見概念,本文描述中,“基座應用”又名“父應用”,為簡化文案,“基座應用與子應用”也被稱為“父子應用”。
瀏覽器外掛/擴充套件
chrome://extensions/
頁面中對第三方工具稱為“擴充套件”,在本文語境下,又稱為“外掛”。
二
研發費力度痛點
就研發效率而言,微前端在團隊多個業務落地後,面臨研發過程費力度高的問題。
費力點1
本地開發時,從啟動 1 個本地服務,變為需啟動 2 個本地服務:
大大多數情況下,專案需要啟動 2 個本地服務(基座應用和子應用)才能進行日常開發,因為子應用通常依賴基座應用透傳一些依賴資料。
而非微前端場景下,啟動 1 個應用就可以了,這反而引入了降低了研發效率。雖然微前端方案更注重業務效率,但研發效率也是必須要考慮的。
如果電腦效能一般的話,卡頓問題就隨之而來了。
費力點2
基座應用原生程式碼需要做適配性改造:
如父應用需要區分本地環境和生產環境類似這樣的程式碼,雖然程式碼量不大,但還是需要關注的:
// 基座程式碼if (isLocal) { return <microapppage src="localhost://8000/a/b/c" />} else if (isProd) { return <microapppage src="}
費力點3
頻繁無規律重新整理:
在 Qiankun 微前端框架且本地開發環境下,基座應用與子應用頁面均需要 WebSocket 與其自身本地服務進行通訊。
在同時啟動基座與子應用的本地服務後,修改子應用程式碼以及偶發的,頁面會觸發 Reload,而不是區域性更新,開發體驗很差。針對這個問題我們做了一些分析。
HMR 的熱更新邏輯
本地開發過程會啟動 Webpack-Dev-Server 服務,其會監聽業務檔案變化,瀏覽器透過 WebSocket 與 Webpack-Dev-Server 進行通訊。
當發現檔案內容改變時 Webpack-Dev-Server 會根據更新的檔案內容生成 Hash 資訊傳遞給瀏覽器,如圖:
當瀏覽器收到資訊時,會根據收到的資訊和配置進行判斷是重新整理操作還是熱更新操作。熱更新時,Webpack-Dev-Serer 透過 Jsonp 拉取最新的 JS 模組程式碼,並進行模組替換,如圖:
若此過程異常,則會降級為頁面重新整理。
無規律重新整理原因分析
基座應用(埠 8010)熱更新時返回的檔案 Json 和 JS 檔案(分別是 **:8010/update.json 和 **:8010/update.js)內容如下:
但在巢狀在基座應用中的本地子應用(埠 8020)熱更新時,兩個同類檔案並沒有返回內容(**:8020/update.json
和 **:8020/update.js
)
若瀏覽器單獨開啟兩個檔案的地址(**:8020/update.json
和 **:8020/update.js
),有內容返回。
但是在基座與子應用巢狀的情況下,子應用的任何請求(包括 update.json
/ update.js
)都會被基座應用代理,開發環境下很容易出現子應用的更新探針請求被基座代理後,出現內容丟失的情況。
子應用 HMR 邏輯檢測到更新探針請求內容異常,區域性更新失效,降級為頁面重新整理。
即使該問題解決,微前端應用開發者依然面臨同時啟動 2 個應用才能啟動開發的問題,所以我們不過度投入精力關注這個問題。
三
技術調研
解決「預設情況下,父子應用需要分別獨立啟動,並指定關聯關係」的問題,最好的方式是迴歸到非微前端場景下的常規開發方式,即只啟動 1 個應用進行本地開發。
通常情況下,我們開發的是子應用(也就是業務頁面),那先實現子應用單獨啟動即可開啟專案開發吧,以下是面向該需求的技術調研。
Shared 通訊
Shared 通訊方案的原理是,主應用維護一個狀態池,透過 Shared 例項暴露一些方法給子應用使用。
同時,子應用需要單獨維護一份 Shared 例項,在獨立執行時使用自身的 Shared 例項,在嵌入主應用時使用主應用的 Shared 例項,這樣就可以保證在使用和表現上的一致性。
Shared 通訊方案要求父子應用都各自維護一份屬於自己的 Shared 例項,同樣會增加專案的複雜度。同時,在子應用獨立執行時,Shared 只能獲取本地快取資料,無法真正做到完全獨立於子應用執行。
Mock 父應用環境
也就是在子應用中模擬父應用巢狀環境,提供一個獨立的模擬父應用的元件,封裝了 Layout 佈局、許可權、使用者資訊等,並且具備必要的父子通訊能力,子應用呼叫該 Mock 元件,獨立啟動以後進行日常開發。
這個方案和 Shared 通訊有類似之處。
使用者體驗
// 這是子應用程式碼
import { MicroLayout } from '@abc/components';
// 元件內使用
<MicroLayout title="Layout 內容">
{children}
</MicroLayout>
流程設計
其他
Chrome 代理外掛
也就是透過 Chrome 外掛,將線上子應用 URL 代理到原生程式碼。
使用者體驗
其他
四
Chrome 代理外掛
產品設計
“Chrome 外掛代理子應用到原生程式碼”以提升微前端研發效率,該瀏覽器外掛需要具備以下核心能力:
規則靈活配置。外掛實現了 from 和 to 的地址對映規則配置表單。
快取能力。使用者關閉瀏覽器/電腦後再次開啟,仍然能夠使用之前儲存的代理規則。 快捷操作。為常用產品配置內建規則,一鍵即可啟動,非常方便。 實時顯示。需要實時顯示代理規則的生效情況,方便使用者確認哪些規則正在生效。
技術設計
Proxy 和 Popup
功能流轉
無感更新(Seamless Update)
這麼做需要在 chrome 外掛的 csp 安全策略配置中加入 cdn 域名白名單,允許外掛訪問外部 cdn 資源。
{
...
"permissions": [
"webRequest", // 允許瀏覽器開放http請求劫持的功能
"storage", // 允許使用瀏覽器快取
"activeTab",
"background",
"webRequestBlocking",
"<all_urls>"
],
// 允許特定域名可以訪問的安全策略
"content_security_policy":"script-src 'self' 'sha256-G7YAg/PQDo8GYc/fSYvWtXP98kXS7iqT7K4QZgyhUIE='; object-src 'self'",
"content_scripts": [
{
"matches": ["*://*.xxxxxxx.net/*"],
"js": ["contentScript.bundle.js"]
}
]
}
// ==========attention===========
//v2配置,v2版本中可以配置script等透過外部引入,這個content_security_policy配置引數不加或者加上之後相應的值填none
"content_security_policy": "script-src 'none'; object-src 'none'",
//v3配置,v3版本中安全政策配置script引入等資訊,都必須填寫self,即只允許script標籤引用當前外掛內部檔案,不允許引用外部連結,如果不填寫self的話,外掛新增到擴充套件程式時會報錯
"content_security_policy": {
//原文:此政策涵蓋您的擴充套件程式中的頁面,包括 html 檔案和服務人員;
"extension_pages": "script-src 'self'; object-src 'self'",
//原文:此政策涵蓋您的擴充套件程式使用的任何[沙盒擴充套件程式頁面](https://developer.chrome.com/docs/extensions/mv3/manifest/sandbox/)。;
"sandbox": "sandbox allow-scripts; script-src 'self'; object-src 'self'"
},
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Popup</title>
<script>
var scriptEl = document.createElement('script');
scriptEl.defer = "defer";
scriptEl.src = " + Date.now();
document.getElementsByTagName('head')[0].appendChild(scriptEl);
</script>
</head>
<body>
<div id="app-container"></div>
</body>
</html>
Proxy
攔截頁面請求。 提供快取配置的功能,使用者在關閉瀏覽器後,配置的對映關係不會自動消失,以便使用者下次開啟的時候正常提供服務。 通訊功能,需要將使用者在 Popup 頁面互動時提交的資料地址資料提供給攔截方法,從而實現對應的攔截效果。 提供配置資訊狀態的快取資料,方便觀測攔截效果。
chrome.webRequest.onBeforeRequest.addListener( worker.getRequest.bind(worker), {urls: worker.getFilterUrls(worker.replaceRules)}, ['blocking']);// Class background class Background { constructor() { this.replaceRules = [] } getRequest(details) { if (!details) { // chrome未返回任何request return false } const {url} = details const returnObj = {} if (this.replaceRules.length) { // 存在替換規則 this.replaceRules.map((item) => { if (url.includes(item.from)) { // 更新 icon 狀態 this.setBadgeInfo(true) // 代理替換 returnObj.redirectUrl = url.replace(item.from, item.to) // 快取更新當前代理域名 chrome.storage.sync.get(null, function (data) { if (data.messageProxyingData) { // 存在被代理的資料,合併資料 const isExistMessageProxyingData = data.messageProxyingData chrome.storage.sync.set({messageProxyingData: Object.assign(isExistMessageProxyingData, {[item.key]: true})}) } else { // 未存在快取,使用新資料 chrome.storage.sync.set({messageProxyingData: {[item.key]: true}}) } }) } }) } return returnObj }}
chrome.webRequest.onBeforeRequest
/ getRequest
配合攔截獲取請求地址,然後攔截替換目標路徑,當然這是比較簡單的邏輯,複雜的可以參考 glob
寫法代理連結。Popup
const sendMessage = (data: DataType[]) => { // 保留資料中已開啟的資料,進行資料傳輸 const finalData = data.filter.((item) => item.is_open); chrome.runtime.sendMessage({ data: finalData, type: 'rule' });};const handleSaveData = ( data: DataType[], isNeedSendMsg: boolean, key?: number[]) => { setDataSource([...data]); // 快取配置 chrome.storage.sync.set({ popupData: data }); if (isNeedSendMsg) { sendMessage(data); if (key?.length) { chrome.storage.sync.get(null, function (data) { const messageProxyingData = data.messageProxyingData || {}; if (messageProxyingData) { key.forEach.((item) => { delete messageProxyingData[item]; }); } chrome.storage.sync.set({ messageProxyingData }); }); } }};
ContentScript
const scrpit = document.createElement('script')scrpit.textContent = `window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = window.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ || { iframeReady: function () { var overlay = document.querySelector( 'iframe[style*="z-index: 2147483647;"]', ); if (overlay) { overlay.style.display = 'none'; } }, };`document.body.appendChild(scrpit)
五
推進情況
專案覆蓋率
目前該外掛在效率前端的微應用專案覆蓋率達到了 100%,在推進過程中,只需要使用者「安裝 1 次外掛」,即可使用,後續更新無需關心(減少「外掛需要更新」的心理負擔)。
研發效率
避免了父子應用均啟動時,子應用程式碼更新後,父應用被動觸發 Reload 的問題。 子應用 HMR 熱更新延時與非微前端專案沒有差距。
使用者反饋
從研發側的反饋來看,確實有效地提高了微應用場景下的研發效率,告別啟動多個本地服務的煩惱。
六
思考
這個產品是效率前端業務小組發起,從 0 到 1 進行產品設計、技術方案調研、開發、完成落地的,既讓參與的同學經歷了完整的技術產品研發過程,也解決了實際業務中遇到的問題,並得到了正反饋。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70027824/viewspace-2996693/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 微前端在得物客服域的技術實踐/ 那麼多微前端框架,為啥我們選Qiankun + MF前端框架
- 微前端開發(Vue)前端Vue
- 微前端架構初探以及我的前端技術盤點前端架構
- 微前端概述(Micro Frontends) 以及相比單體應用,微前端能帶來什麼好處前端
- 用微前端的方式搭建類單頁應用前端
- 微前端(qiankun)主應用共享React元件前端React元件
- 技術雷達之「微前端」- 將微服務理念擴充套件到前端開發 | 《前端的逆襲》知乎專欄前端微服務套件
- 微前端實踐前端
- 微前端如何落地?前端
- 微前端microApp前端APP
- 微前端架構前端架構
- 微前端方案前端
- 基於 React & TypeScript & Webpack 的微前端應用模板ReactTypeScriptWeb前端
- 「譯」用 Blazor WebAssembly 實現微前端BlazorWeb前端
- 微前端調研及簡析SPA實現原理前端
- 前端技術演進(四):前端三層結構與應用前端
- 也聊聊微前端(上)前端
- 淺析微前端沙箱前端
- SpEL應用實戰|得物技術
- 微前端中實現沙箱環境的方案調研前端
- 微核心架構在大型前端系統中的應用架構前端
- 微前端框架single-spa子應用載入解析前端框架
- Midway Serverless 釋出 2.0,一體化讓前端研發再次提效Server前端
- 微前端 – 將微服務理念延伸到前端開發中前端微服務
- Serverless 應用引擎 SAE 助力袋拉拉研發提效 70%Server
- 微前端 Micro-Frontends - 概念前端
- qiankun微前端實踐前端
- 微前端說明以及使用前端
- 微前端專案部署方案前端
- 淺談qiankun微前端前端
- 企業物聯網技術應用推進規劃
- 得物前端喚端業務場景和技術精講前端
- 前端技術演進(七):前端跨棧技術前端
- 熱點微前端Microfrontend的討論:谷歌AdWords是真實的微前端前端谷歌
- 螞蟻金服微貸前端技術團隊 緊急招聘公告前端
- 純前端表格技術應用研討會——華為供應鏈專場前端
- 使用 Angular 打造微前端架構的 ToB 企業級應用Angular前端架構
- 為什麼微前端開始在流行——Web 應用的聚合前端Web