新聞中心
??想了解更多內(nèi)容,請訪問:??

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


咨詢
建站咨詢
