bootstrap跟vue冲突吗_知道微服务,但你知道微前端吗?
在 toB 的前端開發(fā)工作中,我們往往就會(huì)遇到如下困境:
- 工程越來(lái)越大,打包越來(lái)越慢
- 團(tuán)隊(duì)人員多,產(chǎn)品功能復(fù)雜,代碼沖突頻繁、影響面大
- 內(nèi)心想做 SaaS 產(chǎn)品,但客戶總是要做定制化
不同的團(tuán)隊(duì)可能有不同的方法去解決這些問(wèn)題。在前端開發(fā)日新月異、前端工程化蓬勃發(fā)展的今天,我想給大家介紹下另一種嘗試——微前端。
微前端是什么?
那什么是微前端?微前端主要是借鑒后端微服務(wù)的概念。簡(jiǎn)單地說(shuō),就是將一個(gè)巨無(wú)霸(Monolith)的前端工程拆分成一個(gè)一個(gè)的小工程。別小看這些小工程,它們也是“麻雀雖小,五臟俱全”,完全具備獨(dú)立的開發(fā)、運(yùn)行能力。整個(gè)系統(tǒng)就將由這些小工程協(xié)同合作,實(shí)現(xiàn)所有頁(yè)面的展示與交互。
可以跟微服務(wù)這么對(duì)比著去理解:
微服務(wù) 微前端 一個(gè)微服務(wù)就是由一組接口構(gòu)成,接口地址一般是 URL。當(dāng)微服務(wù)收到一個(gè)接口的請(qǐng)求時(shí),會(huì)進(jìn)行路由找到相應(yīng)的邏輯,輸出響應(yīng)內(nèi)容。 一個(gè)微前端則是由一組頁(yè)面構(gòu)成,頁(yè)面地址也是 URL。當(dāng)微前端收到一個(gè)頁(yè)面 URL 的請(qǐng)求時(shí),會(huì)進(jìn)行路由找到相應(yīng)的組件,渲染頁(yè)面內(nèi)容。 后端微服務(wù)會(huì)有一個(gè)網(wǎng)關(guān),作為單一入口接收所有的客戶端接口請(qǐng)求,根據(jù)接口 URL 與服務(wù)的匹配關(guān)系,路由到對(duì)應(yīng)的服務(wù)。 微前端則會(huì)有一個(gè)加載器,作為單一入口接收所有頁(yè)面 URL 的訪問(wèn),根據(jù)頁(yè)面 URL 與微前端的匹配關(guān)系,選擇加載對(duì)應(yīng)的微前端,由該微前端進(jìn)行進(jìn)行路由響應(yīng) URL。 這里要注意跟 iframe 實(shí)現(xiàn)頁(yè)面嵌入機(jī)制的區(qū)別。微前端沒有用到 iframe,它很純粹地利用 JavaScript、MVVM 等技術(shù)來(lái)實(shí)現(xiàn)頁(yè)面加載。后面我們將介紹相關(guān)的技術(shù)實(shí)現(xiàn)。
為什么要用微前端
在介紹具體的改造方式之前,我想跟大家先說(shuō)明下我們當(dāng)時(shí)面臨的問(wèn)題,以及改造后的對(duì)比,以便大家以此為對(duì)照,評(píng)判或決定使用。主要包括打包速度、頁(yè)面加載速度、多人多地協(xié)作、SaaS 產(chǎn)品定制化、產(chǎn)品拆分這幾個(gè)角度。
首先是打包速度。在 6 個(gè)月前,我們的 B 端工程那會(huì)兒還是一個(gè) Monolith。當(dāng)時(shí)已經(jīng)有 20 多個(gè)依賴、60 多個(gè)公共組件、200 多個(gè)頁(yè)面,對(duì)接 700 多個(gè)接口。我們使用了 Webpack 2,并啟用 DLL Plugin、HappyPack 4。在我的個(gè)人主機(jī)上使用 4 線程編譯,大概要 5 分鐘。而如果不拆分,算下來(lái)現(xiàn)在我們已經(jīng)有近 400 個(gè)頁(yè)面,對(duì)接1000 多個(gè)接口。
這個(gè)時(shí)間意味著什么?它不僅會(huì)耽誤我們開發(fā)人員的時(shí)間,還會(huì)影響整個(gè)團(tuán)隊(duì)的效率。上線時(shí),在 Docker、CI 等環(huán)境下,耗時(shí)還會(huì)被延長(zhǎng)。如果部署后出幾個(gè) Bug,要線上立即修復(fù),那就不知道要熬到幾點(diǎn)了。
在使用微前端改造后,目前我們已經(jīng)有 26 個(gè)微前端工程,平均打包時(shí)間在 30-45 秒之間(注意,這里還沒有應(yīng)用 DLL + HappyPack)。
頁(yè)面加載速度其實(shí)影響到并不是很大,因?yàn)榻?jīng)過(guò) CDN、gzip 后,資源的大小還能接受。這里只是給大家看一些直觀的數(shù)據(jù)變化。6 個(gè)月前,打包生成的 app.js 有 5MB(gzip 后 1MB),vendor.js 有 2MB(gzip 后 700KB),app.css 有 1.5MB(gzip 后 250KB)。這樣首屏大概要傳輸 2MB 的內(nèi)容。拆分后,目前首屏只需要傳輸 800KB 左右。
在協(xié)作上,我們?cè)谌珖?guó)有三個(gè)地方的前端團(tuán)隊(duì),這么多人在同一個(gè)工程里開發(fā),遭遇代碼沖突的概率會(huì)很頻繁,而且沖突的影響面比較大。如果代碼中出現(xiàn)問(wèn)題,導(dǎo)致 CI 失敗,所有其他人的代碼提交與更新也都會(huì)被阻塞。使用微前端后,這樣的風(fēng)險(xiǎn)就平攤到各個(gè)工程上去了。
再者就是定制化了。我們做的額是一款 toB 的產(chǎn)品,做成 SaaS 標(biāo)準(zhǔn)版產(chǎn)品大概是所有從業(yè)者的愿望。但整體市場(chǎng)環(huán)境與產(chǎn)品功能所限,經(jīng)常要面臨一些客戶要求做本地化與定制化的要求。本地化就會(huì)有代碼安全方面的考量,最好是不給客戶源代碼,最差則是只給客戶購(gòu)買功能的源代碼。而定制化從易到難則可以分為獨(dú)立新模塊、改造現(xiàn)有模塊、替換現(xiàn)有模塊。
通過(guò)微前端技術(shù),我們可以很容易達(dá)到本地化代碼安全的下限——只給客戶他所購(gòu)買的模塊的前端源碼。定制化里最簡(jiǎn)單的獨(dú)立新模塊也變得簡(jiǎn)單:交付團(tuán)隊(duì)增加一個(gè)新的微前端工程即可,不需要揉進(jìn)現(xiàn)有研發(fā)工程中,不占用研發(fā)團(tuán)隊(duì)資源。而定制化中的改造現(xiàn)有模塊也可以比較好地實(shí)現(xiàn):比如說(shuō)某個(gè)標(biāo)準(zhǔn)版的頁(yè)面中需要增加一個(gè)面板,則可以通過(guò)一個(gè)新的微前端工程,同樣響應(yīng)該頁(yè)面的 URL(當(dāng)然要控制好順序),在頁(yè)面的恰當(dāng)位置插入一個(gè)新的 DOM 節(jié)點(diǎn)即可。
最后就是產(chǎn)品拆分方面的考量了。我們的產(chǎn)品比較大,有幾塊功能比較獨(dú)立、有特色。如果說(shuō)將來(lái)需要獨(dú)立成一個(gè)子產(chǎn)品,有微前端拆分作為鋪墊,騰挪組合也會(huì)變得更加容易些。
其他目標(biāo)
有了以上的一些原因與訴求,在決定進(jìn)行微前端改造前,還需要設(shè)定一些額外的小目標(biāo):
- 不能對(duì)現(xiàn)有的前端開發(fā)方式帶來(lái)太大變化,至少要有平滑過(guò)渡的機(jī)制。
- 每個(gè)為前端工程都要求可以獨(dú)立運(yùn)行,至少在本地開發(fā)時(shí)要能做到。
- 微前端在加載時(shí),要實(shí)現(xiàn)預(yù)加載,并可以自由調(diào)整預(yù)加載順序,甚至是根據(jù)用戶的偏好來(lái)實(shí)現(xiàn)智能化、個(gè)性化的加載順序。
如何改造現(xiàn)有工程?
“Talk is cheap,show me the code“。下面就讓我們一起來(lái)看看具體的改造吧!我們的微前端工程可以劃分為 portal 工程、業(yè)務(wù)工程、common 工程這幾類。
portal 工程
portal,顧名思義,就是入口。這也就是上面所說(shuō)的微前端加載器。當(dāng)用戶打開瀏覽器,首次進(jìn)入我們的頁(yè)面時(shí),不管是什么 URL,首先加載的就是 portal。portal 里會(huì)配置所有業(yè)務(wù)工程的地址、匹配哪些 URL、需要加載哪些資源。如:
// 業(yè)務(wù)工程的名稱customer: { // URL 匹配模式 matchUrlHash: ['^/customer'], // 微前端地址 target: 'http://localhost:8101/mfe-customer/index.html', // 資源匹配模式 resourcePatterns: ['/app.*.css$', '/vendor.*.css$', '/manifest.*.js$', '/vendor.*.js$', '/app.*.js$'],}portal 會(huì)定時(shí)、異步、并發(fā)地下載業(yè)務(wù)工程的資源,并將它們進(jìn)行注冊(cè),此時(shí)并不會(huì)加載這些業(yè)務(wù)工程。這里之所以要業(yè)務(wù)工程的地址(target)、資源(resourcePatterns),是為了加載時(shí)確定地知道其所包含的 app.js、vendor.js、app.css 等資源的路徑。因?yàn)闃I(yè)務(wù)工程每次有變更,app.js 等資源路徑上都會(huì)帶有新的文件內(nèi)容哈希值(Hash),導(dǎo)致路徑不可預(yù)測(cè)。而它的 index.html 的路徑是固定的。我們讀取該 HTML,解析其內(nèi)容,通過(guò)正則就能匹配到 app.js 等資源的路徑。
portal 在運(yùn)行時(shí),會(huì)監(jiān)聽 URL 變化。目前我們只支持 URL Hash(如 #/customer)。當(dāng) Hash 發(fā)生變更時(shí),匹配到業(yè)務(wù)工程,然后執(zhí)行卸載、加載的工作。這個(gè)機(jī)制主要是利用 single-spa 來(lái)實(shí)現(xiàn),但原理就是這么簡(jiǎn)單。
import { registerApplication } from 'single-spa';registerApplication('customer', // 下載微前端工程,獲取三個(gè)函數(shù)鉤子:bootstrap、mount、unmount () => { const html = fetch(mfeConfig.target); const {cssUrls, jsUrls} = match(html, mfeConfig.resourcePatterns); loadCss(cssUrls); loadJs(jsUrls); return windows['mfe:customer']; }, // 對(duì)當(dāng)前瀏覽器 URL Hash 進(jìn)行匹配,如果匹配(返回 true),則加載該微前端(調(diào)用 mount);否則卸載(調(diào)用 unmount) () => { return match(window.location.hash, mfeConfig.matchUrlHash); }, mfeConfig.customProps);業(yè)務(wù)工程
業(yè)務(wù)工程就是普通的微前端工程,一般一個(gè)模塊一個(gè)工程。業(yè)務(wù)工程要扮演兩個(gè)角色,一個(gè)是可獨(dú)立運(yùn)行的前端工程,一個(gè)是受 portal 控制的運(yùn)行時(shí)。前者主要用于我們本地開發(fā),后者則是線上集成時(shí)使用。在獨(dú)立運(yùn)行時(shí),它跟原來(lái)的前端工程沒有什么區(qū)別。以 Vue 工程為例,照樣使用 new Vue({el: '#app'}) 來(lái)啟動(dòng)、渲染頁(yè)面。
new Vue({ el: '#app', i18n, router, store, template: '', components: { App }});而當(dāng)受控運(yùn)行時(shí),則是利用 UMD 方式輸出幾個(gè)鉤子函數(shù),包括初始化、加載、卸載。
if(!window.IS_IN_MFE){ // 獨(dú)立運(yùn)行時(shí) new Vue({...})} else { // 受控運(yùn)行時(shí) module.exports = { bootstrap(){ // 注冊(cè)時(shí)執(zhí)行 }, mount(customProps){ // 加載時(shí)執(zhí)行 return Promise.resolve().then(()=>{ instance = new Vue({...}) }) }, unmount(){ // 卸載時(shí)執(zhí)行 return Promise.resolve().then(()=>{ instance.$destroy() }) } }}線上環(huán)境的 Webpack 配置:
output: { libraryTarget: "umd
總結(jié)
以上是生活随笔為你收集整理的bootstrap跟vue冲突吗_知道微服务,但你知道微前端吗?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 微信网名女唯美的
- 下一篇: 见机行事的下一句是什么呢?