前言
對于前端工程師而言,多多少少會碰到按需加載的需求。
比如一個系統(tǒng),需要用戶登陸以后才能使用,對于傳統(tǒng)的前后端未分離的情況,我們一般的處理方式是,當檢測到用戶未登錄的時候,一般會重定向到登錄頁面,讓用戶進行登錄。
但是對于前后端分離的情況,前端工程師一般傾向于單頁面的處理模式,而想要處理這種情況的話,我們一般傳統(tǒng)的做法是,會選擇采用 location.search 的方式,即通過參數來判斷。
比如檢測到用戶沒登陸,我們會刷新頁面,自動在地址后面加上諸如 ?page=login 等形式的參數,然后我們的系統(tǒng)初始化的時候,會檢測 location.search 中是否存在這個參數,如果存在,頁面上顯示的就是登錄有關的內容。
但是這種處理方式寫起來太過復雜,而前端比較流行的框架 vue、react 等給了我們比較好的解決方案。
那就是采用 vue-router 或者 react-router 這種解決方案,一般會有兩種模式,history 模式和 hash 模式,兩種模式對于開發(fā)來說,沒有任何的區(qū)別。
history、hash 模式區(qū)別
這里主要不是想介紹前端 router 的相關內容的,這里就一筆帶過,不做過多的贅述了。
我們知道的是,history 模式,是基于 html5 提供的 history api 來實現的。
對于 react router 來說,它的作用是這樣的:
Browser history 是使用 React Router 的應用推薦的 history。它使用瀏覽器中的 History API 用于處理 URL,創(chuàng)建一個像example.com/some/path這樣真實的 URL 。
而一般所謂的 hash 模式,就是通過來監(jiān)聽 hash 的變化,來改變頁面的視圖的。
對于 react router 來說,他的作用是這樣的:
Hash history 使用 URL 中的 hash(#)部分去創(chuàng)建形如example.com/#/some/path的路由。
到這里,我想以前不了解的童鞋應該會明白了,這篇文章想要表達的內容到底是什么了。
我們打包以后,采用 hash 的前端路由項目,扔在任何服務器里面,是不會存在找不到資源的情況,因為頁面的入口始終都是 example.com/rootpath/ 其他資源都是根據這個路徑來索引的。
但是對于 history 方式的前端路由項目,打包了以后,扔在各種靜態(tài)資源服務器中,是沒有辦法正常使用的。
刨根問底
你如果之前有類似的經歷,那么你肯定上網找過解決方案。網上一找,會有很多教你怎么配置的“攻略”,但是都是流于表面,根本不告訴你為什么這么用,這么用的原理是什么,這么用究竟是要解決什么問題。
弄懂了原理,我們才能對癥下藥,才能在任何靜態(tài)資源服務器中都能舒服的使用我們的 history 路由。
首先,我們來探求,為什么找不到資源的原因。
因為我們一般都是打包以后放在靜態(tài)資源服務器中的,我們訪問諸如 example.com/rootpath/ 這種形式的資源沒問題,是因為,index.html 文件是真實的存在于 rootpath 文件夾中的,可以找到的,返回給前端的。
但是如果訪問子路由 example.com/rootpath/login 進行登錄操作,但是 login/index.html 文件并非真實存在的文件,其實我們需要的文件還是 rootpath 目錄中的 index.html 。
再者,如果我們需要 js 文件,比如登陸的時候請求的地址是 example.com/rootpath/login/js/dist.js 其實我們想要的文件,還是 rootpath/js/ 目錄中的 dist.js 文件而已。
前端路由其實是一種假象,只是用來蒙蔽使用者而已的,無論用什么路由,訪問的都是同一套靜態(tài)資源。
之所以展示的內容不同,只是因為代碼里,根據不同的路由,對要顯示的視圖做了處理而已。
我相信,說到這里,聰明的童鞋,應該就想明白了,我們該怎么做,才能做到 history 模式的前端路由打包以后照樣能用了。
那就是,一旦匹配子路徑,發(fā)現 404 了,直接將路徑中的子目錄移除掉,返回根目錄種對應的文件即可。
這么說,可能有點拗口,舉幾個例子,可能就比較好理解了。
比如要找 example.com/rootpath/login 靜態(tài)資源服務器找不到,那就返回 example.com/rootpath/ 內容;要找 example.com/rootpath/login/css/style.css 找不到,那就照著 example.com/rootpath/css/style.css 這個路徑去找。
總之就是,請求的是子目錄,找不到,那就返回根目錄一級對應的資源文件就好了。
理是這個理,但是該如何配置呢?
別急,接下來,就將幾種常見的情況下,該如何配置,保證 history 前端路由也能正常使用。
在 nginx 中使用
如果你打包以后的前端靜態(tài)資源文件,想要仍在 nginx 中使用,那首先將你打包好的靜態(tài)資源目錄扔進 www 目錄,比如你打包好的資源的目錄叫 rootpath ,那么直接將 rootpath 整個目錄丟進 www 目錄即可。
然后打開我們的 nginx 配置文件 nginx.conf,插入以下配置:
location /rootpath/ {
root html;
index index.html index.htm;
try_files $uri $uri/ /rootpath/index.html;
}
這樣就能保證我們的前端路由能夠正常的訪問了

如果要放在根目錄用,直接將配置中的 /rootpath 相關字段從配置中去掉即可。
在 tomcat 中使用
如果在 window 中使用,可以直接下載二進制安裝包,解壓以后,一般直接就能使用。

一般我們將我們的靜態(tài)資源放在 webapps 文件夾中。
要想達到和上面 nginx 中一樣的效果,首先同樣是將 rootpath 文件夾放進 webapps 中。
然后在 webapps/rootpath 中新建 WEB-INF 文件夾,文件夾里新建一個 web.xml 文件。
文件中內容配置如下即可:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>your_display_name</display-name>
<error-page>
<error-code>404</error-code>
<location>/index.html</location>
</error-page>
</web-app>
配置好以后,重啟 tomcat,會得到如上圖同樣的效果。
總結反思
其實,很多時候,碰到的問題,多查資料,深究其原理,明白了原理以后,就能找到合適的解決方案。
如果只是機械式地照搬,碰運氣式的嘗試,往往會南轅北轍,很難找到滿意的解決方案。