新聞中心
??想了解更多內(nèi)容,請(qǐng)?jiān)L問:??

??和華為官方合作共建的鴻蒙技術(shù)社區(qū)??
??https://ost.??
路由就是指隨著瀏覽器地址欄的變化,展示給用戶的頁面也不相同。
傳統(tǒng)的網(wǎng)頁根據(jù)用戶訪問的不同的地址,瀏覽器從服務(wù)器獲取對(duì)應(yīng)頁面的內(nèi)容展示給用戶。這樣容易造成服務(wù)器壓力比較大,而且用戶訪問速度也比較慢,在這種場景下,出現(xiàn)了單頁應(yīng)用。
路由的實(shí)現(xiàn)方式
- location.hash+hashchange事件。
- history.pushState()+popState事件。
實(shí)現(xiàn)主要基于以下幾個(gè)方面的特性
- URL 中的 hash 值只是客戶端的一種狀態(tài),也就是說當(dāng)向服務(wù)器發(fā)出請(qǐng)求時(shí),hash 部分不會(huì)被發(fā)送。
- hash 值的改變,都會(huì)在瀏覽器的訪問歷史中增加一個(gè)記錄,因此我們能通過瀏覽器的回退,前進(jìn)按鈕控制 hash 的切換。
- 可以通過設(shè)置a標(biāo)簽,并通過設(shè)置 href 屬性,例如href = ‘#/blue’,當(dāng)點(diǎn)擊標(biāo)簽的時(shí)候,url的 hash 。值會(huì)發(fā)生改變,在當(dāng)前url的后面增加上’#/blue’, 同時(shí)觸發(fā)hashchange,再回調(diào)函數(shù)中進(jìn)行處理。
- 前進(jìn)后退的時(shí)候,可以直接通過js來對(duì) location.hash 進(jìn)行賦值,改變url的 hash 值,例如 location.hash = ‘#/blue’即可,此時(shí)url會(huì)改變, 也會(huì)觸發(fā)hashchange事件。
- 因此我們可以使用 hashchange 事件來監(jiān)聽 hash 值得變化,從而對(duì)頁面進(jìn)行跳轉(zhuǎn)(渲染)。
hash模式
hash方法是在路由中帶有一個(gè)#,主要原理是通過監(jiān)聽#后的 URL 路徑標(biāo)識(shí)符的更改而觸發(fā)的瀏覽器hashchange事件,然后通過獲取location.hash 得到當(dāng)前的路徑標(biāo)識(shí)符,再進(jìn)行一些路由跳轉(zhuǎn)的操作。hash方法的push本身會(huì)記錄你的點(diǎn)擊記錄,當(dāng)你想要通過返回按鈕返回時(shí),它會(huì)根據(jù)你的點(diǎn)擊記錄用到replace來解決。
先建立一個(gè)index.html文件,在body標(biāo)簽中開始hash的編寫:
// index.html
前端路由
hello , Hash router!!!
然后引用js文件處理router里面的邏輯:
// hash.js
class Router {
constructor() {
/**
* 以鍵值對(duì)的形式存儲(chǔ)路由
*/
this.routers = new Object();
/**
* 當(dāng)前路由的URL
*/
this.currentUrl = "";
/**
* 記錄出現(xiàn)過的hash
*/
this.history = [];
/**
* 作為指針,默認(rèn)指向this.history的末尾,根據(jù)后退前進(jìn)指向history中不同的hash
*/
this.currentIndex = this.history.length - 1;
/**
* 默認(rèn)不是后退操作
*/
this.isBack = false;
}
/**
* 都定義在原型上,后面的覆蓋前面的,這個(gè)不執(zhí)行
*/
route(path, callback) {
console.log(1);
}
}
/**
* 將路由的hash以及對(duì)應(yīng)的callback函數(shù)儲(chǔ)存
* @param {*} path
* @param {*} callback
*/
Router.prototype.route = function (routes) {
for (let route of routes) {
this.routers[route.path] = route.callback || function () { };
}
};
/**
* 當(dāng)頁面刷新的時(shí)候
*/
Router.prototype.refresh = function () {
/**
* 獲取當(dāng)前頁面中的hash路徑
*/
this.currentUrl = window.location.hash.slice("1") || "/";
/**
* 不是后退才執(zhí)行
*/
if (!this.isBack) {
if (this.currentIndex < this.history.length - 1)
this.history = this.history.slice(0, this.currentIndex + 1);
/**
* 將當(dāng)前hash路由推入數(shù)組儲(chǔ)存,指針向前移動(dòng)
*/
this.history.push(this.currentUrl);
this.currentIndex++;
}
this.isBack = false;
/**
* 執(zhí)行當(dāng)前hash路徑的回調(diào)函數(shù)
*/
this.routers[this.currentUrl]();
console.log("refresh");
console.log(this.history);
console.log(this.currentIndex);
};
/**
* 當(dāng)頁面后退,回退的過程中會(huì)觸發(fā)hashchange,將hash重新放入,索引增加
*/
Router.prototype.back = function () {
console.log("back");
console.log(this.history);
console.log(this.currentIndex);
// 后退操作設(shè)置為true
this.isBack = true;
/**
* 如果指針小于0的話就不存在對(duì)應(yīng)hash路由了,因此鎖定指針為0即可
*/
this.currentIndex <= 0
? (this.currentIndex = 0)
: (this.currentIndex = this.currentIndex - 1);
/**
* 隨著后退,location.hash也應(yīng)該隨之變化
* 并執(zhí)行指針目前指向hash路由對(duì)應(yīng)的callback
*/
location.hash = `#${this.history[this.currentIndex]}`;
this.routers[this.history[this.currentIndex]]();
};
/**
* 初始化,監(jiān)聽頁面的加載與hash值的變化
*/
Router.prototype.init = function () {
/**
* 修改this指向,否則指向window
*/
window.addEventListener("load", this.refresh.bind(this), false);
window.addEventListener("hashchange", this.refresh.bind(this), false);
};
// 監(jiān)聽hash模式路由
Router.prototype.eventHashRouter = function() {
// 監(jiān)聽load事件,防止刷新頁面數(shù)據(jù)丟失
window.addEventListener("load", this.hashRouter.bind(this));
window.addEventListener("hashchange", this.hashRouter.bind(this))
}
//replace模式頁面跳轉(zhuǎn)
Router.prototype.replace = function(url) {
url = "#" +url;
window.location.replace(url);
}
const route = new Router();
/**
* 初始化
*/
route.init();
const routes = [
{
path: "/",
callback: function () {
let el = document.body;
el.style.backgroundColor = "#fff";
},
},
{
path: "/blue",
callback: function () {
let el = document.body;
el.style.backgroundColor = "blue";
},
},
{
path: "/green",
callback: function () {
let el = document.body;
el.style.backgroundColor = "green";
},
},
{
path: "/red",
callback: function () {
let el = document.body;
el.style.backgroundColor = "red";
},
},
{
path: "/orange",
callback: function () {
let el = document.body;
el.style.backgroundColor = "orange";
},
},
];
/**
* 將hash值與cb綁定
*/
route.route(routes);
window.onload = function () {
let btn = document.getElementById("btn");
btn.addEventListener("click", route.back.bind(route), false);
};
history模式
HistoryAPI來實(shí)現(xiàn)URL的變化,其中最主要用history.pushState()新增一個(gè)歷史記錄,用history.replaceState()直接替換當(dāng)前歷史記錄,可以在不進(jìn)行刷新的情況下,操作瀏覽器的歷史記錄。需要后臺(tái)配置支持,因?yàn)槲覀兊膽?yīng)用是個(gè)單頁的客戶端應(yīng)用,如果后臺(tái)沒有正確的配置,當(dāng)用戶在瀏覽器直接訪問一些沒有配置的路徑就會(huì)返回404,但因?yàn)闆]有#號(hào),所以當(dāng)用戶刷新頁面之類的操作時(shí),瀏覽器還是會(huì)給服務(wù)器發(fā)送請(qǐng)求。為了避免出現(xiàn)這種情況,所以這個(gè)實(shí)現(xiàn)需要服務(wù)器的支持,需要定向到根路徑。html5 提供了historyAPI來實(shí)現(xiàn)URL的變化,其中最主要的 API 有以下兩個(gè):
- history.pushState()新增一個(gè)歷史記錄。
- history.replaceState() 直接替換當(dāng)前歷史記錄。
- 相同點(diǎn):可以在不進(jìn)行刷新的情況下,操作瀏覽器的歷史記錄。
- 先建立一個(gè)index.html文件,在body標(biāo)簽中開始history的編寫:
前端路由
hello , History router!!!
然后引用js文件處理router里面的邏輯:
// history.js
/**
* history路由
*/
class Router {
constructor() {
/**
* 以鍵值對(duì)的形式存儲(chǔ)路由
*/
this.routers = new Object();
}
}
/**
* 監(jiān)聽頁面的popstate事件
*/
Router.prototype.bindPopState = function (e) {
const path = e.state && e.state.path;
this.routers[path] && this.routers[path]();
};
/**
* 將路由的path以及對(duì)應(yīng)的callback函數(shù)儲(chǔ)存
* @param {*} path
* @param {*} callback
*/
Router.prototype.route = function (routes) {
for (let route of routes) {
this.routers[route.path] = route.callback || function () { };
}
};
/**
* 初始化,直接替換當(dāng)前歷史紀(jì)錄,并用狀態(tài)對(duì)象進(jìn)行存儲(chǔ)
*/
Router.prototype.init = function (path) {
window.history.replaceState({ path: path }, null, path);
this.routers[path] && this.routers[path]();
/**
* 加入事件監(jiān)聽
*/
window.addEventListener("popstate", this.bindPopState.bind(this), false);
};
/**
* 更新頁面,新增一個(gè)歷史紀(jì)錄
*/
Router.prototype.go = function (path) {
window.history.pushState({ path: path }, null, path);
this.routers[path] && this.routers[path]();
};
const route = new Router();
route.init(window.location.href);
const routes = [
{
path: "http://127.0.0.1:5500/",
callback: function () {
let el = document.body;
el.style.backgroundColor = "#fff";
},
},
{
path: "http://127.0.0.1:5500/color/blue",
callback: function () {
let el = document.body;
el.style.backgroundColor = "blue";
},
},
{
path: "http://127.0.0.1:5500/color/green",
callback: function () {
let el = document.body;
el.style.backgroundColor = "green";
},
},
{
path: "http://127.0.0.1:5500/color/red",
callback: function () {
let el = document.body;
el.style.backgroundColor = "red";
},
},
{
path: "http://127.0.0.1:5500/color/orange",
callback: function () {
let el = document.body;
el.style.backgroundColor = "orange";
},
},
];
/**
* 將hash值與cb綁定
*/
route.route(routes);
/**
* a標(biāo)簽會(huì)跳轉(zhuǎn)頁面,阻止
*/
window.addEventListener(
"click",
function (e) {
var e = e || window.event;
var target = e.target || e.srcElement;
if ((target.tagName = "A")) {
e.preventDefault();
route.go(e.target.getAttribute("href"));
}
},
false
);
總結(jié)
本文講解了路由的核心實(shí)現(xiàn)原理,但是結(jié)合具體框架后,框架增加了很多特性,如動(dòng)態(tài)路由、路由參數(shù)、路由動(dòng)畫等等,這些導(dǎo)致路由實(shí)現(xiàn)變的復(fù)雜。本文去粗取精只針對(duì)前端路由最核心部分的實(shí)現(xiàn)進(jìn)行分析,并基于 hash 和 history 兩種模式,頁面加載時(shí),它可能有一個(gè)非空狀態(tài)對(duì)象。例如,如果頁面設(shè)置了一個(gè)狀態(tài)對(duì)象(使用pushState()or replaceState())然后用戶重新啟動(dòng)他們的瀏覽器,當(dāng)頁面重新加載時(shí),頁面將收到一個(gè)onload事件,雖沒有popstate事件,但是將獲得加載的狀態(tài)對(duì)象。
??想了解更多內(nèi)容,請(qǐng)?jiān)L問:??
??和華為官方合作共建的鴻蒙技術(shù)社區(qū)??
??https://ost.??
文章名稱:前端路由與單頁頁面實(shí)踐
網(wǎng)頁URL:http://m.5511xx.com/article/dpoidhc.html


咨詢
建站咨詢
