Vue 通过 原生 js(Hash、Histroy) 实现一个SPA、VueRouter 的简单实现(分享)

1. 什么是 SPA

  • 之前关于 VueRouter 的文章里写过,这里就不过多叙述了 跳转目标文章

2. 通过 hash 实现

2.1 实现思路
  • 改变 url 的 Hash 值,也就是 # 后面的路径
  • 通过 hashchange 来监测变化,做出对应的操作
2.2 代码实现
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Hash</title>
    <style>
      /* 底部区域 */
      footer {
        position: fixed;
        left: 0;
        bottom: 0;
        display: flex;
        justify-content: space-between;
        width: 100%;
      }

      footer .box {
        display: flex;
        justify-content: center;
        width: 100px;
        height: 60px;
        text-align: center;
        border: 1px solid #ccc;
        box-sizing: border-box;
      }

      footer .box button {
        background-color: white;
        border: none;
        outline: none;
      }
    </style>
  </head>
  <body>
    <div class="contaienr">
      <div class="content">我是内容区域哈哈哈</div>
      <footer>
        <div class="box">
          <button onclick="to('home')">首页</button>
        </div>
        <div class="box">
          <button onclick="to('mall')">商城</button>
        </div>
        <div class="box">
          <button onclick="to('shopping')">购物车</button>
        </div>
        <div class="box">
          <button onclick="to('user')">我的</button>
        </div>
      </footer>
    </div>
    <script>
      class MyHash {
        constructor(routes) {
          this.routes = routes;
          this.watchHash();
        }

		// 改变 hash 值
        to(path) {
          location.hash = path;
        }

        // 观察 hash 的变化,做出对应操作
        watchHash() {
          window.addEventListener("hashchange", (e) => {
            let { newURL } = e;
            let path = newURL.split("#")[1];
            let { component } = this.routes.find((item) => item.path == path);
            content.innerHTML = component();
          });
        }
      }

      let content = document.querySelector(".content");

      let myHash = new MyHash([
        {
          path: "home",
          component: () => `<h1>我是首页</h1>`,
        },
        {
          path: "mall",
          component: () => `<h1>我是商城页</h1>`,
        },
        {
          path: "shopping",
          component: () => `<h1>我是购物车页</h1>`,
        },
        {
          path: "user",
          component: () => `<h1>我是用户页</h1>`,
        },
      ]);

      function to(path) {
        myHash.to(path);
      }
    </script>
  </body>
</html>

3. 通过 history 实现

3.1 实现思路
  • 在 window 上有一个 history 对象,使用该对象的 pushState、replaceState方法时,对浏览器的历史记录进行操作,页面并不会进行刷新
  • 而使用 history.gohistory.back(工具栏的后退)history.forward(工具栏的前进)时会触发 window 上的 popstate 方法,页面也不会进行刷新
3.2 代码实现
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>history</title>
    <style>
      /* 底部区域 */
      footer {
        position: fixed;
        left: 0;
        bottom: 0;
        display: flex;
        justify-content: space-between;
        width: 100%;
      }

      footer .box {
        display: flex;
        justify-content: center;
        width: 100px;
        height: 60px;
        text-align: center;
        border: 1px solid #ccc;
        box-sizing: border-box;
      }

      footer .box button {
        background-color: white;
        border: none;
        outline: none;
      }
    </style>
  </head>
  <body>
    <div class="contaienr">
      <div class="content">我是内容区域哈哈哈</div>
      <footer>
        <div class="box"><button onclick="to('home')">首页</button></div>
        <div class="box"><button onclick="to('mall')">商城</button></div>
        <div class="box"><button onclick="to('shopping')">购物车</button></div>
        <div class="box">
          <button onclick="to('user','replaceState')">我的</button>
        </div>
      </footer>
    </div>
    <script>
      class MyHistory {
        constructor(routes) {
          this.routes = routes;
          this.watchHistory();
        }

        // 由于 pushState 和 replaceState 并不会触发 popState 事件,需要手动监听重写
        rewriteApi(path, type) {
          history[type]({ path }, "", "/" + path);
          this.matchCurRoute(path);
        }

        // 监听 go,back,forward
        watchHistory() {
          window.addEventListener("popstate", (e) => {
            this.matchCurRoute(e.state.path);
          });
        }

        // 路由发送变化执行对应的操作
        matchCurRoute(path) {
          let { component } = this.routes.find((item) => item.path == path);
          content.innerHTML = component();
        }
      }

      let content = document.querySelector(".content");

      let myHistory = new MyHistory([
        {
          path: "home",
          // 点击不同的按钮修改内容区域
          component: () => `<h1>我是首页</h1>`,
        },
        {
          path: "mall",
          component: () => `<h1>我是商城页</h1>`,
        },
        {
          path: "shopping",
          component: () => `<h1>我是购物车页</h1>`,
        },
        {
          path: "user",
          component: () => `<h1>我是用户页</h1>`,
        },
      ]);

      // 默认是 push
      function to(path, type = "pushState") {
        myHistory.rewriteApi(path, type);
      }
    </script>
  </body>
</html>

4. 效果图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值