利用pushState, popState和location.hash等方法自己实现一个小型路由

这篇文章主要是记录下HTML5中history提供的 pushState , replaceState API。最后通过这些API自己实现小型的路由。 关

这篇文章主要是记录下HTML5中history提供的 pushState

, replaceState

API。最后通过这些API自己实现小型的路由。

关于window.history提供的API请参见 Mozilla文档

其中 history

提供的 pushState

replaceState

2个API提供了操作浏览器历史栈的方法。

其中 pushState

:

history.pushState(data, null, '#/page=1');

pushState接收3个参数,第一个参数为一个obj,表示浏览器

第二个参数是document.title的值,一般设定为`null`

第三个参数string,用以改变 当前url

pushState

方法在改变 url

的同时向浏览器历史栈中压入新的历史记录。

接收 url

的参数为 string

类型,用以改变当前地址栏的url.需要注意的一点就是这个参数不能和跨域,即协议,域名,端口必须都是相同的,如果出现跨域的情况,即会提示:

Uncaught DOMException: Failed to execute 'pushState' on 'History': A history state object with URL 'http://www.baidu.com/' cannot be created in a document with origin 'http://commanderXL.com' and URL

Example:

打开www.baidu.com

history.pushState(null, null, '?page=1')

//地址栏变成 www.baidu.com/?page=1

history.pushState(null, null, '#page=2');

//地址栏变成 www.baidu.com/#page=2

其中 replaceState

:

history.replaceState(null, null, '#page=2');

replaceState

接收的参数 pushState

相同,但是最终的效果是:地址栏url会根据接收的参数而变化,但是浏览器并未在当浏览历史栈中增加浏览器的历史记录,而是替换当前的浏览器历史记录。

通过 pushState

replaceState

虽然能改变URL,但是不会主动触发浏览器 reload

window

对象还提供 popstate

方法:

window.addEventListener('popstate', function() {

});

这个方法用以监听浏览器在不同历史记录中进行切换,而触发相应的事件。

在浏览器提供的history对象上还有 go

, back

方法,用以模拟用户点击浏览器的前进后退按钮。在某个web应用当中,比如点击了 <a>

标签,发生了页面的跳转。这时调用 history.back()

;方法后页面回退,同时页面发生刷新,这时 window.onpopstate

无法监听这个事件。但是如果是通过 pushState

或者 replaceState

来改变URL且不发生浏览器刷新的话,再使用 history.back()

history.go()

,这样 popstate

事件会被触发。

history.pushState({page: 1}, null, '?page=1');

history.pushState({page: 2}, null, '?page=2');

history.back(); //浏览器后退

window.addEventListener('popstate', function(e) {

//在popstate事件触发后,事件对象event保存了当前浏览器历史记录的状态.

//e.state保存了pushState添加的state的引用

console.log(e.state); //输出 {page: 1}

});

PS: 通过 pushState

在url上添加 ?page=1

可以通过 location.search

去获取 search

的内容。不过如果通过 location.search

去改变 url

的话是会主动触发浏览器 reload

的。这个特性可以和下面将的关于 hash

的内容对比下。

API大致了解了,那么这些方法可以运用到哪些地方呢?一个比较常用的场景是就在单页应用中,通过这些API完成前端的路由设计,利用 pushState

, replaceState

可以改变 url

同时浏览器不刷新,并且通过 popstate

监听浏览器历史记录的方式,完成一系列的异步动作。

<a data-href="/post"></a>

<a data-href="/login"></a>

//路由

const Router = [];

const addRoute = (path = '', handle = () => {}) => {

let obj = {

path,

handle

}

Router.push(obj);

}

//添加路由定义

addRoute('/post', function() {

//do something

});

addRoute('/login', function() {

//do something

})

//路由处理

const routeHandle = (path) => {

Router.forEach((item, index) => {

if(item.path === path) {

item.handle.apply(null, [path]);

return true;

}

})

return false;

}

//拦截默认的a标签行为

document.addEventListener('click', function(e) {

let dataset = e.target.dataset;

if(dataset) {

if(routeHandle(dataset.href)) {

//阻止默认行为

e.preventDefault();

}

}

})

大致的实现思路就是,通过 <a>

添加路由信息,然后拦截 <a>

标签的默认行为,并与注册的路由信息进行匹配。若匹配成功调用对应的 handle

方法.

不过 pushState

replaceState

方法在低版本的IE浏览器下兼容性不是很好。所以可以进行降级使用 hash

来进行路由设计。

hash

请戳我

可以通过 location.hash

获取 url

上第一个 #(fragment)

及后面的内容。同时还能通过 location.hash

改写其内容,且不会主动触发浏览器 reload

。 有些功能是不是和 pushState

replaceState

一样? 所以为了兼容到低版本的浏览器,可以通过监听 #

变化来进行路由设计。

那么如何去监听呢? 比较粗暴的一种方式就是 polling

var oldHash = location.hash;

setTimeInterval(function() {

if(oldHash !== location.hash) {

//do something

oldHash = location.hash;

}

}, 100);

不过,H5还提供了一个API: hashchange

。它的就可以直接代替上面的 polling

方法,来监听 #

的变化。

window.addEventListener('hashchange', function() {

routeHandle(locaiton.hash);

});

这个小型的路由设计可以参见我的 github

.

稍微总结下:

上面主要介绍了history提供的一些API,hash的相关知识。在平时可以运用到SPA当中,Gmail就是通过hash来进行路由设计的。它相对于页面跳转来说:

  1. 页面只需要加载一次。后面的页面切换可以通过ajax去请求数据。页面体验更加流畅;

  2. 可以利用本地缓存,优化页面体验。在不同页面切换的过程中更加流畅;

  3. 可进行按需加载...

等等一些实用的好处吧。

未登录用户
全部评论0
到底啦