前端路由的两种模式

1,203 阅读5分钟

一、  hash模式

1、认识window.location

表示其链接到的对象的位置(URL)。在控制台打印window.location,可以看到有以下的属性和方法

其中window.location.hash会存有一个哈希值,这个hash是指URL中:

#之后的路径地址(包含#)

2、hash在页面中有什么作用

● 锚点定位

<script type="text/javascript">
function showNode (oNode) {
  var nLeft = 0, nTop = 0;
  for (var oItNode = oNode; oItNode; nLeft += oItNode.offsetLeft, nTop += oItNode.offsetTop, oItNode = oItNode.offsetParent);
  window.scrollTo(nLeft, nTop); 
}

function showBookmark (sBookmark, bUseHash) {
  if (arguments.length === 1 || bUseHash) {
      window.location.hash = sBookmark;
      return;
  }
  var oBookmark = document.querySelector(sBookmark);
  if (oBookmark) { showNode(oBookmark); }
}
</script>
<p id="myBookmark1">
    <span class="intLink" onclick="showBookmark('#myBookmark2');">
        Go to bookmark #2
    </span>
</p>
...
<p id="myBookmark2">
    <span class="intLink" onclick="showBookmark('#myBookmark1');">
        Go to bookmark #1
    </span>
</p>

通过以上代码,可以实现在同一个文档下,可以通过点击到达对应的书签处,那我们怎么知道当前处在哪个书签呢?可以将该书签作为一个URL的hash。

同样的场景,其实通过a标签也能实现

<nav>
    <ul>
        <li>
            <a href="#md01"> 跳转到锚点1</a>
        </li>
        <li>
            <a href="#md02"> 跳转到锚点2</a>
        </li>
    </ul>
</nav>
<main>
    <ul>
        <li id="md01">
            锚点一
        </li>
        <li id="md02">
            锚点二
        </li>
    </ul>
</main>

你会发现:在同一个文档下,指定到某一块元素,URL的hash是会变化的,但是不会对页面重新发起请求

3、hashchange()

当 URL 的片段标识符更改时,将触发hashchange事件 (跟在#符号后面的 URL 部分,包括#符号),因此可以监听hashchange去做你想做的事情

function locationHashChanged() {
  console.log('The hash has changed!')
}
window.onhashchange = locationHashChanged;

4、hash的应用

基于以上的说明,可以利用URL hash的变化不会重新发起http请求去实现页面内容的变化

<a href="#/a">A页面</a>
<a href="#/b">B页面</a>
<div id="app"></div>
<script>
  function render() {
    app.innerHTML = window.location.hash
  }
  window.addEventListener('hashchange', render)
</script>

二、  history模式

1、  认识window.history

获取History对象的引用,提供了操作浏览器会话历史(浏览器地址栏中访问的页面,以及当前页面中通过框架加载的页面)的接口。

可以看到该对象继承原型上的有关对会话历史操作的几个接口,下面来介绍几个重要的接口

2、  方法

2.1 前进、后退
back()

此异步方法转到浏览器会话历史的上一页,与用户单击浏览器的返回按钮,等价于 history.go(-1)

forward()

此异步方法转到浏览器会话历史的下一页,与用户单击浏览器的前进按钮,等价于 history.go(1)

go()

通过当前页面的相对位置从浏览器历史记录(会话记录)异步加载页面。比如:参数为 -1 的时候为上一页,参数为 1 的时候为下一页。

2.2 pushState()

会在当前的 document 中创建和激活一个新的历史记录

history.pushState(state, title[, url])
state是一个 JavaScript 对象,它与pushState()创建的新历史记录条目相关联,即传递给新URL的参数
title大多数浏览器忽略,可传递空字符串应该可以防止将来对方法的更改
url非必传,如果是有传,则相对于当前 URL 进行解析。新网址必须与当前网址同源,否则会引发异常

参考例子:

1.新建test目录下新建test.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <a href="javascript:toA();">A页面</a>
    <a href="javascript:toB();">B页面</a>
    <a href="javascript:toC();">返回</a>
    <script>C
      function toA() {
        history.pushState({a: 111}, '', '/a')
      }C
      function toB() {
        history.pushState({b: 222}, '', '/b')
      }
      function toC() {
        history.back()
      }
    </script>
  </body>
</html>

2.test目录下终端执行

npm i -g light-server

3.test目录下终端执行

light-server -s .  --port 3000

4.打开test.html

分别点击A页面,B页面,可以看到URL会发生以下变化

http://localhost:3000/test.html -- http://localhost:3000/a -- http://localhost:3000/b

每次变化后观察控制台你会发现当前页面并不会重新发起http请求

注意:

从某种程度来说,调用 pushState() 和 window.location = "#foo"基本上一样,他们都会在当前的 document 中创建和激活一个新的历史记录,但是 pushState() 有以下优势:

1.  新的 URL 可以是任何和当前 URL 同源的 URL。但是设置 window.location 只会在你只设置锚的时候才会使当前的 URL。

2.  非强制修改 URL。相反,设置 window.location = "#foo"; 仅仅会在锚的值不是 #foo 情况下创建一条新的历史记录。

3.  可以在新的历史记录中关联任何数据。window.location = "#foo"形式的操作,你只可以将所需数据写入锚的字符串中。

4.  pushState()不会造成hashchange事件调用,即使新的URL和旧的URL只是锚点的不同

2.3 replaceState()

把当前历史记录实体替换成新的记录(url有传的情况,否则保留当前记录),可以用于更新当前的 state 对象或者当前历史实体的 URL 来响应用户的的动作

history.replaceState(stateObj, title[, url]);

参数和pushState()一致

替换之后历史记录里面则不存在被替换掉的记录了,也就是对历史记录做前进,后退操作则不会定位到被替换掉的记录

2.4 popState()

前进后退操作会触发popState事件,而replaceState(), pushState()则不会触发。

如果当前处于激活状态的历史记录条目是由 history.pushState() 方法创建的或者是由 history.replaceState() 方法修改的,则 popstate 事件的 state 属性包含了这个历史记录条目的 state 对象的一个拷贝。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
   <a href="javascript:toA();">A页面</a>
    <a href="javascript:toB();">B页面</a>
    <a href="javascript:toC();">返回</a>
    <div id="app"></div>
   <script>
      function render(str) {
        app.innerHTML = window.location.pathname + str
      }
      function toA() {
        history.pushState({a: 111}, null, '/a')
        render('AAA')
      }
      function toB() {
        history.pushState({b: 222}, null, '/b')
        render('BBB')
      }
      function toC() {
        history.back()
      }
      window.onpopstate = function(event) {
        console.log(event)
        render('CCC')
      }
    </script>
  </body>
</html>

三、  hash模式和history模式的区别

1、  hash(#)的改变虽然让URL看起来不同,但是在手动刷新页面的时候,HTTP请求会自动忽略hash,请求的还是同一个URL。

2、  history模式不带有#符号,但是如果通过pushState或者replaceState改变了URL,在手动刷新的时候,HTTP请求会以当前实际的URL发送请求,这就可能出现资源路径请求404的情况,因此需要服务端做重定向。

3、  不管是hash模式还是history模式,都可以做到修改URL不重新发起HTTP页面请求