学习笔记学习笔记
主站博客
面试
前端
开发工具
Cesium
GitHub
主站博客
面试
前端
开发工具
Cesium
GitHub
  • 面试
  • 算法

    • hash哈希

      • lc-1-简-两数之和
    • top K

      • lc-215-数组中的第K个最大元素
      • lc-347-中-前 K 个高频元素
      • lc-LCR159-简-库存管理 III
    • 二叉树

      • lc-101-简-对称二叉树
      • lc-102-中-二叉树的层序遍历
      • lc-104-简-二叉树的最大深度
      • lc-105-中-从前序与中序遍历序列构造二叉树
      • lc-114-中-二叉树展开为链表
      • lc-226-简-翻转二叉树
      • lc-236-中-二叉树的最近公共祖先
    • 动态规划

      • lc-1277-中-统计全为 1 的正方形子矩阵
      • lc-221-中-最大正方形
      • lc-62-中-不同路径
      • lc-70-简-爬楼梯
      • lc-72-中-编辑距离
      • lc-746-简-使用最小花费爬楼梯
      • 背包-01背包
      • 背包-完全背包
    • 原地哈希

      • lc-33-中-搜索旋转排序数组
      • lc-442-中-数组中重复的数据
      • lc-448-简-找到所有数组中消失的数字
    • 图

      • lc-207-中-课程表
      • lc-997- 简-找到小镇的法官
    • 待分类

      • lc-11-中-盛最多水的容器
      • lc-121-简-买卖股票的最佳时机
      • lc-128-中-最长连续序列
      • lc-136-简-只出现一次的数字
      • lc-139-中-单词拆分
      • lc-152-中-乘积最大子数组
      • lc-20-简-有效的括号
      • lc-200-中-岛屿数量
      • lc-22-中-括号生成
      • lc-279-中-完全平方数
      • lc-31-中-下一个排列
      • lc-322-中-零钱兑换
      • lc-34-中-在排序数组中查找元素的第一个和最后一个位置
      • lc-39-中-组合总和
      • lc-416-中-分割等和子集
      • lc-42-难-接雨水
      • lc-437-中-路径总和 III
      • lc-438-中-找到字符串中所有字母异位词
      • lc-49-中-字母异位词分组
      • lc-53-中-最大子数组和
      • lc-55-中-跳跃游戏
      • lc-56-中-合并区间
      • lc-560-中-和为 K 的子数组
      • lc-647-中-回文子串
      • lc-79-中-单词搜索
    • 排序

      • 归并排序

        • 归并排序
      • 快排
      • 排序+双指针

        • lc- 524-中-通过删除字母匹配到字典里最长单词
        • lc-15-中-三数之和
        • lc-16-中-最接近的三数之和
        • lc-283-简-移动零
        • lc-3-中-无重复字符的最长子串
        • lc-75-中-颜色分类
      • 计数排序
    • 算法工具库js
    • 递归+回溯

      • lc-17-中-电话号码的字母组合
      • lc-77-中-组合
      • LCR-083-中-全排列-不重复
      • LCR-084-中-全排列-重复
    • 链表

      • lc-142-中-环形链表 II
      • lc-148-中-排序链表
      • lc-160-简-相交链表
      • lc-19-中-删除链表的倒数第 N 个结点
      • lc-287-中-寻找重复数
  • 手撕代码

    • 柯里化 Curring
    • h5 Evnet详解
    • promiseify化
    • script标签详解
    • vue-SSR服务端渲染
    • vue双向绑定
    • vue核心原理全解
    • 路由的实现
    • 原型链
    • 变量提升
    • 宏任务、微任务
    • 左右两栏布局
    • 异步
    • 微前端
    • 实现js的filter函数
    • 实现promise
    • 捕获、冒泡
    • js中new的本质
    • Object.xxx
    • this
    • 反射Reflect,、代理Proxy
    • 基础类型
    • 深拷贝-浅拷贝
    • 继承
    • 跨域
    • 闭包
    • 防抖-节流

路由的实现

在常用的前端框架(Vue, React 等)中,通常会有 hash 路由 和 history 路由两种路由方式。

hash 路由:

  • 监听 url 中 hash 的变化,渲染不同的内容,这种路由不向服务器发送请求,不需要服务端的支持;
  • 事件hashchange只会在 hash 发生变化时才能触发,而第一次加载页面时并不会触发这个事件,因此我们还需要监听load事件。

location属性

属性含义
location.href完整的urll
location.protocol当前URL的协议,包括 : ; 比如 https:
location.host主机名和端口号
location.hostname主机名
location.port端口号
location.pathnameurl的路径部分,从 / 开始;
location.search查询参数,从 ? 开始
location.hashhash值,从 # 开始的

示例代码

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <a href="#/">主页</a>
    <a href="#/home">home</a>
    <a href="#/index">index</a>
    <div id="content"></div>

    <script>
      /*
    URL中hash值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash部分不会被发送。
    hash值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash的切换。
    我们可以使用hashchange事件来监听hash的变化。
    */

      class Router {
        constructor({ routes }) {
          console.log(`Router constructor`)
          this.routes = routes
          this.renderPages = {}
          this.init()
          this.routes.forEach((item) => {
            console.log(`setting renderPages[${item.path}]`)
            this.renderPages[item.path] = function () {
              document.getElementById('content').innerHTML = item.component
            }
          })
        }

        init() {
          window.addEventListener('load', this.updateLocation.bind(this))
          window.addEventListener('hashchange', this.updateLocation.bind(this))
        }

        updateLocation() {
          let pathRes = window.location.hash.slice(1)
          console.log(`renderPages :${pathRes}`)
          console.log(`this.renderPages :`, this.renderPages)
          this.renderPages[pathRes]()
        }
      }

      new Router({
        routes: [
          {
            path: '',
            component: '主页',
          },
          {
            path: '/',
            component: '主页',
          },
          {
            path: '/home',
            component: 'home',
          },
          {
            path: '/index',
            component: 'index',
          },
        ],
      })
    </script>
  </body>
</html>

history 路由:

  • 监听 url 中的路径(path)变化,渲染不同的内容,这种路由不向服务器发送请求,需要客户端和服务端共同的支持;
history路由的特点:
  • url中path值的改变,不会重新加载页面。
  • 通过popstate事件可以监听到path值的变化。
注意
  • 和hash路由一样,popstate事件只会在 history 发生变化时才能触发,而第一次加载页面时并不会触发这个事件,因此我们还需要监听load事件。
  • pushState和replaceState被调用时,不会触发触发 popstate 事件的,但是我们可以使用window.dispatchEvent来添加事件。

window.history对象的常用方法

方法作用
pushState(obj, title, url)前进到指定的 url,history栈会新增一条记录,不刷新页面
replaceState(obj, title, url)用 url 替换当前的路由,history栈不会新增记录,不刷新页面
forward()前进到下一个路由,如果存在的话
back()后退到上一个路由
go(number)进入到任意一个路由,正数为前进,负数为后退

示例代码

下面代码用 live-server 插件打开

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <div>
      下面代码用 live-server插件打开
    </div>
    <div class="container">
      <nav>
        <p><button class="back">back</button></p>
        <p><button class="go">go</button></p>
        <p><button class="pushstate">pushState</button></p>
        <p><button class="replacestate">replaceState</button></p>
      </nav>
      <div id="app">
        <p class="current">当前URL:<span></span></p>
        <p class="history-len">历史记录的长度:<span></span></p>
      </div>
    </div>

    <script>
      // 声明HistoryRouter
      class HistoryRouter {
        currentUrl = "";

        constructor() {
          this.refresh = this.refresh.bind(this);
          this.addStateListener(); // 添加pushstate/replacestate监听事件

          // 监听所有history事件
          window.addEventListener("load", this.refresh, false);
          window.addEventListener("popstate", this.refresh, false);
          window.addEventListener("pushState", this.refresh, false);
          window.addEventListener("replaceState", this.refresh, false);
        }

        /**
         * window只有 load/popsate的事件,没有pushstate/replacestate的事件,这个函数做下hook监听
         */
        addStateListener() {
          const listener = function (type) {
            var orig = history[type];
            return function () {
              var rv = orig.apply(this, arguments);
              var e = new Event(type);
              e.arguments = arguments;
              window.dispatchEvent(e);
              return rv;
            };
          };
          window.history.pushState = listener("pushState");
          window.history.replaceState = listener("replaceState");
        }
        // state事件统一回调入口
        refresh(event) {
          console.log(`refresh ${location.pathname}`);
          this.currentUrl = location.pathname;
          this.emit("change", location.pathname); //触发change事件
          document.querySelector("#app span").innerHTML = location.pathname;
        }

        // 事件管理emit/on模式
        handlers = {};
        on(evName, listener) {
          this.handlers[evName] = listener;
        }
        emit(evName, ...args) {
          const handler = this.handlers[evName];
          if (handler) {
            handler(...args);
          }
        }
      }

      //创建HistoryRouter
      const router = new HistoryRouter();
      //处理change事件
      router.on("change", function (curUrl) {
        console.log(`router.on change `, curUrl);
        document.querySelector(".current span").innerHTML = curUrl;
        document.querySelector(".history-len span").innerHTML = history.length;
      });

      //为button添加事件来测试
      document.querySelector(".back").addEventListener("click", function () {
        console.log(`click back`);
        window.history.back();
      });
      document.querySelector(".go").addEventListener("click", function () {
        console.log(`click go`);
        window.history.go(1);
      });
      document
        .querySelector(".pushstate")
        .addEventListener("click", function () {
          console.log(`click pushstate`);
          const url = Math.random().toString(36).slice(-6) + ".html";
          window.history.pushState({}, "", url);
        });
      document
        .querySelector(".replacestate")
        .addEventListener("click", function () {
          console.log(`click replacestate`);
          const url = Math.random().toString(36).slice(-6) + ".html";
          window.history.replaceState({}, "", url);
        });
    </script>
  </body>
</html>

js路由

react 路由

vue 路由

在 GitHub 上编辑此页
上次更新:
贡献者: 国wei
Prev
vue核心原理全解
Next
原型链