React路由有兩種實現方式:
- HashRouter:利用hash實現路由切換
- BrowserRouter:利用h5 Api實現路由切換
1.1 HashRouter
利用hash實現路由切換
<body>
<div id="root"></div>
<a href="#/a">去/a</a>
<a href="#/b">去/b</a>
</body>
<script> let root = document.getElementById('root') window.addEventListener('hashchange',(event) =>{ let hash = window.location.hash root.innerHTML = hash }) </script>
模擬點擊切換頁面,每當瀏覽器里的hash值發生變化之后,就會觸發一個事件,叫hashchange,這個函數有一個回調,可以通過window.location.hash拿到當前的hash值。
1.2 BrowserRouter (瀏覽器路由)
利用h5 Api實現路由切換,主要是借助history對象。
- history對象提供了操作瀏覽器會話的歷史接口。
- historylength屬性聲明了瀏覽器歷史列表中的元素數量。
- pushState,H5引入了history.pushState()和history.replaceState()方法,它們分別可以添加和修改歷史記錄的條目,這些方法通常與window.onpopstate配合使用
- onpopstate,window.onpopstate是popstate事件再window對象上的事件處理程序
setTimeout( () => { //pushState不會觸發事件
window.history.pushState({page: 1},'page1','/page1') },1000) setTimeout( () => { //pushState不會觸發事件
window.history.pushState({page: 2},'page2','/page2') },2000) setTimeout( () => { //pushState不會觸發事件
window.history.pushState({page: 3},'page3','/page3') },3000) //后退的時候觸發popstate
setTimeout( () => { //go(-1)后退一個,會觸發popstate事件
window.history.go(-1) },4000)
通過 window.history.pushState可以向history容器中存入當前路徑,這個容器結構類似於棧,后進先出,當然也可以通過window.history.go這個方法后退到前一個路徑,這個方法會觸發一個popstate事件,通過這個事件我們可以做一些操作,比如向頁面添加內容等:
//這個事件會在go(-1)的時候觸發,可以在觸發的時候改變文檔內容
window.onpopstate = function(event){ console.log(event) root.innerHTML = window.location.pathname }
history對象在回退的時候有事件,但是在pushstate的時候並不會觸發事件,我們要想自己做一些操作就需要改寫一個onpushState事件,以便我們進行頁面操作。
let root = document.getElementById('root') window.onpushstate = function(state,title,url) { root.innerHTML = url } ;(function (history) { //1.緩存原生的pushState方法
let pushState = history.pushState //2.改寫pushState方法
history.pushState = function(state,title,url) { //3.自定義改寫后的事件名為onpushstate
if(typeof window.onpushstate === 'function') { window.onpushstate(state,title,url) } //4.調用原生方法並且執行
pushState.call(history,state,title,url) } })(window.history)
看過vue源碼的同學肯定知道vue里面對數組的響應式處理就是通過改寫數組的那7個方法實現的。我們這里也一樣,攔截了原生的方法,對原生方法進行了緩存,然后再改寫原生方法,最后再執行,就這么簡單。
完整版:
<body>
<div id="root"></div>
</body>
<script> let root = document.getElementById('root') window.onpushstate = function(state,title,url) { root.innerHTML = url } ;(function (history) { //1.緩存原生的pushState方法
let pushState = history.pushState //2.改寫pushState方法
history.pushState = function(state,title,url) { //3.自定義改寫后的事件名為onpushstate
if(typeof window.onpushstate === 'function') { window.onpushstate(state,title,url) } //4.調用原生方法並且執行
pushState.call(history,state,title,url) } })(window.history) //這個事件會在go(-1)的時候觸發,可以在觸發的時候改變文檔內容
window.onpopstate = function(event){ console.log(event) root.innerHTML = window.location.pathname } // window.history 這是瀏覽器原生提供的對象,通過它來操作會話容器
setTimeout( () => { //pushState不會觸發事件
window.history.pushState({page: 1},'page1','/page1') },1000) setTimeout( () => { //pushState不會觸發事件
window.history.pushState({page: 2},'page2','/page2') },2000) setTimeout( () => { //pushState不會觸發事件
window.history.pushState({page: 3},'page3','/page3') },3000) //后退的時候觸發popstate
setTimeout( () => { //go(-1)后退一個,會觸發popstate事件
window.history.go(-1) },4000) </script>
下一節寫一個自己的路由庫!