摘要:我們繼續沿用了原來就有的,借此把融入整個微前端框架,而已經改造的則不需要我們的開發團隊,分框架組和各個業務組。項目該項目是整個微前端項目的入口。本坑實踐它很大的理由也是用自己的方法初探微前端實踐方法的可行性。
在寫這篇文章的一個多月前,本坑還不知道微前端是什么,大概從字面上的含義是比較小的前端項目。
本坑開始實踐它,是由于工作要求。改造一個運行多年,前端用jsp寫的服務平臺項目(以下簡稱該平臺)。改造它是改造它的前端架構。改造它的原因是比較多人反饋,其頁面加載和渲染顯得吃力,頁面切換后首屏等待時間長的問題,交互體驗舒適度不可避免的下降了,特別是在老式電腦面前。
該平臺業務比較多,所以組長希望前端框架組能把平臺中的前端部分分離出來,最好用當下滿大街的Vue、能夠按照各個一級菜單分成若干前端子項目,用戶訪問依然是整體的項目,同時這一改造實施過程不需要重做一個、而是整個500多個頁面從局部開始、是逐步、兼容的,新舊同時運行,直至整體被替換。(ps:不重做?這......科學嗎?)
其實大概知道慢在哪里,但是不知道究竟慢在具體哪個部分。和其他一以貫之的類似管理平臺布局并無不同。左邊導航欄,上面頂欄,右側內容欄,整體頁面是一個index.jsp。上面提到的內容欄是一個iframe,里面通過切換src來切換頁面。更多的業務造就更多頁面,更多頁面帶來更多的加載。加上長時間沒有做好資源加載的管理,導致渲染一次頁面需要加載大量js,css或者多次加載同一個文件的情況。該平臺大量的配置頁面生成,是通過easyui的來做的。通過數據來創造整個頁面dom節點,也拖累了內容完整呈現的時間。
我們使用谷歌瀏覽器performance可以最終追查到這個系統在哪些方面,哪個方法存在著哪些延遲。結論是:
1、混亂的項目資源管理導致大量的資源請求。
2、easyui和項目中不少的dom操作帶來大量的重排和重繪。
3、埋點,插件使用不當以及其他。
它是什么呢?
微前端的概念來自于之前流行的微服務。它的來源很大程度是來自于這篇文章 。微服務系統使得后臺服務架構能夠比較好地規避越來越臃腫的體積帶來的性能下降。根據業務合理拆分成一個個的服務,盡量避免一個子服務影響整個項目運行的優勢,有效的進行隔離。
那么,前端也有同樣的需求嗎?答案是肯定的。
今天,日益更新的前端技術,已經能夠把一個個頁面各個小元素打包成組件庫,功能包,在多個項目中引入使用。此外,我們不用再使用難受的iframe來聚合不同的項目,而是導出一個個web component,只需要import 到頁面就可以使用。把一個個子項目打包成一個個web component,聚合在入口項目之內。這也許就是微前端現在比較時髦的樣子。
如果一個大項目有以下特點,微前端可以在這些項目中運用:
1、大項目有統一的入口,子項目頁面需要無刷新下切換,可是各個子項目在業務上和開發團隊上是不同的。
2、項目過大,打包、運行、部署效率出現顯著下降的問題。這時希望能根據業務拆分打包,部署。
回到本文開頭,一開始面對這樣的需求還是有些想辭職的沖動,因為覺得需求有點不是符合實際,實際上要實施改版也是需要過程的。
不過靜下來想想,搜搜,翻了翻當前項目的前端結構,隱隱約約似乎浮現一些需求可行性的線索。
因為項目的最終目的是把整個jsp頁面改成vue來寫。而這一要求是逐步替換的過程,所以在改造過程中,同時要保證項目兼容jsp的頁面。我們繼續沿用了原來就有的iframe,借此把jsp融入整個微前端框架,而已經改造的micro則不需要iframe.
我們的開發團隊,分框架組和各個業務組。其中每個業務組有3到8個人,他們大多數是后端背景,主要做的也是后端開發??蚣芙M有前端和后端。為了應付龐大的業務開發需求。大部分后端人員都需要使用jsp,js等前端技術進行開發。
框架組為了減少他們的前端開發門檻,前端框架組會封裝好easyui組件,提供業務組使用。所以,正如前文提到,后端人員是通過數據,結合框架組提供的組件來完成頁面的開發的。從某種角度來說,數據配置的頁面對接下來的改造工作有一定的幫助,因為大部分頁面可以同時改寫。
我們對整個項目進行了大致的分類。
1、portal 項目:該項目是整個微前端項目的入口。里面含有loader,用以加載各個項目模塊。它也嵌入到子項目中,使得多帶帶運行子項目和portal項目一樣的界面要求。
2、permission 項目:該項目包含菜單組件,登錄頁面,頂欄組件,權限控制等。在任何環境下,它都必須首先加載,為子項目模塊掛載提供錨點。
3、common項目:該項目包含公共業務組件。比如封裝好的頁面,可以直接給不太能夠掌握vue項目的后端人員更加友好的去使用。
4、業務項目:就是指業務組各個模塊開發的前端項目。什么樣的業務分為一個項目,這點由產品和技術人員一起來決定。相對于portal項目,業務項目相當于它的子項目。
前端框架組必須提供一套統一的業務項目的前端模板,可以在確認新建的子項目后迅速的加入到整個項目中,進行開發和部署,而這一過程不能影響其他項目的部署和運行。
除了上述方案浮出水面,還會在改造過程中遇到一個個細節問題。 不過在大方向,框架組成上,前端結構上做好了,細節問題也會隨耐心和時間被解決。
本坑根據以上的分類,大致進行說明其實現。這其中結合了不少前輩之經驗,在文章結尾處鳴謝。
portal 項目是整個項目部署的入口,它的核心來自于single-spa
在整個項目結構中它將集成到每一個子項目。集成的方式很粗暴簡單,就是外聯加載。
portal負責根據不同的環境來對應的組件和app,同時也安裝各個app,卸載各個app等,它負責app在single-spa的生命周期。比如集成模式下根據環境和路由加載對應的app,而在子項目運行時只加載公共組件和不同業務的app。
那么protal是如何加載的呢?
protal維護了一個json里面包含了各個子項目的index.html的信息,通過匹配index.html里面的src 、link,加載各項資源。
module.exports = { common: { webName:"common", globalVarName: "mfe:common", componentsTarget: "/common/release/components/web.html", resourcePatterns: ["/components.[0-9a-z]{8}.js/g"], loadType:"before" }, permission: { webName:"permission", globalVarName: "mfe:permission", // URL 匹配模式 matchUrlHash: "", // 微前端地址 componentsTarget: "/permission/release/components/web.html", webTarget:"/permission/release/web/web.html", // 資源匹配模式 resourcePatterns: ["/common.[0-9a-z]{8}.css/g","/store.[0-9a-z]{8}.js/g", "/publicPath.[0-9a-z]{8}.js/g","/singleSpaEntry.[0-9a-z]{8}.js/g","/components.[0-9a-z]{8}.js/g"], //是否要在項目啟動前加載,before為提前加載,after為hash變化后加載 loadType:"before" }, app4vue:{ webName:"repair-order", globalVarName: "mfe:app4vue", matchUrlHash: "/layout/repair-order", webTarget: "/app4/release/web/web.html", resourcePatterns: ["/common.[0-9a-z]{8}.css/g","/store.[0-9a-z]{8}.js/g", "/singleSpaEntry.[0-9a-z]{8}.js/g"], loadType:"after" } }
async gatherResource () { const self = this // const spaEntry = "portal" const web = self._webName //如果是微前端聚合模式 if (window._IS_SIGLE_PORTAL) { if (web !== "mfe-permission") { await self.loadComponents(micros.common) await self.loadApp(micros.permission) } } else { if (web === "mfe-permission") { await self.loadComponents(micros.common) } else { if (web !== "mfe-common") { await self.loadComponents(micros.common) } await self.loadApp(micros.permission) } } // return new Promise(resolve => resolve("loader:all Finish!")) }
permission負責登錄頁,layout中的菜單欄,頂欄。所有的子項目app都必須掛載到permission項目中的顯示區塊里。也就是說permssion會提供錨點給子項目掛載。
所以permission負責路由的控制,這里的路由有總體路由和app內路由切換。如果app切換的路由控制涉及到singer-spa,app的切換會觸發single-spa:routing-event事件,portal監聽該事件 unmounted和mounted app,如果app內部的路由切換,需要觸發app內部路由切換。
本坑嘗試監聽permission的 hash,由于vue新版本,hash實際是監聽不了的,所以監聽hash是沒辦法的。
這看上去會干涉到子項目的代碼,如果哪位大神有好方法可以在評論區貼上你的看法。
業務項目的獨立運行只會發生在開發模式之下,在生產或者測試環境并不會獨立運行。
集成環境下輸出成三個周期,提供給single-spa
export var global = {}; export const bootstrap = () => { return Promise.resolve(); } export function mount (props) { Vue.mixin({ data: function () { return { props } } }) return Promise.resolve().then(() => { createDomElement(); global.instance = new Vue({ el: "#app4", router, render: h => h(App) }) }) } export function unmount () { return Promise.resolve().then(() => { global.instance.$destroy(); global.instance.$el.innerHTML = ""; delete global.instance }) } function createDomElement () { // Make sure there is a div for us to render into let node = document.getElementById("main-content"); let el = document.getElementById("app4"); if (!el) { el = document.createElement("div"); el.id = "app4"; node.appendChild(el); } return el; }
開發模式獨立運行
const init = async () => { //啟動single-spa const loader = new Loader(process.env) await loader.startSingleSpa() Vue.mixin({ data () { return { loader } } }) Vue.config.productionTip = false; //permission渲染后再掛載自己上去 window.addEventListener("single-spa:main-content-mount", evt => { if (!window.vim) { window.vm = new Vue({ el: loader.createHookEle("app4"),//掛載自己 // store, router, render: h => h(App) }) } }) } init()
common 類似插件的打包,不贅述。
import "./styles/vars.scss" import MButton from "./components/button" const components = [MButton]; const install = function (Vue) { if (install.installed) return; components.map(component => { Vue.use(component); }); }; // 全局引用可自動安裝 if (typeof window !== "undefined" && window.Vue) { install(window.Vue); } export default { install, MButton }
幾個主要源碼采用外聯形式
single-spa是怎么聚合各個獨立的vue app的呢?本坑嘗試理解它
Single-spa 把app聚合成三個周期 bootstrap mounted unmounted,這三個周期是需要自己去配置改寫的,其實single-spa還有其他周期,不需要改寫。
也就是對single-spa來說app只有這三個東西需要特別的關心,app的卸載和加載。其他都是app自己的事。mounted、unmonuted和vue app 獨立運行的mounted和destroy本質上沒有區別,只是single-spa做了一層代理。代理完成app的掛載和銷毀。
single-spa內部也保存了一個數組,負責維護內部注冊的app.注冊完后代理完成app的掛載。卸載后銷毀之。
如果親愛的客官,你也遇到這種問題,用這種改法是沒有保證的。
本坑實踐它很大的理由也是用自己的方法初探微前端實踐方法的可行性。
這種大跨度的改變會帶來不少不可預測的底層沖突,前后端的沖突。
第二呢,這種大跨度的改變幾乎等同于重構。
第三呢,微前端方案也有自身的局限性,比如對庫版本的管理,app樣式的隔離沒有做到很好等等。還是要根據實際來衡量。
針對太過古老的系統比如jsp,可以先嘗試把jsp轉為html,在前端性能上多改進,再另行考慮綜合性改版。
估計很多地方搞的不好,代碼或者信息錯誤,歡迎各位指導。
single-spa官網
微前端實踐
前端微服務化解決方案
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/54165.html
摘要:為了幫助用戶更好地完成消費決策閉環,馬蜂窩上線了大交通業務?,F在,用戶在馬蜂窩也可以完成購買機票火車票等操作。第二階段架構轉變及服務化初探從年開始,整個大交通業務開始從架構向服務化演變。 交通方式是用戶旅行前要考慮的核心要素之一。為了幫助用戶更好地完成消費決策閉環,馬蜂窩上線了大交通業務?,F在,用戶在馬蜂窩也可以完成購買機票、火車票等操作。 與大多數業務系統相同,我們一樣經歷著從無到有...
閱讀 3076·2023-04-25 20:43
閱讀 1719·2021-09-30 09:54
閱讀 1590·2021-09-24 09:47
閱讀 2874·2021-09-06 15:02
閱讀 3510·2021-02-22 17:09
閱讀 1233·2019-08-30 15:53
閱讀 1441·2019-08-29 17:04
閱讀 1956·2019-08-28 18:22