前前言
最近工作比較忙,寫的比較慢,當我寫完這一篇準備發(fā)布的時候,發(fā)現(xiàn)掘金剛好也有一篇關(guān)于前端路由的,而且寫的比我詳細,不過辛辛苦苦寫的總不能刪掉吧,再說我的路由風格是純個人思路實現(xiàn)的,所以還是硬著頭皮發(fā)了哈。愛掏網(wǎng) - it200.com
前言
用過現(xiàn)代前端框架的同學,對前端路由一定不陌生, vue, react, angular 都有自己的 router, 那么你對 router 的工作原理了解嗎? 如果還不了解, 那么請跟我一起來手寫一個簡單的前端路由, 順便了解一下.
實現(xiàn)路由的2種方式
- hash模式
- history模式
缺點:
hash:丑(地址欄要多一個#), 某些特殊場景有坑 ,如微信支付回調(diào)
history: 兼容性較差, 直接訪問會400 ,除非后端或者服務器有做處理
基本原理
hash是基于 監(jiān)聽 hashchange 事件實現(xiàn)的,history 是基于 pushState 和 popState,
實現(xiàn)
由于history兼容性較差,而且實現(xiàn)方式基本沒多大區(qū)別,本文就以hash模式來實現(xiàn),history的實現(xiàn)方式只實現(xiàn)不同的部分。愛掏網(wǎng) - it200.com
html>
head>
meta charset="UTF-8">
title>Documenttitle>
head>
body>
input type="button" onclick="hashPush('index')" value="goto index">
body>
script>
// 頁面跳轉(zhuǎn)
function hashPush(url) {
location.hash = "#" + url
}
// 監(jiān)聽hash的變動
window.addEventListener('hashchange', function (e) {
console.log('當前的hash地址', location.hash.slice(1) || "/")
})
script>
html>
以上就是hash模式的最簡單實現(xiàn),查看控制臺,可以看到不管是點擊跳轉(zhuǎn)按鈕,還是點擊瀏覽器的前進/后退按鈕,控制臺都會輸出當前頁面對應的 'url',有了 'url',我們就可以對內(nèi)容做條件渲染了
我們在上面的代碼的基礎(chǔ)上,稍作修改一下,增加2個div,一個是login,一個是index
html>
head>
meta charset="UTF-8">
title>Documenttitle>
style>
body{
margin: 0;
padding: 0;
}
#login, #index{
width: 100%;
height: 100%;
display: none;
}
#login{
background: #f5f5f5;
display: block;
}
#index{
background: #aaddff;
}
style>
head>
body>
div id="login" class="component">
input type="button" onclick="hashPush('index')" value="登陸">
div>
div id="index" class="component">
input type="button" onclick="hashPush('login')" value="退出登陸">
div>
body>
script>
// 頁面跳轉(zhuǎn)
function hashPush(url) {
location.hash = "#" + url
}
// 監(jiān)聽hash的變動
window.addEventListener('hashchange', function (e) {
let url = location.hash.slice(1) || "/"
console.log('當前的hash地址', url)
setVisible(url)
})
// 顯示跟路由地址對應的內(nèi)容,隱藏其他內(nèi)容
function setVisible(url) {
let components = Array.from(document.body.querySelectorAll(".component"))
components.map(item => {
if(item.id===url) {
console.log('顯示',item.id)
item.style.display = 'block'
} else {
console.log('隱藏',item.id)
item.style.display = 'none'
}
})
}
script>
html>
這樣,我們就能根據(jù)不同的hash地址,顯示不同的內(nèi)容,是不是已經(jīng)有點路由的味道了呢?但是目前還無法傳參,傳參的方式有很多種,最常見最通俗的,應該是query string 了吧? query string 其實很簡單,解析url即可。愛掏網(wǎng) - it200.com
// 監(jiān)聽hash的變動
window.addEventListener('hashchange', function (e) {
let url = location.hash.slice(1) || "index"
// 解析url
let questionIndex = url.indexOf("?")
let path, query
if(questionIndex >= 0){
path = url.substr(0,questionIndex)
let queryString = url.substr(questionIndex+1)
let queryArray = queryString.split("&")
let queryObject = {}
queryArray.map(str => {
let equalIndex = str.indexOf("=")
if(equalIndex > 0) {
let key = str.substr(0,equalIndex)
let value = str.substr(equalIndex+1)
queryObject[key] = value
}
})
query = queryObject
} else {
path = url
query = {}
}
console.log('接收到的參數(shù)', query)
setVisible(path)
})
這樣,我們就能獲取到URL上傳遞的query string , 還順便解決了傳參會導致路由解析不正確的bug.
傳參其實還有個更簡單的方法,就是 設(shè)置一個全局變量 param,然后在需要傳值的時候,直接對param 賦值即可
// 設(shè)置一個全局變量
var param = {}
// 頁面跳轉(zhuǎn)
// 這個多了個參數(shù) args
function hashPush(url, args) {
location.hash = "#" + url
param = args
}
由于是全局變量,所以可以在任意位置使用 param,不過這樣直接使用全局變量的方式,也有它的弱點,就是點擊返回按鈕的時候無法保存變量,而query string 因為是在 url 里面的,所以返回的時候,可以拿到上個頁面的 query string, 那我們有沒有辦法讓全局變量的方式也能保存上一個頁面的參數(shù)呢? 我們來稍微修改一下代碼
// 設(shè)置一個全局變量
var param = {}
// 頁面跳轉(zhuǎn)
// 這個多了個參數(shù) args
function hashPush(url, args) {
location.hash = "#" + url
// 如果有傳args參數(shù),就在 param 對象下建一個名字跟當前url一樣的對象,并把args賦值給它
if(args) {
param[url] = args
}
}
那我們在 hashchange 的時候,可以根據(jù)url來定位該頁面的參數(shù)。愛掏網(wǎng) - it200.com
// 監(jiān)聽hash的變動
window.addEventListener('hashchange', function (e) {
// 省略其他代碼
args = param[path] || {}
// 省略其他代碼
})
這樣,我們就初步實現(xiàn)了一個hash路由,那么接下來,我們來看看history路由怎么實現(xiàn)。愛掏網(wǎng) - it200.com
history 模式
history 模式主要依靠 調(diào)用 history.pushState() 方法 和 監(jiān)聽 popstate 事件。愛掏網(wǎng) - it200.com
history.pushState() 方法接收3個參數(shù):
- 要傳遞的數(shù)據(jù)(參數(shù))
- 給頁面設(shè)置的標題(兼容性差,幾乎沒用)
- url
我們看看調(diào)用實例
history.pushState({id:1}, '我是頁面標題', url)
需要注意的是 pushState 的時候,并不會觸發(fā) popstate 事件,只有在前進后退的時候,才會觸發(fā),所以 pushState 之后,需要手動去設(shè)置頁面的相關(guān)狀態(tài)。愛掏網(wǎng) - it200.com如上面的例子,我們需要這樣做
history.pushState({id:1}, '我是頁面標題', url)
setVisible(url)
然后監(jiān)聽 popstate 事件,捕獲 前進/后退
window.addEventListener('popstate',function (e) {
// e.state 就是你 pushState 的時候,傳的第一個參數(shù)
let state = e.state || {}
let url = state.target.location.pathName
// 根據(jù)參數(shù) 做一些其他操作
})
最后
本文最終實現(xiàn)代碼已經(jīng)放在 github上,想要直接看效果的同學,可以上去直接copy,運行。愛掏網(wǎng) - it200.com
如果覺得本文對您有用,請給本文的github加個star,萬分感謝
另外,github上還有其他一些關(guān)于前端的教程和組件,有興趣的童鞋可以看看,你們的支持就是我最大的動力。愛掏網(wǎng) - it200.com
其他鏈接
- 用proxy實現(xiàn)一個更優(yōu)雅的vue
- 3行代碼實現(xiàn)一個簡易版promise
- vuex其實超簡單,只需3步
- vuex其實超簡單,喝完這3步,還有3步
- 從零開始徒手擼一個vue的toast彈窗組件
- JS中的繼承(上)
- JS中的繼承(下)
- JS中的原型對象
- JS中創(chuàng)建對象的方法
- moment太重? 那就試試miment--一個超輕量級的js時間庫
- 美團小程序框架mpvue入門教程
- 美團小程序框架mpvue(花名:沒朋友)蹲坑指南
- 一個基于mpvue的toast彈窗組件mptoast
原文發(fā)布時間為:2024年06月29日
本文作者:noahlam
本文來源:掘金?如需轉(zhuǎn)載請聯(lián)系原作者