這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 背景 大家在日常開發中應該經常會有需要切換不同環境地址的情況。當一個項目代碼切換環境地址時,vue-cli沒有能夠感知文件的變化,所以代理的還是舊的地址,所以通常我們需要執行npm run serve進行項目重跑,而項目重跑往往意味著長時 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
背景
大家在日常開發中應該經常會有需要切換不同環境地址的情況。當一個項目代碼切換環境地址時,vue-cli沒有能夠感知文件的變化,所以代理的還是舊的地址,所以通常我們需要執行npm run serve
進行項目重跑,而項目重跑往往意味著長時間的等待,非常痛苦!
方案調研
事實上,其實我們只是需要重啟webpack為我們啟動的proxy代理服務
,或許能夠從webpack的代理服務插件中找到解決方法。
從webpack官網可以看到proxy服務其實是由 http-proxy-middleware提供的,或許我們能夠從中找到解決方法。
初步方案
在http-proxy-middleware的配置選項中,除了我們常見的target,還有router。router返回一個字元串的服務地址,當兩個選項都配置了的情況下,會優先使用router函數的返回值,只有當router的返回值不可用時,才會使用target的值。
我們可以利用這一點來重新配置我們的項目代碼。參考文檔在這裡
// vue.config.js const { defineConfig } = require('@vue/cli-service') const { proxy } = require('./environments/proxy.js') module.exports = defineConfig({ devServer:{ proxy }, })
// proxy.js const fs = require('fs') const path = require('path') const encoding = 'utf-8' const getContent = filename => { const dir = path.resolve(process.cwd(), 'environments') return fs.readFileSync(path.resolve(dir, filename), { encoding }) } const jsonParse = obj => { return Function('"use strict";return (' + obj + ')')() } const getConfig = () => { try { return jsonParse(getContent('proxy-config.json')) } catch (e) { return {} } } module.exports = { proxy: { // 介面匹配規則自行修改 '/api': { // 這裡必須要有字元串來進行占位 // 如果報錯Invaild Url,將target改成有效的url字元串即可,如http://localhost:9001 target: 'that must have a empty placeholder', changeOrigin: true, router: () => (getConfig() || {}).target || '' } } }
// proxy-config.json { "target": "http://localhost:9001" }
自此,當我們需要修改環境地址時,只需要修改proxy-config.json
文件便能夠實時生效,不再需要npm run serve
!
重點代碼分析
實現代碼中其實最主要的就是getContent
這個方法,我們項目在每次發起http請求時都會調用router中的函數
,而getContent則會通過node的fs服務,對我們的環境地址文件進行實時讀取
,從而指向我們最新修改的環境地址。
方案總結
在按照參考文檔配置了項目代碼之後,我們發現確實能夠及時指向新的環境地址,再也不需要重啟代碼,不需要長時間的等待了。但是,我們多了兩個需要維護的文件,每次我們修改環境地址時,不僅需要修改config中的api,還需要修改proxy-config.json中的target!
有沒有可能在只需要修改config文件的情況下,實現代理地址動態修改
呢?
方案優化
從上面的重點代碼分析中,可以看到只要我們可以在router函數執行時,拿到正確的config文件中導出的api屬性的值,也可以實現同樣的效果!
這是不是意味著只要我們在函數中對config文件進行require請求,讀取api的值,再return出去就能及時修改代理指向了呢?
沒錯,你會發現無論你怎麼修改,函數內require取到的api永遠是不變的,還是服務剛啟動時的環境地址。
參考源碼可以知道,這是因為我們在使用require請求文件信息時,node會解析出我們傳入的字元串的文件路徑的絕對路徑,並且以絕對路徑為鍵值,對該文件進行緩存
。
因此,如果我們在執行require函數時打斷點進行觀察的話,會發現require上面有一個cache緩存了已經載入過的文件。
這也恰恰說明瞭只要我們能夠刪除掉文件保存在require中的緩存,我們就能夠拿到最新的文件內容,那麼我們也可以據此得出我們的最終優化方案。
// vue.config.js const hotRequire = modulePath => { // require.resolve可以通過相對路徑獲取絕對路徑 // 以絕對路徑為鍵值刪除require中的對應文件的緩存 delete require.cache[require.resolve(modulePath)] // 重新獲取文件內容 const target = require(modulePath) return target } ... proxy: { '/api': { // 如果router有效優先取router返回的值 target: 'that must have a empty placeholder', changeOrigin: true, // 每次發起http請求都會執行router函數 router: () => (hotRequire('./src/utils/config') || {}).api || '', ws: true, pathRewrite: { '^/api': '' } } }
自此,我們項目修改環境地址將不在需要重啟項目,也不需要維護額外的文件夾,再也不需要痛苦等待了!