10

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

 3 years ago
source link: https://segmentfault.com/a/1190000007166839
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

发布于 2016-10-14

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

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

其中history提供的pushStatereplaceState2个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会根据接收的参数而变化,但是浏览器并未在当浏览历史栈中增加浏览器的历史记录,而是替换当前的浏览器历史记录。

通过pushStatereplaceState虽然能改变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方法.

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

hash请戳我

可以通过location.hash获取url上第一个#(fragment)及后面的内容。同时还能通过location.hash改写其内容,且不会主动触发浏览器reload。 有些功能是不是和pushStatereplaceState一样? 所以为了兼容到低版本的浏览器,可以通过监听#变化来进行路由设计。

那么如何去监听呢? 比较粗暴的一种方式就是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. 可进行按需加载...

等等一些实用的好处吧。

项目地址请戳我


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK