基于umijs
多模塊系統(tǒng)調(diào)試開發(fā)方案
背景
? 隨著前端開發(fā)過程工程化的深入率拒,前端SPA應(yīng)用已經(jīng)越來越多的在各種信息系統(tǒng)內(nèi)部采用,而且隨著微服務(wù)架構(gòu)的深入迈螟,前端SPA應(yīng)用的使用又更加普及了叉抡。如果只是一個(gè)獨(dú)立應(yīng)用的開發(fā),那么前端在開發(fā)階段答毫,往往是以下形態(tài)存在的:
? 本地npm run start
,通過了devServer
開啟了一個(gè)web服務(wù)器褥民,另外通過本地模擬
或者proxy
的方式,把向后端請求接口代理到了后端開發(fā)者提供給我們的地址洗搂。
? 這種方式的確簡單和方便消返,但是有個(gè)前提:一個(gè)獨(dú)立的應(yīng)用
。如果開發(fā)的是一個(gè)大型系統(tǒng)的一個(gè)模塊耘拇∧旒眨可以參考下圖:
? 那么開發(fā)以上單個(gè)模塊的前端工程,往往需要和多方進(jìn)行交互惫叛,甚至還可能存在前端運(yùn)行的特定容器秦驯。這些交互包括:請求后端其他(非本模塊)接口服務(wù)、接口請求權(quán)限認(rèn)證挣棕、前端調(diào)用其他模塊頁面等等译隘。如果還是采用簡單的方式進(jìn)行本地開發(fā),伴隨而來的就需要解決諸如:添加多個(gè)proxy
洛心、前端模擬登錄固耘、前端跨域問題等等,而這種問題的解決词身,同時(shí)也伴隨著工程化配置的復(fù)雜化厅目。
? 本文就基于umijs
腳手架前端應(yīng)用為基礎(chǔ),講解伴隨著前端工程化的深入法严,前端應(yīng)用本地開發(fā)三種解決以上問題的方式损敷。
可行方案迭代
前端Http
全局跨域
? 前端開發(fā)相關(guān)性最強(qiáng)的是后端請求接口以及接口引起的權(quán)限和跨域問題。那么如果借助瀏覽同一個(gè)窗口深啤,同域cookie共享的機(jī)制,自然而然可以想象把前端頁面對后端接口的請求诱桂,通過全地址的方式進(jìn)行處理(而不是devServer
代理一次)。這樣可以提前登陸多模塊的平臺系統(tǒng),然后設(shè)置瀏覽器全局跨域的方式辞槐,繞過接口的授權(quán)和跨域限制。
? 簡單來說就是兩步:
-
修改前端異步請求庫丙号,添加后端接口全地址前綴犬缨,比如
axios
和jquery
的設(shè)置import axios from 'axios'; // 后端接口全路徑前綴 let serverHost = "http://192.168.91.66/" if (process.env.NODE_ENV !== 'development') { axios.defaults.baseURL = serverHost; jquery.ajaxSettings.baseURL = serverHost; }
-
修改瀏覽器跨域限制,默認(rèn)開發(fā)階段支持接口調(diào)用全局跨域枝恋。
chrome瀏覽器畦攘,右鍵屬性知押,在目標(biāo)欄,添加
--args --disable-web-security --user-data-dir="C:/chromeDev"
這樣處理以后,保證了后端接口的合理調(diào)用,但是這種處理方式也存在一定的限制栈虚,比如,如果是前端引用其他模塊的頁面粘姜,跨頁面之間的交互仍舊是限制的(iframe
內(nèi)部window.parent
)孤紧。
前端devServer
代理
既然第一種方案不能那么完美的解決跨頁面交互跨域的問題号显,那么第二種方案自然而然的產(chǎn)生了,深度使用devServer
的代理功能揽碘。
我們通過umijs
配置的proxy
屬性雳刺,把全部需要依賴的后端接口都添加上代理浑此,包括前端跨頁面訪問的其他模塊頁面地址紊馏,一般存在的形式可能如下:
proxy:{
'/xxService': {
target: 'http://10.30.21.32:8080/',
changeOrigin: true
}
}
同時(shí)每次開發(fā)調(diào)試前岸啡,登錄多模塊系統(tǒng),瀏覽器端獲取到cookie信息(chrome F12 Appliaction
)悦荒,添加到proxy
代理頭:
const msServiceCookie = 'language=zh_CN; JSESSIONID=EBE18455DA8A776B097DE467B12E6833; GWSessionId=839AEF9FA35BCAF24466CF7A6C85063C; CASTGC=TGT-44-UUEoZ9yhQc1vNNzdeng2YQewyvsErWbjCLPqfTGAYGIFqYiaML';
const isDev = process.env.NODE_ENV === 'development';
const proxyHeaders = isDev ? {
'Cookie': msServiceCookie,
} : {};
proxy:{
'/xxService': {
target: 'http://10.30.21.32:8080/',
changeOrigin: true,
headers: proxyHeaders
}
}
這樣蟀拷,本地前端開發(fā)調(diào)試過程,無論是接口調(diào)用還是跨域問題,都可以解決了强戴。唯一不太方便的地方就是匕累,cookie是有有效期的衰琐,每次失效得從新賦值,后端接口如果前綴規(guī)則很多羡宙,在proxy
內(nèi)部需要配置多次狸剃。
Nginx
同域代理
行文至此,似乎本文的話題應(yīng)該結(jié)束了狗热,但是總覺得以上兩種方案還是不夠完美钞馁,同時(shí)又讓我想起了一句萬變不離其中的真理:
計(jì)算機(jī)科學(xué)體系內(nèi),沒有加一層解決不了的問題
對匿刮,下面描述的就是基于多一層(Nginx
)僧凰,來實(shí)現(xiàn)更合適這種場景的解決方案。
Nginx
的特點(diǎn)就不再贅述熟丸,我們今天用到的就是其最普通的功能:代理训措。最終我們的目標(biāo)就是,把我們本地的調(diào)試站點(diǎn)和多模塊的平臺系統(tǒng)實(shí)現(xiàn)同域化
。當(dāng)然還要適當(dāng)?shù)膽?yīng)用umijs
非根目錄部署特性。大概原理如下:
我們在本地開發(fā)環(huán)境運(yùn)行一個(gè)Nginx
服務(wù)(http://127.0.0.1:9528)隘竭,配置其代理愈案,使得路徑/app-local/
和/sockjs-node/
代理到本地開發(fā)啟動的nodejs
服務(wù)(本地代碼http://127.0.0.1:9527
),其他路徑直接代理到目標(biāo)多模塊平臺系統(tǒng)(http://10.30.5.36:8080/
)。這樣借助nginx
代理自動攜帶cookie的特色,我們可以直接訪問本地http://127.0.0.1:9528
,既可以做到同訪問http://10.30.5.36:8080
的效果了总珠。而只有/app-local/和/sockjs-node/
代理到了我們的本地,這也正是我們需要達(dá)到的目標(biāo)勘纯。參考nginx
示例如下:
# 開發(fā)代理
server {
listen 9528;
server_name_in_redirect off;
# 本地開發(fā)啟動的站點(diǎn)
location ^~/app-local/{
proxy_pass http://127.0.0.1:9527;
}
# 本地調(diào)試webpack hot功能的websockt
location ^~/sockjs-node/{
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X_Forward_For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_pass http://127.0.0.1:9527;
}
# BAP遠(yuǎn)程環(huán)境
location /{
proxy_pass http://10.30.5.36:8080/;
}
}
介紹到這里局服,熟悉umijs
路由的同學(xué)會發(fā)現(xiàn),原先我們的應(yīng)用的產(chǎn)品態(tài)的路由驳遵,現(xiàn)在必須添加/app-local
前綴了淫奔,否則前端的路由是沒有辦法定位到我們之前的功能的。直白點(diǎn)講堤结,之前我們的功能可能設(shè)置的路由為/appx/emp/list
,那么我們現(xiàn)在開發(fā)階段唆迁,必須修改為/app-local/appx/emp/list
才能正常訪問。同時(shí)竞穷,本地應(yīng)用的所有靜態(tài)資源唐责,也必須添加/app-local
前綴。幸好瘾带,umijs
已經(jīng)提供了合適的機(jī)制來實(shí)現(xiàn)這個(gè)功能鼠哥。還是umijs
的配置:
import routes from './routes';
let buildDevBase = `/app-local/`;
const isDev = process.env.NODE_ENV === 'development';
// ++ 功能點(diǎn)添加
const chainWebpack = (config) => {
// 添加靜態(tài)資源非根目錄部署路徑(開發(fā)階段(yarn start:custom)umi的publicPath沒有生效)
if (process.env.NODE_ENV === 'development') {
config.merge({
output: {
publicPath: buildDevBase,
},
});
}
}
let appConfig = {
treeShaking: true,
routes,
targets: {
ie: 11,
},
plugins: [
[
'umi-plugin-react',
{
dva: {
immer: true
},
dynamicImport: {
webpackChunkName: true,
loadingComponent: './platform/loading',
},
title: 'MES-APP',
dll: false,
routes: {
exclude: [
/models\//,
/services\//,
/model\.(t|j)sx?$/,
/service\.(t|j)sx?$/,
/components\//
],
},
},
]
],
chainWebpack: chainWebpack
};
// ++ 功能點(diǎn)添加
if(isDev){
appConfig = {...appConfig,base: buildDevBase,publicPath: buildDevBase}
}
export default appConfig;
其實(shí)以上的修改,說到底就是實(shí)現(xiàn)umijs
應(yīng)用非根目錄部署過程。
那么本地開發(fā)過程中朴恳,第一步當(dāng)然是通過本地9528
端口抄罕,登錄平臺系統(tǒng),然后切換到本地應(yīng)用地址進(jìn)行正常打開調(diào)試于颖,此時(shí)已經(jīng)是同域場景呆贿,接口調(diào)用也會直接代理到了平臺,對其他模塊頁面訪問也不再造成跨域問題了森渐,似乎我們需要實(shí)現(xiàn)的功能都實(shí)現(xiàn)了做入。當(dāng)然這個(gè)方案還是有待真實(shí)場景試驗(yàn),比如sockjs-node/
這樣的路徑也是在不斷嘗試過程中發(fā)現(xiàn)的同衣。
后記
前端模塊化母蛛、組件化和工程化,已經(jīng)把前端開發(fā)過程變的不再那么簡單乳怎,各種基礎(chǔ)環(huán)境配置也被引入了前端開發(fā)過程,本文的http
代理前弯,就是個(gè)例子蚪缀。但是我相信,本質(zhì)還是不變的恕出,無論是前端路由询枚,還是React的虛擬Dom,還是nodejs
的devServer
都離不開之前的技術(shù)體系浙巫,話說回來了金蜀,前端基礎(chǔ)還是那三把劍:html
,css
,javascript
。