微前端是什么嵌言?
參考網(wǎng)站:
https://micro-frontends.org
https://microfrontends.com
微前端就是與多個(gè)可以獨(dú)立發(fā)布功能的團(tuán)隊(duì)一起構(gòu)建現(xiàn)代化web應(yīng)用程序的技術(shù)蝠检、策略和方法怖亭,將大而可怕的事物分割成更小涎显、更易于管理的部分,然后明確它們之間的依賴關(guān)系兴猩。我們的技術(shù)選擇期吓,我們的代碼庫(kù),我們的團(tuán)隊(duì)倾芝,以及我們的發(fā)布過(guò)程都應(yīng)該能夠相互獨(dú)立地操作和進(jìn)化讨勤,而不需要過(guò)度的協(xié)調(diào)。微前端架構(gòu)是一種類似于微服務(wù)的架構(gòu)晨另,它將微服務(wù)的理念應(yīng)用于瀏覽器端潭千,即將 Web 應(yīng)用由單一的單體應(yīng)用轉(zhuǎn)變?yōu)槎鄠€(gè)小型前端應(yīng)用聚合為一的應(yīng)用。
為什么要用微前端借尿?
拆分巨型應(yīng)用刨晴,使應(yīng)用方便迭代更新
兼容歷史應(yīng)用,實(shí)現(xiàn)增量開(kāi)發(fā)
特點(diǎn):
獨(dú)立部署
增量遷移
團(tuán)隊(duì)自治
松耦合代碼
優(yōu)點(diǎn):
通過(guò)路由進(jìn)行跨應(yīng)用程序通信
解決了大型項(xiàng)目如何迭代的問(wèn)題
解決了多團(tuán)隊(duì)技術(shù)棧不同的問(wèn)題路翻,實(shí)現(xiàn)react和Vue等框架整合
缺點(diǎn):
有效載荷大小
環(huán)境差異配置難
業(yè)務(wù)和治理復(fù)雜
微前端結(jié)構(gòu)方案
自由組織模式
沒(méi)有特別形勢(shì)狈癞,類似iframe嵌套、npm包自由發(fā)揮
基座模型
類似微服務(wù)的注冊(cè)中心模式茂契,有個(gè)基座蝶桶,其他應(yīng)用都往里加
去中心模式
webpack5模塊聯(lián)邦,多個(gè)應(yīng)用可以互相嵌套掉冶,可以深入到組件導(dǎo)入導(dǎo)出
主流微前端框架
國(guó)內(nèi)使用基座模式偏多
Single-Spa:最早的微前端框架真竖,兼容多種前端技術(shù)棧脐雪。
Qiankun:基于Single-Spa,阿里系開(kāi)源微前端框架恢共。
微前端-乾坤
本文將按照下面的順序逐一講解:
1)什么是微前端以及為什么使用微前端
2)乾坤框架介紹
3)基于乾坤框架實(shí)例
4)開(kāi)發(fā)過(guò)程中遇到的問(wèn)題
一战秋、什么是微前端
微前端的核心思想就是將按照不同功能或不同維度拆分的獨(dú)立子應(yīng)用,通過(guò)主應(yīng)用來(lái)加載這些子應(yīng)用旁振,達(dá)到子項(xiàng)目可以獨(dú)立開(kāi)發(fā)获询、獨(dú)立部署涨岁、不受技術(shù)棧影響效果拐袜。
二、乾坤框架介紹
下面的地址是乾坤文檔地址梢薪,詳細(xì)介紹了微前端的概念蹬铺、乾坤的核心設(shè)計(jì)思想
介紹
https://qiankun.umijs.org/zh/guide
三、乾坤框架實(shí)例
說(shuō)明:例子基于三個(gè)獨(dú)立項(xiàng)目秉撇,項(xiàng)目都采用了vue框架
1甜攀、創(chuàng)建項(xiàng)目
由于是基于項(xiàng)目改造,所以項(xiàng)目已經(jīng)創(chuàng)建好了琐馆,這里就不贅述創(chuàng)建項(xiàng)目的過(guò)程了
2规阀、主應(yīng)用配置
主應(yīng)用不限技術(shù)棧,只需要提供一個(gè)容器 DOM瘦麸,然后注冊(cè)微應(yīng)用并 start 即可
1)主應(yīng)用安裝乾坤包
npm i qiankun -S
2)修改App.vue文件谁撼,添加微應(yīng)用掛載節(jié)點(diǎn)
3)注冊(cè)微應(yīng)用,并啟動(dòng)
首先滋饲,配置一些全局變量厉碟,用于區(qū)分開(kāi)發(fā)環(huán)境、測(cè)試環(huán)境屠缭、生產(chǎn)環(huán)境的微應(yīng)用的入口
VUE_APP_UPKEEP_STATIC_HOST是微應(yīng)用A的域名箍鼓;VUE_APP_SUB_APP_UPKEEP_PATH是微應(yīng)用A的路徑;
VUE_APP_BRAND_STATIC_HOST是微應(yīng)用B的域名呵曹;VUE_APP_SUB_APP_BRAND_PATH是微應(yīng)用B的路徑款咖。
這里之所以將資源加載的路徑寫(xiě)的這么具體,是因?yàn)槲覀兊捻?xiàng)目都是部署在同一臺(tái)機(jī)器上奄喂,只不過(guò)是訪問(wèn)路徑有些區(qū)別铐殃,為了防止微應(yīng)用資源加載錯(cuò)誤,所以才寫(xiě)的那么具體砍聊。
開(kāi)發(fā)環(huán)境配置的一些變量:
測(cè)試環(huán)境配置的一些變量:
生產(chǎn)環(huán)境配置的一些變量:
由于生產(chǎn)環(huán)境會(huì)區(qū)分灰度環(huán)境背稼,所以通過(guò)變量process.env.IS_GRAY來(lái)區(qū)分路徑。
然后玻蝌,配置微應(yīng)用的注冊(cè)信息蟹肘、將微應(yīng)用的注冊(cè)和啟動(dòng)封裝成模塊
將微應(yīng)用的注冊(cè)信息封裝成模塊词疼,micros/subApps.js
引進(jìn)微應(yīng)用注冊(cè)信息,將微應(yīng)用的注冊(cè)和啟動(dòng)封裝成模塊帘腹,micros/index.js
接著贰盗,修改入口文件index.js
注意:一定要先掛載主應(yīng)用之后,才能注冊(cè)和啟動(dòng)微應(yīng)用阳欲,否則不會(huì)觸發(fā)微應(yīng)用的加載舵盈,即先執(zhí)行了mountApp之后,在執(zhí)行startMicroApp
ThirdMicroPush和ThirdMicroReplace方法用于微應(yīng)用間跳轉(zhuǎn)球化,實(shí)質(zhì)是調(diào)用了history.pushState和history.replaceState方法秽晚,改變?yōu)g覽器的地址,如果改變后的地址匹配上之前的激活規(guī)則activeRule的值筒愚,則激活相應(yīng)的微應(yīng)用赴蝇,示例如下:
ThirdMicroReplace('/umc-mall/upkeep/index.html#/home?processNo=tuhu')
4)在webpack.dev.js的devServer屬性添加跨域的配置
由于主應(yīng)用和微應(yīng)用間的端口號(hào)不一樣,會(huì)存在跨域問(wèn)題巢掺,所以需要配置可跨域信息
至此句伶,主應(yīng)用的配置已經(jīng)修改完成,下面說(shuō)下微應(yīng)用的改造陆淀,微應(yīng)用不需要額外安裝任何其他依賴即可接入 qiankun 主應(yīng)用考余。
3、微應(yīng)用A改造
微應(yīng)用A的入口文件導(dǎo)出相應(yīng)的生命周期鉤子
微應(yīng)用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js轧苫,這里是根目錄下的src/modules/index.js) 導(dǎo)出 bootstrap楚堤、mount、unmount 三個(gè)生命周期鉤子浸剩,以供主應(yīng)用在適當(dāng)?shù)臅r(shí)機(jī)調(diào)用钾军,同時(shí)兼容微應(yīng)用獨(dú)立運(yùn)行
修改的代碼在下面兩張圖內(nèi):
配置微應(yīng)用A的打包信息
修改webpack.conf.js文件的output配置
如果項(xiàng)目中有用dll 打包輸出一些資源,則需要對(duì)dll的配置信息進(jìn)行修改绢要,并且重新打包吏恭,如果沒(méi)有的話,至此子應(yīng)用的配置就已經(jīng)修改完成了重罪。
由于我們的項(xiàng)目有用到dll打包輸出的資源樱哼,所以還需要修改webpack.dll.js
在output對(duì)象上添加屬性libraryTarget: window
然后需要重新打包dll
注意,確保項(xiàng)目中引進(jìn)的是新打的dll文件剿配,否則會(huì)出現(xiàn)一些異常問(wèn)題
其他的微應(yīng)用配置同上搅幅。至此,主應(yīng)用和微應(yīng)用的改造已經(jīng)完成呼胚,不出問(wèn)題的話茄唐,項(xiàng)目就可以跑起來(lái)了
四、開(kāi)發(fā)過(guò)程遇到的問(wèn)題
1、Cannot read property 'range' of null
在src/modules/views/home/index.vue文件中的methods對(duì)象下有如下方法沪编,將該方法注釋掉呼盆,控制臺(tái)就不報(bào)錯(cuò)了
按照webpack官方文檔描述,是支持動(dòng)態(tài)路徑引進(jìn)相應(yīng)文件的蚁廓,但是這里卻報(bào)錯(cuò)了访圃,而別的項(xiàng)目也有使用動(dòng)態(tài)引進(jìn)文件,卻沒(méi)有報(bào)錯(cuò)相嵌⊥仁保看下控制臺(tái)的報(bào)錯(cuò)信息,是從eslint-loader拋出來(lái)的饭宾,于是對(duì)比了一下關(guān)于eslint包的版本批糟,發(fā)現(xiàn)正常跑起來(lái)項(xiàng)目安裝的babel-eslint版本是7.2.3,而報(bào)錯(cuò)項(xiàng)目安裝的版本是10.0.3捏雌。于是將babel-eslint版本改成7.2.3跃赚,重啟項(xiàng)目就正常了。
原因:安裝babel-eslint版本不對(duì)性湿,導(dǎo)致解析動(dòng)態(tài)引進(jìn)文件語(yǔ)法錯(cuò)誤
解決方法:安裝7.2.3版本的babel-eslint
2、根目錄下的模板文件的ejs語(yǔ)法在瀏覽器下未被正常解析
效果如下:
原因:html-loader和html-webpack-plugin有沖突满败。如果用html-loader處理了.html類型文件肤频,會(huì)把html模板編譯成js模塊的字符串,html-webpack-plugin解析時(shí)候發(fā)現(xiàn)文件已經(jīng)被編譯了算墨,就會(huì)直接跳過(guò)對(duì)其編譯
圖1:
圖2:
解決方法:將圖2的配置去掉
3宵荒、Utils is not defined
場(chǎng)景:微應(yīng)用項(xiàng)目都是用統(tǒng)一的cli命令初始化項(xiàng)目的,統(tǒng)一會(huì)在window對(duì)象下掛載Utils屬性净嘀。在本地開(kāi)發(fā)過(guò)程能正常訪問(wèn)到Utils對(duì)象报咳,但是在線上環(huán)境,就拋Utils is not defined挖藏。
解決思路:之前微應(yīng)用項(xiàng)目沒(méi)有配置微應(yīng)用信息前暑刃,作為獨(dú)立H5項(xiàng)目,線上和本地都可以訪問(wèn)到window.Utils對(duì)象膜眠。在配置微應(yīng)用之后岩臣,本地可以訪問(wèn)到Utils對(duì)象,但是線上訪問(wèn)不到宵膨。應(yīng)該是和修改了webpack配置有關(guān)架谎。然后對(duì)比了一下修改webpack前后的配置信息,唯一的區(qū)別就是:重新打包了dll后辟躏,本地開(kāi)發(fā)時(shí)候引的是本地dll文件谷扣,即重新打的dll文件,而線上引的是cdn地址dll文件捎琐,問(wèn)題就出在這里会涎。
dll文件引進(jìn)如下:
開(kāi)發(fā)環(huán)境的dll引進(jìn)放在inc-footer-dev.html中
線上環(huán)境的dll引進(jìn)放在inc-footer.html中
根目錄下的模板文件index.html
原因:修改了dll打包配置涯曲,線上用的不是最新打包出來(lái)的dll文件
因?yàn)槲覀冃薷牧薲ll的配置文件的output內(nèi)容,即添加了libraryTarget: 'window'在塔,本地引用的是重新打包后的dll文件幻件,打包出來(lái)的全局變量是掛載在window上,肯定是沒(méi)有問(wèn)題蛔溃,而線上引用的是修改dll配置文件之前打包出來(lái)的文件绰沥,沒(méi)有配置libraryTarget,默認(rèn)是var聲明的變量
解決方法:線上環(huán)境引用的dll文件改成項(xiàng)目打包出來(lái)的dll文件贺待,修改inc-footer.html:
4徽曲、微應(yīng)用A的window.Utils被微應(yīng)用B的window.Utils覆蓋
場(chǎng)景:一起有三個(gè)項(xiàng)目:主應(yīng)用、微應(yīng)用A麸塞、微應(yīng)用B秃臣,微應(yīng)用A和微應(yīng)用B都是用同一個(gè)腳手架搭的項(xiàng)目,會(huì)默認(rèn)在全局window上添加變量Utils哪工。
問(wèn)題:現(xiàn)在從鏈接上直接跳到主應(yīng)用奥此,在主應(yīng)用上調(diào)接口判斷要激活哪個(gè)微應(yīng)用。假如主應(yīng)用調(diào)接口后判斷要激活微應(yīng)用A雁比,這時(shí)候Utils對(duì)象是微應(yīng)用A設(shè)置的稚虎;當(dāng)在微應(yīng)用A的某個(gè)頁(yè)面,點(diǎn)擊某個(gè)按鈕時(shí)候會(huì)激活微應(yīng)用B偎捎,跳轉(zhuǎn)到微應(yīng)用B的某個(gè)頁(yè)面蠢终,這時(shí)候Utils對(duì)象是微應(yīng)用B設(shè)置的;當(dāng)從B在回到A上時(shí)茴她,Utils對(duì)象還是B設(shè)置的值寻拂。
解決方法:將微應(yīng)用A和B的Utils對(duì)象重新命名,避免重名導(dǎo)致覆蓋想象
注意:當(dāng)調(diào)用start方法時(shí)丈牢,設(shè)置sandbox:false祭钉,即當(dāng)關(guān)閉沙箱功能時(shí)候,檢查在主應(yīng)用和微應(yīng)用在window對(duì)象上掛載的屬性是否有重名赡麦,避免重名想象
5朴皆、主應(yīng)用線上編譯部署時(shí)候,拋錯(cuò):BigInt is not defined
錯(cuò)誤信息如下:webpack-cli/bin/cli.js文件拋出來(lái)的錯(cuò)誤泛粹,然后想到項(xiàng)目用的是webpack5的版本遂铡,會(huì)不會(huì)是線上編譯的webpack版本不對(duì),然后將線上編譯的webpack 的版本打印出來(lái)晶姊,版本是5.55.0扒接,發(fā)現(xiàn)沒(méi)錯(cuò)。
接著想到webpack5版本對(duì)node版本有要求,Webpack 5 對(duì) Node.js 的版本要求至少是 10.13.0 (LTS)會(huì)不會(huì)是線上編譯的node版本不符合要求钾怔。接著打印出線上node版本碱呼,發(fā)現(xiàn)node版本是8.3.0。問(wèn)題就出在這了宗侦。
解決方法:在編譯腳本里面愚臀,指定node的版本:NODE_VERSION=10.15.3
6、接口請(qǐng)求時(shí)候矾利,網(wǎng)關(guān)報(bào)錯(cuò)
在我們的項(xiàng)目里面姑裂,會(huì)加載一個(gè)網(wǎng)關(guān)包,用于對(duì)請(qǐng)求數(shù)據(jù)的加密男旗。在線上訪問(wèn)主應(yīng)用時(shí)舶斧,主應(yīng)用調(diào)接口是正常的,但是主應(yīng)用激活微應(yīng)用后察皇,微應(yīng)用中調(diào)接口茴厉,報(bào)如下錯(cuò)誤:
問(wèn)題:請(qǐng)求頭中的字段:X-PA-SIGN-V和X-PA-SIGN-ALG的值不對(duì)
主應(yīng)用會(huì)去初始化網(wǎng)關(guān)信息,微應(yīng)用在乾坤容器環(huán)境內(nèi)的話不會(huì)重新初始化網(wǎng)關(guān)信息什荣,但是主應(yīng)用調(diào)接口時(shí)候請(qǐng)求頭的X-PA-SIGN=v3和X-PA-SIGN-ALG=1矾缓,接口請(qǐng)求正常,微應(yīng)用接口卻報(bào)值不對(duì)的問(wèn)題溃睹。
主應(yīng)用代碼如下:
微應(yīng)用代碼如下:
主應(yīng)用沒(méi)有報(bào)網(wǎng)關(guān)問(wèn)題而账,微應(yīng)用卻報(bào)了,兩者關(guān)于網(wǎng)關(guān)信息唯一不同的地方就是引進(jìn)的包名不一致因篇,于是將微應(yīng)用的網(wǎng)關(guān)包名改成和主應(yīng)用一致,接口就正常了
問(wèn)題原因:兩個(gè)項(xiàng)目的網(wǎng)關(guān)包不一樣
解決方法:項(xiàng)目的網(wǎng)關(guān)包改成一致就可以了
7笔横、運(yùn)行主項(xiàng)目時(shí)候竞滓,報(bào)錯(cuò):
index.1b2c201fea272d41a704.js:39190 Uncaught QiankunError: application 'umcmallupkeep' died in status NOT_MOUNTED: [qiankun]: Target container with #childContainer not existed after umcmallupkeep mounted!
大致意思是,用于掛載微項(xiàng)目的容器節(jié)點(diǎn)childContainer不存在吹缔。
先來(lái)看下子項(xiàng)目的注冊(cè)信息商佑,指定的容器節(jié)點(diǎn)名是否為childContainer:下圖中的container:'#childContainer',值沒(méi)有配置錯(cuò)誤
接著看下主應(yīng)用的掛載組件App.vue內(nèi)容:
微應(yīng)用掛載組件App.vue內(nèi)容:
法相主應(yīng)用和微應(yīng)用的掛載節(jié)點(diǎn)的id都是app厢塘, 在看下報(bào)錯(cuò)圖片渲染出來(lái)的DOM只有微應(yīng)用的DOM結(jié)構(gòu)茶没,主應(yīng)用的DOM節(jié)點(diǎn)都沒(méi)有發(fā)現(xiàn)。懷疑是掛載DOM節(jié)點(diǎn)的id命名沖突導(dǎo)致的晚碾。
將主應(yīng)用的掛載id由app改成main抓半,項(xiàng)目就不報(bào)錯(cuò)了。
正常顯示效果如下:
報(bào)錯(cuò)原因:主應(yīng)用和微應(yīng)用的DOM節(jié)點(diǎn)id命名沖突了
解決方法:修改成不一樣的就可以了
8格嘁、控制臺(tái)報(bào)跨域問(wèn)題 笛求,微應(yīng)用資源獲取失敗
Access to fetch at 'https://xxx.com.cn/vender/1.0.0/encrypt.min.js' from origin 'http://localhost:5910' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. Uncaught TypeError: application 'umcmallupkeep' died in status LOADING_SOURCE_CODE: Failed to fetch
報(bào)died in status LOADING_SOURCE_CODE: Failed to fetch錯(cuò)誤,一般是微應(yīng)用還沒(méi)有改造完成導(dǎo)致的。但是我們的項(xiàng)目已經(jīng)改造完成了探入,還是報(bào)該錯(cuò)誤狡孔。在看控制臺(tái)有一個(gè)跨域的錯(cuò)誤,會(huì)不會(huì)是該報(bào)錯(cuò)導(dǎo)致的呢蜂嗽?
于是將該文件的引用改成項(xiàng)目中的文件地址苗膝,果然就不報(bào)錯(cuò)了。
報(bào)錯(cuò)原因:當(dāng)主應(yīng)用發(fā)生跨域報(bào)錯(cuò)時(shí)植旧,會(huì)block調(diào)微應(yīng)用的加載和執(zhí)行
解決方法:解決掉跨域報(bào)錯(cuò)問(wèn)題
注意:如果測(cè)試環(huán)境有調(diào)生產(chǎn)環(huán)境資源辱揭,也會(huì)報(bào)跨域問(wèn)題,導(dǎo)致微應(yīng)用加載失敗