前端路由Hash与History模式

2019-11-04 admin

了解SPA

现代前端项目多为单页Web应用(SPA),在单页Web应用中路由是其中的重要环节。 SPA 是 single page web application 的简称,译为单页Web应用。 简单的说 SPA 就是一个WEB项目只有一个 HTML 页面,一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转。 取而代之的是利用 JS 动态的变换 HTML 的内容,从而来模拟多个视图间跳转。

前度路由

简单的说,就是在保证只有一个 HTML 页面,且与用户交互时不刷新和跳转页面的同时,为 SPA 中的每个视图展示形式匹配一个特殊的 url。在刷新、前进、后退和SEO时均通过这个特殊的 url 来实现。 我们需要实现下满两点:

  • 改变 url 且不让浏览器像服务器发送请求。
  • 可以监听到 url 的变化
  • 可以在不刷新页面的前提下动态改变浏览器地址栏中的URL地址

hash 模式和 history 模式,就是用来实现上面功能的

Hash模式

在url后面加上#,如http://127.0.0.1:5500/前端路由/hash.html#/page1这个url后面的#/page1就是hash值

  • hash 值的变化不会导致浏览器像服务器发送请求
  • location.hash可以获取hash值
  • hashchange是hash值发生改变的调用的函数

基于以上三点我们可以写一个路由实例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <ul>
      <li><a href="#/">/</a></li>
      <li><a href="#/page1">page1</a></li>
      <li><a href="#/page2">page2</a></li>
    </ul>
    <div class="content-div"></div>
  </body>
  <script>
    class RouterClass {
      constructor() {
        this.routes = {}; // 记录路径标识符对应的cb
        this.currentUrl = ""; // 记录hash只为方便执行cb
        window.addEventListener("load", () => this.render());
        window.addEventListener("hashchange", () => this.render());
      }

      /* 初始化 */
      static init() {
        window.Router = new RouterClass();
      }

      /* 注册路由和回调 */
      route(path, cb) {
        this.routes[path] = cb || function() {};
      }

      /* 记录当前hash,执行cb */
      render() {
        this.currentUrl = window.location.hash.slice(1) || "/";
        this.routes[this.currentUrl]();
      }
    }

    RouterClass.init();
    const ContentDom = document.querySelector(".content-div");
    const changeContent = content => (ContentDom.innerHTML = content);

    Router.route("/", () => changeContent("默认页面"));
    Router.route("/page1", () => changeContent("page1页面"));
    Router.route("/page2", () => changeContent("page2页面"));
  </script>
</html>

History模式

History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。可以参考下两篇文章对history的说明 https://css-tricks.com/using-the-html5-history-api/ https://developer.mozilla.org/zh-CN/docs/Web/API/History 下面介绍在这个模式下需要用到的api

history基本api

  • history.go(n):路由跳转几步,n为2往前跳转2个页面,-2往后跳转两个页面
  • history.back():路由后退,相当于 history.go(-1),用户可点击浏览器左上角的后退按钮模拟此方法
  • history.forward():路由前进,相当于 history.go(1),用户可点击浏览器左上角的前进按钮模拟此方法

pushState()

history.pushState():添加一条路由历史记录,如果设置跨域网址则报错

history.pushState用于在浏览历史中添加历史记录,但是并不触发跳转,此方法接受三个参数,依次为: state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填nulltitle:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填nullurl:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

window的popstate事件

当活动历史记录条目更改时,将触发popstate事件。如果被激活的历史记录条目是通过对history.pushState()的调用创建的,或者受到对history.replaceState()的调用的影响,popstate事件的state属性包含历史条目的状态对象的副本。 需要注意的是调用history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back())

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <ul>
      <li><a href="/">/</a></li>
      <li><a href="/page1">page1</a></li>
      <li><a href="/page2">page2</a></li>
    </ul>
    <div class="content-div"></div>
  </body>
  <script>
    class RouterClass {
      constructor(path) {
        this.routes = {}; // 记录路径标识符对应的cb
        history.replaceState({ path }, null, path); // 进入状态
        this.routes[path] && this.routes[path]();
        window.addEventListener("popstate", e => {// 当用户点击浏览器的前进或者后退触发
            console.log(e.state)
          const path = e.state && e.state.path;
          this.routes[path] && this.routes[path]();
        });
      }

      /* 初始化 */
      static init() {
        window.Router = new RouterClass(location.pathname);
      }

      /* 注册路由和回调 */
      route(path, cb) {
        this.routes[path] = cb || function() {};
      }

      /* 跳转路由,并触发路由对应回调 */
      go(path) {

        history.pushState({ path }, null, path);
        console.log(history);
        this.routes[path] && this.routes[path]();
      }
    }

    RouterClass.init();
    const ul = document.querySelector("ul");
    const ContentDom = document.querySelector(".content-div");
    const changeContent = content => (ContentDom.innerHTML = content);

    Router.route("/", () => changeContent("默认页面"));
    Router.route("/page1", () => changeContent("page1页面"));
    Router.route("/page2", () => changeContent("page2页面"));

    ul.addEventListener("click", e => {
      console.log(e.target.tagName);
      if (e.target.tagName === "A") {
        e.preventDefault();
        Router.go(e.target.getAttribute("href"));
      }
    });
  </script>
