本文是手寫Vue-Router的第一篇,主要是對Vue-Router的知識儲備,為後面的手寫做準備。
那麼 VueRouter 怎麼實現呢?要想實現 VueRouter,首先要知道 VueRouter 它的本質是什麼。
VueRoute 的本質是什麼?VueRouter 的本質就是根據 "不同的 hash 值"
或者 "不同的路徑地址"
, 將不同的內容渲染到 router-view
中。
再過去,我學習 VueRouter 的時候,知道 VueRouter 有兩種模式,一種是 hash
模式,一種是 history
模式。那麼這兩種模式有什麼區別呢?
如果是 history 模式,那麼我們的路徑就是這樣的:http://localhost:8080/home
,如果是 hash 模式,那麼我們的路徑就是這樣的:http://localhost:8080/#/home
。
瞭解了這些知識之後,所以實現 VueRouter 的核心關鍵點就在於如何監聽 'hash'
或 '路徑'
的變化, 再將不同的內容寫到 router-view
中。
那麼在實現 VueRouter 之前呢,我在給大家補充一下,如何監聽 'hash'
或 '路徑'
的變化。
首先我新建了一個 test.html 檔案,然後在裡面寫了一個 div
,然後給這個 div
設定了一個 id
,id
的值為 html
。
並且在頁面當中新增了兩個 a 標籤,兩個 a 標籤的 href 分別跳轉地址為,一個是 #/home
,一個是 #/about
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="#/home">首頁</a>
<a href="#/about">關於</a>
<div id="html"></div>
</body>
</html>
基本的結構我們搭建完畢,好了接下來我們怎麼監聽 hash 的變化呢?也非常的簡單,其實在我們原生的 JS 當中,有一個 hashchange
事件,這個事件就是用來監聽 hash 變化的(專門用於監聽 hash 變化的)。
那麼知道了監聽 hash 變化的事件之後,我們怎麼使用呢?我們可以給 window
繫結一個 hashchange
事件,然後在這個事件當中,有一個回撥函數,主要 hash 變化之後,我們就可以在這個回撥函數當中,獲取到當前的 hash 值。
那麼怎麼驗證它會執行這個回撥函數呢,我們可以在這個回撥函數當中,列印一下當前的 hash 值。
<script>
window.addEventListener('hashchange', () => {
console.log('當前的hash值發生了變化');
});
</script>
好了,我們開啟瀏覽器,然後點選首頁,我們可以看到控制檯列印了一句話,說明我們的 hash 值發生了變化,看到這一點就可以驗證我的一個說法。
接下來我們要做的就是將內容渲染到 div
中,我們先簡單的來將 hash 值寫入到 div
中。
window.addEventListener('hashchange', () => {
const currentHash = location.hash.slice(1);
document.querySelector('#html').innerHTML = currentHash;
});
我們開啟瀏覽器,點選首頁,我們可以看到 div
中的內容變成了 home
,點選關於,我們可以看到 div
中的內容變成了 about
。
將來我們是不是根據這個獲取到對應的元件,然後將元件渲染到 div
(某一個容器當中)中就可以了。
好了到這裡我們的監聽 hash 就可以,可以了之後還沒完,可以了之後有沒有這麼一種情況,就是我們第一次開啟頁面的時候我們地址上面是沒有 hash 值的,還有可能就是我們位址列是有 hash 值的這種情況,是不是有可能,對吧,我們先來看看我們第一次開啟頁面的時候,有 hash 值我們的容器顯示的是什麼。
我們可以看到我們的容器顯示的是空的,那麼我們怎麼解決這個問題呢?我們可以在頁面載入的時候,手動的觸發一次 hashchange
事件,這樣我們就可以在頁面載入的時候,將內容渲染到 div
中。
首先我們在 window
上面繫結一個 load
事件,然後在這個事件當中,我們手動的觸發一次 hashchange
事件。
window.addEventListener('load', () => {
const currentHash = location.hash.slice(1);
document.querySelector('#html').innerHTML = currentHash;
});
我們開啟瀏覽器,我們可以看到我們的容器當中顯示的是 home
,這樣我們就解決了第一次開啟頁面的時候,我們的容器顯示的是空的這個問題。
到此為止,我們就可以監聽 hash 的變化了,那麼我們怎麼監聽路徑的變化呢?我們可以使用 history
的 pushState
方法,這個方法可以改變路徑,然後我們就可以監聽路徑的變化了。
在看路徑地址之前,我們先將基本的內碼錶面結構搭建一下,路徑與之前的 hash 是不一樣的,所以我們這裡的 a 標籤就不能使用 href 屬性了,路徑我們可以給 a 標籤繫結一個事件,繫結一個方法然後在這個方法當中來改變路徑。
頁面樣式的基本結構程式碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a onclick="go('/home')">首頁</a>
<a onclick="go('/about')">關於</a>
<div id="html"></div>
<script>
function go(path) {
}
</script>
</body>
</html>
定義了一個 go 方法,接收一個引數 path,接下來要做的事情就是根據這個 path 來改變路徑,這個我們要怎麼實現呢?這裡我們可以藉助一個 history
物件,在 history 物件當中有一個 pushState
方法,這個方法接收三個引數,第一個引數是 state
,第二個引數是 title
,第三個引數是 url
。
pushState 方法引數:
那麼我們怎麼使用呢?我們可以在 go 方法當中,呼叫 pushState
方法,然後將 path 傳入到 pushState
方法當中,這樣我們就可以改變路徑了。
history.pushState(null, null, path);
好了,我們開啟瀏覽器,點選首頁,我們可以看到我們的路徑變成了 http://localhost:8080/home
,點選關於,我們可以看到我們的路徑變成了 http://localhost:8080/about
。
沒問題之後,我們再將內容渲染到 div
中,我們可以在 go
方法當中,獲取到當前的路徑,然後將路徑寫入到 div
中。
document.querySelector('#html').innerHTML = path;
我們開啟瀏覽器,點選首頁,我們可以看到我們的容器當中顯示的是 home
,點選關於,我們可以看到我們的容器當中顯示的是 about
。
到此為止,我們就可以監聽路徑的變化了,好了知道這些內容之後,還有一個注意點需要給大家說一下:
我們先基於 IDEA 執行我們的專案,然後,點選一下首頁這個時候我們的路徑與容器內容都是 /home
, 好,我們這個時候將地址複製一下,例如現在路徑已經變為了 http://localhost:63342/home
,我們在點選一下關於,我們可以看到我們的路徑變為了 http://localhost:63342/about
, 好,這個時候我們的關鍵點就要來了:
正如上圖所示,我們的路徑變為了 http://localhost:63342/home
, 但是容器的內容還是 about
,這是為什麼呢?所以說這個東西我們也需要進行同步一下,那麼我們手動新增了路徑那麼它怎麼知道我們有沒有前進與後退呢?非常簡單,其實在我們的原生 JS 當中,又有一個事件,這個事件就是 popstate
事件,通過這個事件,我們就可以監聽到前進與後退的點選,通過這個事件監聽了前進與後退的點選之後,它會執行一個回撥函數,我們在這個回撥函數當中,就可以處理之前的問題了。
更改我們的程式碼,我們可以在 window
上面繫結一個 popstate
事件,然後在這個事件當中,我們可以獲取到當前的路徑,然後將路徑寫入到 div
中。
window.addEventListener('popstate', () => {
document.querySelector('#html').innerHTML = location.pathname;
});
我們開啟瀏覽器,點選首頁,我們可以看到我們的容器當中顯示的是 home
,點選關於,我們可以看到我們的容器當中顯示的是 about
,好,這個時候我們的關鍵點就要來了,我們點選一下瀏覽器的前進與後退,我們可以看到我們的容器當中顯示的是 home
與 about
,這樣我們就解決了這個問題。
到此為止,我們瞭解瞭如何監聽 hash 與路徑的變化,並且瞭解到瞭如何監聽前進與後退的點選,hash 與路徑的變化。
本篇文章就到這裡,感謝大家的閱讀,如果有什麼不足的地方,歡迎大家指出,我會及時的進行修改。