Skip to content

早期的路由都是后端实现的,直接根据 url 来 reload 页面,页面变得越来越复杂服务器端压力变大, 随着 ajax 的出现,页面实现非 reload 就能刷新数据,也给前端路由的出现奠定了基础。我们可以通过记录 url 来记录 ajax 的变化,从而实现前端路由。

本文主要讲两种主流方式实现前端路由。

Hash 模式

浏览器提供了一些 api 可以让我们获取到URL中带“#”的标识。比如 URL.hash、location.hash。

如 网址 https://www.vip.com/#drop-item-2 通过location.hash可以获取#drop-item-2

同时我们可以通过 hashchange 事件来监听hash值的改变,这样就能通过事件监听 url 中 hash 的改变从而改变特定页面元素的显示内容,从而实现前端路由。

简单实现代码如下:

html
<body>
<div id="app">
  <a href="/home">home</a>
  <a href="/about">about</a>

  <div class="router-view"></div>
</div>
<script>
  // 1.获取路由显示元素
  const routerViewEl = document.querySelector('.router-view');

  // 2.监听 hashchange 事件
  window.addEventListener('hashchange', () => {
    // 3.判断 hash 的改变值,修改路由显示元素的 innerHTMl
    switch (location.hash) {
      case '#/home':
        routerViewEl.innerHTML = 'Home';
        break;
      case '#/about':
        routerViewEl.innerHTML = 'about';
        break;
      default:
        routerViewEl.innerHTML = 'default';
    }
  });
</script>
</body>

另外一个例子

切换hash,执行对应名称的方法,达到修改背景色的目的

History API

History 接口是 HTML5 新增的, 它有六种模式改变 URL 而不刷新页面。

  • pushState:使用新的路径;
  • replaceState:替换原来的路径;
  • popState:路径的回退;
  • go:向前或向后改变路径;
  • forward:向前改变路径;
  • back:向后改变路径;

其中比较重要的两个 api 是 pushState 和 replaceState 是比较重要的,是实现 history 模式的重要 api。

这两个 API 都接收三个参数,分别是

  • 状态对象(state object) — 一个JavaScript对象,与用pushState()方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,popstate事件都会被触发,并且事件对象的state属性都包含历史记录条目的状态对象的拷贝。
  • 标题(title) — FireFox浏览器目前会忽略该参数,虽然以后可能会用上。考虑到未来可能会对该方法进行修改,传一个空字符串会比较安全。或者,你也可以传入一个简短的标题,标明将要进入的状态。
  • 地址(URL) — 新的历史记录条目的地址。浏览器不会在调用pushState()方法后加载该地址,但之后,可能会试图加载,例如用户重启浏览器。新的URL不一定是绝对路径;如果是相对路径,它将以当前URL为基准;传入的URL与当前URL应该是同源的,否则,pushState()会抛出异常。该参数是可选的;不指定的话则为文档当前URL。

首先我们用 pushState 来简单实现下,代码如下:

html
<body>
<div id="app">
  <a href="/home">home</a>
  <a href="/about">about</a>

  <div class="router-view"></div>
</div>
<script>
  // 1.获取路由显示元素
  const routerViewEl = document.querySelector('.router-view');

  // 2.获取所有路由跳转元素
  const aEls = document.getElementsByTagName('a');
  // 3.遍历所有 a 元素,注册事件监听点击
  for (let aEl of aEls) {
    aEl.addEventListener('click', (e) => {
      // 4.阻止默认跳转
      e.preventDefault();
      // 5.获取 href 属性
      const href = aEl.getAttribute('href');
      // 6.执行 history.pushState
      history.pushState({}, '', href);
      // 
      // history.replaceState({}, '', href);

      // 7.判断 pathname 路径的改变
      switch (location.pathname) {
        case '/home':
          routerViewEl.innerHTML = 'Home';
          break;
        case '/about':
          routerViewEl.innerHTML = 'about';
          break;
        default:
          routerViewEl.innerHTML = 'default';
      }
    });
  }
</script>
</body>

如果把代码改成 replaceState 实现。那么就不能操作浏览器上面的前进后退操作。

对比

hash 方案更常见些,也是前端框架,如Vue,Angular的默认路由模式, hash模式的缺点就是路径比较丑,总是多了一个#!,优点是浏览器兼容性强

history模式是,URL就像一个正常的url,例如http://yoursite.com/user/id,更顺眼些。 缺点是需要后台配置支持,否则可能会出现404的情况

所以呢,使用history模式要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

具体配置方法

参考

https://segmentfault.com/a/1190000007238999

https://www.cnblogs.com/cqkjxxxx/p/15253331.html

https://router.vuejs.org/zh/guide/essentials/history-mode.html