</html>

Hash 模式和 History 模式对比

Hash 模式是使用 URL 的 Hash 来模拟一个完整的 URL,因此当 URL 改变的时候页面并不会重载。History 模式则会直接改变 URL,所以在路由跳转的时候会丢失一些地址信息,在刷新或直接访问路由地址的时候会匹配不到静态资源。因此需要在服务器上配置一些信息,让服务器增加一个覆盖所有情况的候选资源,比如跳转 index.html 什么的

hash路由 优缺点

  • 优点

    • 实现简单,兼容性好(兼容到ie8
    • 绝大多数前端框架均提供了给予hash的路由实现
    • 不需要服务器端进行任何设置和开发
    • 除了资源加载和ajax请求以外,不会发起其他请求
  • 缺点

    • 对于部分需要重定向的操作,后端无法获取hash部分内容,导致后台无法取得url中的数据,典型的例子就是微信公众号的oauth验证
    • 服务器端无法准确跟踪前端路由信息
    • 对于需要锚点功能的需求会与目前路由机制冲突

History(browser)路由 优缺点

  • 优点

    • 对于重定向过程中不会丢失url中的参数。后端可以拿到这部分数据
    • 绝大多数前段框架均提供了browser的路由实现
    • 后端可以准确跟踪路由信息
    • 可以使用history.state来获取当前url对应的状态信息
  • 缺点

    • 兼容性不如hash路由(只兼容到IE10)
    • 需要后端支持,每次返回html文档

参考文章

实例来源:https://segmentfault.com/a/1190000018081475 官方文档:https://developer.mozilla.org/en-US/docs/Web/API/History_API 优缺点比较:https://juejin.im/post/5b5ec5…

[转载]原文链接:https://segmentfault.com/a/1190000020888923

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-78814.html

文章标题:前端路由Hash与History模式

相关文章
前端问答社区成立了
由雷锋网友提供的给大家相互交流的前端问答社区正式上线了,欢迎大家来此相互交流相互学习 ...
2016-03-30
移动端网页设计经验与心得
智能手机发展确实很迅速,像今年,我的大部分工作就都在移动端网页上。 再往前些年,看到的手机版/移动版网页,限制于浏览器与手机性能,2g网络速度等 网页设计无非是蓝、黑、白,界面单调,并且要尽可能的设计简单。 现在情况就大不相同了,软件上we...
2015-12-23
前端工程师应该具备的三种思维
如果你是一个天才等级的工程师(马上可以离开),可以独立完成一个很多事情,你可以是一个怪咖,因为我相信没有一个人不会不佩服你。但现实归现实,多数人都不是天才,而我们在职场上也不是单打独斗,我们需要团队合作,需要协调和配合,需要考虑除了代码以外...
2016-01-13
vue使用watch 观察路由变化,重新获取内容
问题背景: 点击用户头像 =&gt; 进入用户个人中心,在用户个人中心里点击其他用户的头像,我希望显示被点击用户的个人中心,但只看到了路由参数在发生变化,页面内容并没有更新。如图: 页面代码如下: &lt;script&gt; exp...
2017-03-13
bootstrap table之通用方法( 时间控件,导出,动态下拉框, 表单验证 ,选中与获取信息)代码分享
1.bootstrap-table 单击单行选中 $(&#x27;#gzrwTable&#x27;).on(&#x27;click-row.bs.table&#x27;, function(e, row, $element) { $(&#x...
2017-02-17
javascript:;与javascript:void(0)区别
javascript:;直接返回undefined javascript:void(0);要去执行一次表达式“0”,然后返回undefined ...
2017-05-12
Bootstrap显示与隐藏简单实现代码
本文实例为大家分享了bootstrap显示隐藏的具体代码,供大家参考,具体内容如下 &lt;html&gt; &lt;head&gt; &lt;meta charset=&quot;utf-8&quot;&gt; &lt;meta http...
2017-03-14
性能与生态双突破 HTML5重现爆发曙光
走过早熟的WebAPP,也经历过概念性的“轻应用”,HTML5这个被视作移动互联网未来的技术标准,终于在2015年看到了爆发的曙光。1月 15日,搜狐发布基于HTML5的“手机搜狐网3.0,加上微信几天前开放HTML5接口,HTML5很可能...
2015-11-12
2015年预测:Web体验与以往的五大不同
在过去的一年,我们见证了Uber的崛起、Apple加入了可穿戴设备的竞赛中、以及诸如Facebook收购Whatapp这类大的并购事件。那么在2015年我们将看到哪些巨大的改变?这里列出了五个对未来的预测 更清洁、简单的内容 2013年...
2015-11-11
前端开发领域推荐关注的微信公众号
这篇文章分享了前端领域的多个值得关注的技术、设计、极客、创业相关微信公众号。其中有最受欢迎的热门公众号、也有专注某个技术或设计的公众号,涵盖:算法、JavaScript、Nodejs、程序员、Web前端、Linux、数据库、创业、UI设计和...
2017-03-23
回到顶部