history的pushState方法和replaceState方法

·  阅读 243
histroy.pushState(stateObj, title, URL);
histroy.replaceState(stateObj, title, URL);

history.pushState({a: 'aaaa'}, 'page2', 'xxx.html')
复制代码

history和ajax的区别:

我们设想下,当在浏览器窗口打开第一个地址,比如url1时,这时history中就有了url1这个记录,且length属性值为1,history对象中有个当前页面指针(从概念上可以这么理解)指向url1。

如果再打开一个url2页面(无论是通过在地址栏直接输入、或通过url1中的链接或js代码打开),这时history中就有了url1和url2这两个记录,是一个有序的列表,这时length属性值为2,history对象中的当前页面指针指向url2,这时url2是最新的页面,页面不可以前进,但可以后退到url1。

这时如果点击浏览器本身提供的后退按钮(或用js调用back方法),这时url1页面会被重新加载显示,history对象的length仍然为2,url1和url2组成的列表仍然不变,但history对象中的当前页面指针指向url1了,这时就不能后退但可以前进了。可以理解成一个数据结构中的双向链表机制。

通过上面的描述我们可以看出,我们说的历史记录都是指一个完整的页面请求url,而ajax并不是一个完整的页面请求,因此浏览器无法记录ajax的操作信息。

具体案例:

我们来设想这样一个应用。一个页面来显示一篇长文章,该文章内容很长,分为很多章节。我们希望页面不会一次把所有章节的内容都加载起来,而是有一个章节导航,点击每个章节链接,通过jax加载具体章节的内容,而其它页面部分不需要要变化。

我们先看下传统的实现代码(注意,这里只注意核心逻辑代码的实现,其它的页面布局等尽量简化):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test2</title>
    <style type="text/css">
    div {
        padding-bottom:100px;
    }
    </style>
    <script type="text/javascript" src="jquery.min.js"></script>
</head>
<body>
    <div style="float:left;border:1px solid red;margin:20px">
        <p><a href="javascript:;" id="section1">第1章</a></p>
        <p><a href="javascript:;" id="section2">第2章</a></p>
        <p><a href="javascript:;" id="section3">第3章</a></p>
    </div>
    <div style="float:left;border:1px solid red;margin:20px" id="content">

    </div>
    <script>
        $(function(){
           //添加链接的处理事件
           $("a").click(ajax);
           //加载默认的章节,默认显示第1章
           $("#section1").trigger("click");
        });
               
        function ajax(event){
            //实际的流程是发起ajax请求,获取内容并显示。这里为了简化,没有写实际的ajax请求。
            //这段代码应该在ajax的请求响应中编写。
            $("#content").html(this.id+"的内容");
            var title = this.id;
            document.title = title;
        }
    </script>
</body>
</html>
复制代码

在浏览器加载该页面,当我们点击不同的章节链接时,内容会跟着变化,浏览器的标题也跟着变化。但是:

1)回退、前进按钮用不了

2)当我们刷新页面时,不管当前在哪个章节,都会重新回到第一个章节。

3)地址栏的url没有变化,也意味着我们没法把某个章节的地址保存下来,以后再次打开直接显示该章节内容。

上面就是传统ajax应用的一些弊端。下面我们就来解决这些问题。

我们先给出解决代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>test2</title>
    <style type="text/css">
    div {
        padding-bottom:100px;
    }
    </style>
    <script type="text/javascript" src="jquery.min.js"></script>
</head>

<body>
    <div style="float:left;border:1px solid red;margin:20px">
        <p><a href="javascript:;" id="section1">第1章</a></p>
        <p><a href="javascript:;" id="section2">第2章</a></p>
        <p><a href="javascript:;" id="section3">第3章</a></p>
    </div>
    <div style="float:left;border:1px solid red;margin:20px" id="content">

    </div>
    <script>
        $(function(){
            //添加链接的处理事件
            $("a").click(ajax);
            //加载默认的章节
            changeContent();
            //添加popstate事件
            $(window).on("popstate",function(){
                changeContent();
            });
        });
        
        function changeContent(){
            var query = location.href.split("?")[1];
            if (!query) {
                // 如果没有查询条件,则显示默认第1个章节
                history.replaceState(null, "", 
                    location.href + "?name=" + $("#section1")[0].id);    
                changeContent();
            } else {
                //触发按钮click事件,加载内容,
                //注意不要漏了true参数,这样可以和用户直接点击触发的页面变化区别开来
                $("#"+query.split("=")[1]).trigger("click",true);       
            }    
        }
       
        function ajax(event,isPopstate){
            $("#content").html(this.id+"的内容");
            var title = this.id;
            document.title = title;
            if(!isPopstate){
                history.pushState(null, "", location.href.split("?")[0] + "?name=" + title);
            }
        }
  
    </script>
</body>
</html>
复制代码

加载上面页面,测试下,所有的问题都解决了。下面我们来解释下上述代码。

我们先看changeContent方法,该方法首先获取页面的url地址,判断该地址是否有查询条件(是否带章节信息),如果没有,认为要显示第一章节。我们利用history的replaceState方法来改变当前的url,加上name=section1的查询条件,表示是第1章。因为replaceState方法不会改变页面内容,因此还需要接着再调用changeContent方法。如果地址带了查询条件,认为已经指定显示某个章节内容,这时触发章节链接的click事件。

我们再看ajax方法,就是章节链接的click事件响应函数,为了简化,该函数没有发起实际的ajax请求,而是相当于直接处理ajax返回的结果。首先是用得到的结果更新页面(这里是直接写死的),然后更新标题,这与传统的ajax做法一样。关键的区别是,判断该方法如果是用户点击的(不是onpopstate事件处理的),就会调用history对象的pushState方法来将当前页面信息保存到history对象中,并新增一个记录信息代表ajax请求后的页面。

changeContent方法同样是onpopstate事件的处理函数,其功能就是利用获取到的url信息(保存在history记录)中,来通过ajax获取到对应的内容,让页面显示相应的信息。

从用户感知上看,就跟正常的回退、前进导致的页面切换一样。用户感觉不到是ajax请求,还以为就是多个独立的页面在切换。

总结:

调用history.pushState()或者history.replaceState()不会触发popstate事件。popstate事件只会在浏览器某些行为下触发,比如点击后退、前进按钮(或者在JavaScript中调用history.back()、history.forward()、history.go()方法)。

pushState()方法是在历史记录中增加一条新的记录。

replaceState()方法是将当前的历史记录给替换掉。

参考链接:www.cnblogs.com/51kata/p/51…

分类:
前端
标签:
分类:
前端
标签: