- 要修改後端程式碼,如果後端專案執行在自己機器上還好,如果是執行在別的伺服器上,甚至是其他團隊在維護,那麼修改一段程式碼就會比較麻煩,並且還要確保這些程式碼不會被髮布到生產環境中去。
- 每次啟動瀏覽器的時候要輸入一長串程式碼來關閉瀏覽器的安全策略,比如 Chrome 瀏覽器的命令如下:
open -a /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
不僅麻煩,萬一一不小心在這種模式下訪問了一些敏感的資料,還會帶來安全隱患。 - 開發模式下前端調介面要帶上測試後端伺服器的 URL,因此釋出到生產環境之前還要把它去掉。
總之就是比較麻煩,體會不到那種脫了褲子就上,完事提上褲子就走的爽快感,我說的是上廁所。所以今天就是要介紹一種更加簡單安全的解決方案,同時我們會深入去了解其中的原理是什麼。
首先,用 create-react-app 建立一個前端專案,假如你的前端專案執行的地址是 http://localhost:3000
,與此同時提供 API 的後端專案執行的地址是 http://localhost:4000
,你要做的只是在前端工程的 package.json 檔案中新增這樣一行配置:
"proxy": "http://localhost:4000"
複製程式碼
然後你就會神奇地發現,從前端頁面發出的 HTTP 請求,雖然訪問的依然是 3000 埠,但是會被自動轉發到 4000 埠的後端伺服器並得到正確的響應,於此同時訪問頁面的請求卻不會被轉發,依然能夠被前端路由捕獲,這樣我們就完全不需要再考慮如何處理跨域的問題了。問題是解決了,但是又出現了 2 個問題縈繞在我的心中:
- package.json 中的
proxy
引數是作用在什麼地方的? - 是怎麼樣做到把訪問頁面的請求和訪問 REST API 的請求區分開的?
帶著這樣的疑問我們一起去看看 create-react-app 的原始碼是怎樣寫的,首先在前端專案中的 package.json 裡我們能看到,專案啟動執行的指令碼是 react-script start
,所以我們開啟檔案 create-react-app/packages/react-scripts/scripts/start.js(為何直接能定位到這個檔案,以及 react-script 這個命令是如何註冊的,屬於其他知識點,本文不展開說明,有疑問的童鞋可以去這裡 學習一個),我們看到有以下程式碼:
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
// Serve webpack assets generated by the compiler over a web sever.
const serverConfig = createDevServerConfig(
proxyConfig,
urls.lanUrlForConfig
);
const devServer = new WebpackDevServer(compiler, serverConfig);
複製程式碼
此處的 paths.appPackageJson
的宣告在 create-react-app/packages/react-scripts/config/paths.js 中:
module.exports = {
appPackageJson: resolveApp('package.json'),
};
複製程式碼
因此我們就知道,這裡的 proxySetting
就是我們之前在前端工程的 package.json 中定義的 proxy
的值,然後我們看到,proxySetting
被用來生成了 serverConfig
,最終 serverConfig
作為配置引數建立了 WebpackDevServer
例項。webpack-dev-server 是一個用於啟動 webpack 的測試伺服器,並且提供了諸如 HMR 等方便開發的功能,因此我們就得出第一個結論:前端工程的 package.json 中定義的 proxy
值,是作用於 WebpackDevServer,最終通過 WebpackDevServer 進行的轉發。
讓我們繼續試圖解答第二個問題——是怎麼樣做到把訪問頁面的請求和訪問 REST API 的請求區分開的?我們看到 proxySetting
首先是被傳入 prepareProxy
方法得到 proxyConfig
,然後在 createDevServerConfig
方法中返回了一個物件,並且物件的 proxy
欄位的值為 proxyConfig
,最終該物件就是 webpack-dev-server 的配置項,在 webpack-dev-server 文件 中可以看到 proxy 的作用就是做一層代理,把從頁面來的請求轉發到另一個地址,因此關鍵就在於 proxyConfig
的配置是怎麼樣的,於是目光轉移到 prepareProxy
方法,prepareProxy
方法的定義在 create-react-app/packages/react-dev-utils/WebpackDevServerUtils.js 中,在這裡我們可以看到首先是對 proxy
進行了型別和格式的檢測,然後如果 proxy
是一個格式正確的字串,就返回一個只有一個物件元素的陣列,在這個物件中的 context
欄位中出現瞭如下的判斷:
context: function(pathname, req) {
return (
req.method !== 'GET' ||
(mayProxy(pathname) &&
req.headers.accept &&
req.headers.accept.indexOf('text/html') === -1)
);
}
複製程式碼
在這裡我們看到有對 req.headers.accept
進行判斷,req.headers.accept
用於表示瀏覽器通過這次 HTTP 請求希望獲取到的內容型別,因此如果 accept 中帶有 text/html
則說明本次請求獲取的是一個 document,因此就不應該被轉發到後端,這一堆判斷邏輯用一幅圖表示出來如下:
除了文中提到的這種最簡單的配置,webpack-dev-server 的 proxy 還支援多種配置方式以同時滿足多種代理規則,感興趣的同學可以去文件裡面瞭解更多細節。