Vue 預設是使用 hash 模式,但可選擇使用 history 模式。hash 模式時的 URL 會帶 #
符號,例如 https://example.com/#/about
跳轉時不會發出 HTTP 請求,只會按 #
後的內容來顯示相應的元件。
history 模式不會帶 #
,例如 https://example.com/about
。跳轉頁面時會發 HTTP 請求。背後原理是使用 Web History API 和 建構 Router 來避免重刷頁面。但注意,history 模式需配合後端設定,把找不到的路由全都指向 index.html
,才不會出現 404 的問題。
以下會再作詳細解說。
#
後內容的變化。#
前的路由,並把首頁回傳給你,之後所有跳轉頁面都不會發出 HTTP 請求。所以使用者其實是一直停留在 index.html,並在此檔案上切換顯示不同元件來模擬跳轉頁面。#
符號。#
後的內容,因為會把 #
當作是錨點。<a href="#about"> About Us </a>
<h2 id="about">
...
</h2>
pushState()
和replaceState()
方法,以及瀏覽器的 popstate 事件來實現。當 URL 有變,就會 window.location.pathname
來取出路由內容,再在建構 router 物件時,利用 route 的物件把該 pathname 值對應到指定的元件。過程不會重刷頁面。pushState()
更換 URL、存入瀏覽記錄等。https://www.example.com/product/111
,會出現 404 頁面,因為伺服器會去查看有沒有 product/111
這個路由來處理這個 HTTP 請求,最後因為沒找到所以就回 404頁面。以 Node.js 的 Express.js 框架為例。個人對後端的認識不多,只能以自己過往寫 Express.js 來設定伺服器作例子,讓我們更清楚明白以上提到伺服器找不到路由的意思。
先看看 Express.js 最簡單的起手式:
app.js
var express = require('express');
var app = express();
// URL:http://localhost:3000
app.get('/', function(req,res){
res.send('首頁');
})
// URL:http://localhost:3000/user
app.get('/user', function(req,res){
res.send('個人頁面');
})
// 如果沒有對應到以上任何一個 URL,最後就會執行以下程式碼
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
如上做法,跟前端設定 404 頁面的原理是一樣。程式碼由上到下執行,當 HTTP 請求進來時,如果在這裏找不到此請求的 URL,最後就會執行回傳 404 錯誤的程式碼。
因此,後端伺服器需要設定,如果沒找到對應的路由,就一律回傳 index.html。注意,雖然有發出 http 請求,但是以上提過,Vue 是使用 HTML5 history API 以及建構 route 物件的方法,避免了重刷頁面,並實現切換頁面的效果。因此跳轉頁面時不會重刷頁面。
以上講了很多原理,但實際上回傳 404 的情況是怎樣?以下將示範一次,使用 history 模式時,到底如何會發生 404 問題。
首先使用 Vue CLI 建立一個 Vue 專案,並選擇包含 router 功能,router 使用 history mode。
之後打開檔案,輸入 npm run build
打包一次專案,產生 dist 資料夾。把 dist 資料夾拖到 VScode 裏打開,並打開 index.html,再使用 live server 瀏覽這個檔案,你會發現剛載入網頁後,URL 是 http://127.0.0.1:5500/index.html
,但內容是空的:
那是因為現在是直接用 live serve 瀏覽 index.html 檔案。如果你直接訪問 http://127.0.0.1:5500/
就會成功顯示:
然後,嘗試按下 About 按鈕,發現 URL 變成http://127.0.0.1:5500/about
,有成功渲染 about 元件:
再按 Home 按鈕,也有成功渲染 Home 元件,URL 變成 http://127.0.0.1:5500/
:
http://127.0.0.1:5500/about
但當你試著直接訪問 http://127.0.0.1:5500/about
此 URL,會出現 404 頁面:
或者,你可以試試在第二步,成功打開 About 頁面時,再重刷頁面,也同樣是會出現 404 頁面。
現在我們逐一理解原因。
第一步,伺服器收到 /
的請求時,會正常指向 index.html,並讓 Vue 使用 Web History API 和建構 Router 物件來把 pathname 與指定元件對應起來。
第三步,當你直接訪問 http://127.0.0.1:5500/about
,就會向後端伺服器發出 /about
這個 GET 請求,但是伺服器沒有設定處理 /about
這路由,因此回傳 404。
那為什麼在第 2 步,可以成功打開 About 頁?個人理解是,因為我們不是直接訪問 About頁,而是透過打開了首頁,再按 Nav bar 的 About 來進入 About 頁。因此,這裏的流程是:你先載入了 index.html,之後就開始使用 Web History API,跑以上提過的流程,把 pathname 對應到指定元件,成功把 About 元件顯示出來。
從以上情況得知,要避免以上 404 的情況,在伺服器就需要把所有找不到的 URL 一律指向 index.html
。例如,當你直接訪問 http://127.0.0.1:5500/about
時,就會指向 index.html
,然後 Vue 就會跑 Web history API,由 index.html
裏的 Vue router 去把 pathname 對應到指定元件。
#
,跳轉時不會發出 HTTP 請求。限制是不利 SEO,以及會與錨點功能有衝突等。#
,跳轉時會發出 HTTP 請求。限制是瀏覽器兼容性較差,以及後端需要自行設定,避免 404 問題。原来这就是hash模式和history模式的区别(vue-router mode)
Using Vue Router History Mode