我之前在上線自己的博客遇到過下面這些問題
- 爲啥我的博客在開發階段都沒問題,部署到服務器之後訪問不了除了
/
的頁面 - 路由用hash模式就沒問題,改成history就會有問題
- 公衆號:前端南玖
- 不定時有送書活動,記得關注~
- 每日推送前端技術文章~
什麼是路由
在Web開發過程中,經常會遇到『路由』的概念。那麼,到底什麼是路由?簡單來說,路由就是URL到函數的映射。
並且路由這個概念最早是出現在後端的,因爲早期的網頁都是服務端渲染的,比如:JSP,PHP,ASP等語言,都是直接返回渲染好的html給客戶端顯示。
後端路由
在web開發早期的年代裏,前端的功能遠不如現在這麼強大,一直是後端路由佔據主導地位。無論是jsp,還是php、asp,用戶能通過URL訪問到的頁面,大多是通過後端路由匹配之後再返回給瀏覽器的。瀏覽器在地址欄中切換不同的URL時,每次都向後臺服務器發出請求,服務器響應請求,在後臺拼接html文件返回給前端,並且每次切換頁面時,瀏覽器都會刷新頁面。
在後端,路由映射表中就是不同的URL地址與不同的html + css + 後端數據 之間的映射
下面這張圖或許能夠讓你更加清楚的瞭解後端路由:
前端路由
前端路由是後來發展到SPA(單頁應用)時纔出現的概念。
前端路由主要是有兩種模式:
- hash: 帶有hash的前端路由,優點是兼容性高。缺點是URL帶有
#
號不好看 - **history: **不帶hash的前端路由,優點是URL不帶
#
號,好看。缺點是既需要瀏覽器支持也需要後端服務器支持
前端路由應用最廣泛的例子就是當今的SPA的web項目。不管是Vue、React還是Angular的頁面工程,都離不開相應配套的router工具。
整個頁面就只有一整套的HTMLCSS+JS,這一套HTML+CSS+JS中包含了許多個網頁,當我們請求不同的URL時,客戶端會從這一整套HTML+CSS+JS中找到對應的HTML+CSS+JS,將它們解析執行,渲染在頁面上。
前端路由帶來的最明顯的好處就是,地址欄URL的跳轉不會白屏了——這也得益於客戶端渲染帶來的好處。
我們同樣來看一張圖:
Hash模式
早起的前端路由實現就是基於
location.hash
來實現的,location.hash
就是路由#
後面的內容,其原理就是通過hashchange
監聽#
後面的內容的變化來進行頁面更新。hash
模式是利用瀏覽器不會對#
後面的路徑對服務端發起請求。
使用hash模式有兩個特點:
- 改變hash值,瀏覽器不會重新加載頁面
- 當刷新頁面時,hash不會傳給服務器
也就是說http://localhost/#a
與http://localhost/
這兩個路由其實都是去請求http://localhost
這個頁面的內容,至於爲什麼它們可以渲染出不同的頁面,這個是前端自己來判斷的,不需要後端配置。
優點:
- 可以兼容低版本瀏覽器,支持IE8
- 只有
#
之前的內容纔會作爲URL發送給服務器,就算服務端沒有對路由進行全覆蓋也不會返回404 hash
改變都會在瀏覽器訪問的歷史記錄中增加一個記錄,所以可以通過瀏覽器進行前進後退,如果想在hash模式下不保存記錄,可以使用location.replace
History模式
history
是基於HTML5新增的pushState
和replaceState
兩個API以及瀏覽器的popState
事件監聽歷史棧的改變,只要歷史棧有信息發生改變,就會觸發該事件。這種模式同樣也是不會向後端發起請求的。
優點:
- 該模式的路由不帶
#
,看起來更美觀 pushState
設置的URL
可以是任意的與當前URL同源的URL,而hash
只能改變#後面的內容
缺點:
- IE9及其以下版本瀏覽器不支持,IE10開始支持
vue-router會檢測瀏覽器版本,當無法啓用history
模式時會自動降級爲hash
模式
文章開頭說的問題是什麼原因導致的?
爲啥我的博客在開發階段都沒問題,部署到服務器之後訪問不了除了/
的頁面??
那是因爲vue-cli
在開發模式下幫你啓動的那個express
開發服務器幫你做了這方面的配置。理論上我們在開發模式下本來也是需要配置服務端的,只不過vue-cli
都幫你配置好了,所以你就不用手動配置了。
那麼該如何配置呢?
其實在生產模式下配置也很簡單,參考vue-router給出的配置例子。一個原則就是,在所有後端路由規則的最後,配置一個規則,如果前面其他路由規則都不匹配的情況下,就執行這個規則——把構建好的那個index.html
返回給前端。這樣就解決了後端路由拋出的404的問題了,因爲只要你輸入了http://localhost/user/1
這地址,那麼由於後端其他路由都不匹配,那麼就會返回給瀏覽器index.html
。
瀏覽器拿到這個html之後,router庫就開始工作,開始獲取地址欄的URL信息,然後再告訴前端庫(比如Vue)渲染對應的頁面。到這一步就跟hash模式是類似的了。
當然,由於後端無法拋出404的頁面錯誤,404的URL規則自然是交給前端路由來決定了。你可以自己在前端路由裏決定什麼URL都不匹配的404頁面應該顯示什麼。
頁面渲染流程
- 瀏覽器通過DNS服務器得到域名的IP地址,向這個IP地址請求得到HTML文本
- 瀏覽器渲染進程解析HTML文本,構建DOM樹
- 解析HTML的同時,如果遇到內聯樣式或者樣式文件,則下載並構建樣式規則,如果遇到JavaScript腳本,則會下載執行腳本
- DOM樹和CSSOM構建完成之後,渲染進程將兩者合併成渲染樹(render tree)
- 渲染進程開始對渲染樹進行佈局,生成佈局樹(layout tree)
- 渲染樹對佈局樹進行繪製,生成繪製記錄
服務端渲染(SSR)
顧名思義,服務端渲染就是在瀏覽器請求頁面URL時,服務端直接將我們需要的HTML文本組裝好,並返回給瀏覽器,這個HTML文本被瀏覽器解析之後,不需要經過JavaScript腳本的執行,可直接構建出完整的DOM樹並展示頁面中。這個服務端組裝的過程,叫做服務端渲染。
客戶端渲染(CSR)
頁面的渲染其實就是瀏覽器將HTML文本轉化爲頁面幀的過程。而如今我們大部分WEB應用都是使用 JavaScript 框架(Vue、React、Angular)進行頁面渲染的,也就是說,在執行 JavaScript 腳本的時候,HTML頁面已經開始解析並且構建DOM樹了,JavaScript 腳本只是動態的改變 DOM 樹的結構,使得頁面成爲希望成爲的樣子,這種渲染方式叫動態渲染
,也可以叫客戶端渲染(client side rende)
。
前端渲染把渲染的任務交給了瀏覽器,通過客戶端的算力來解決頁面的構建,這個很大程度上緩解了服務端的壓力。而且配合前端路由,無縫的頁面切換體驗自然是對用戶友好的。不過帶來的壞處就是對SEO不友好。
需要明確的是,只要在瀏覽器地址欄輸入URL再回車,是一定會去後端服務器請求一次的。而如果是在頁面裏通過點擊按鈕等操作,利用router庫的api來進行的URL更新是不會去後端服務器請求的。
前端路由與服務端渲染
雖然前端渲染有諸多好處,不過SEO的問題,還是比較突出的。所以react、vue等框架在後來也在服務端渲染上做着自己的努力。基於前端庫的服務端渲染跟以前基於後端語言的服務端渲染又有所不同。前端框架的服務端渲染大多依然採用的是前端路由,並且由於引入了狀態統一、vnode等等概念,它們的服務端渲染對服務器的性能要求比php等語言基於的字符串填充的模板引擎渲染對於服務器的性能要求高得多。所以在這方面不僅是框架本身在不斷改進算法、優化,服務端的性能也必須要有所提升。
當然在二者之間,也出現了預渲染的概念。也即先在服務端構建出一部分靜態的html文件,用於直出瀏覽器。然後剩下的頁面再通過常用的前端渲染來實現。通常我們可以把首頁採用預渲染的方式。這個的好處是明顯的,兼顧了SEO和服務器的性能要求。不過它無法做到全站SEO,生產構建階段耗時也會有所提高,這也是遺憾所在。
關於預渲染,可以考慮使用prerender-spa-plugin這個webapck的插件,它的3.x版本開始使用puppeteer來構建html文件了。