Javascript 前端路由的简单实现(hash模式和history模式)

1,626 阅读4分钟

「这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战

Javascript 前端路由的简单实现(hash模式和history模式)

路由是根据不同的url地址来显示不同的页面或内容的功能。 之前的路由主要是后端路由,对应不同的路由,服务端会返回相应的数据(html, json等等),然后浏览器重新解析和渲染。后端路由有一个很大的问题是路由切换的时候会频繁刷新页面,对用户体验来说并不友好。在这个背景之下,前端路由出现了,它可以实现路由切换同时不刷新页面。

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

  • hash 路由:监听 url 中 hash 的变化,渲染不同的内容,这种路由不向服务器发送请求,不需要服务端的支持;
  • history 路由:监听 url 中的路径(path)变化,渲染不同的内容,这种路由不向服务器发送请求,需要客户端和服务端共同的支持;

注意,这里的需要服务端的支持并不是指会向服务器发送请求,而是因为框架和history模式的特性,需要在服务端进行一些配置。如果服务端没有配置新更新的 url ,一刷新浏览器就会报错,因为刷新浏览器会真实地向服务器发送一个 http 的请求。因此若要使用 history 路由,需要服务端的支持。具体可以参考这一篇文章 vue-router history模式 为什么需要服务端配置以及如何配置

一、hash模式

hash模式主要是监听 url 中 hash 的变化,这里就必须提到一个关键的对象 window.location

改变hash不会触发页面跳转,因为hash链接是当前页面中的某个片段,所以如果hash有变化,那么页面将会滚动到hash所连接的位置。但是页面中如果不存在hash对应的片段,则没有任何效果。

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

可以发现 href = protocol + host(hostname+port) + pathname + search + hash
hash模式主要用到的是location.hash

hash路由的特点:

  1. url中hash值的改变,不会重新加载页面。
  2. 通过hashchange事件可以监听到hash值的变化。
  3. hash值的改变会在浏览器的访问历史中增加一条记录。

注意,

  1. 事件hashchange只会在 hash 发生变化时才能触发,而第一次加载页面时并不会触发这个事件,因此我们还需要监听load事件。
  2. 这两个事件的 event对象 是不一样的:hashchange 事件中的 event 对象有 oldURLnewURL 两个属性,可以从中提取出 preHashcurrentHash 。但是 load 事件中的 event 没有这两个属性,不过我们可以通过 location.hash 来获取到当前的 hash 路由。

1. hash模式的简单实现

//hash路由
class HashRoute {
    constructor() {
        //存储对象
        this.routes = {};
        //当前hash
        this.currentHash = ''
        //绑定this.避免监听时this指向改变
        this.freshRoute = this.freshRoute.bind(this);
        //监听
        // 页面加载事件
        window.addEventListener('load', this.freshRoute, false);
        // hashchange事件
        window.addEventListener('hashmessage', this.freshRoute, false);
    }

    // 存储
    storeRoute(path, callback) {
        this.routes[path] = callback || function () { };
    }

    // 触发
    freshRoute() {
        this.currentHash = getHash();
        let callback = this.routes[this.currentHash]
        if (typeof callback === "function") callback()
    }

    // 获取当前hash值
    getHash() {
        return location.hash.slice(1) || '/';
    }
}

二、history模式

history模式主要是监听 url 中 path 的变化,这里就必须提到两个关键的对象 window.locationwindow.history

window.history对象的常用方法

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

history路由的特点:

  1. url中path值的改变,不会重新加载页面。
  2. 通过popstate事件可以监听到path值的变化。

注意,

  1. 和hash路由一样,popstate事件只会在 history 发生变化时才能触发,而第一次加载页面时并不会触发这个事件,因此我们还需要监听load事件。
  2. pushStatereplaceState被调用时,不会触发触发 popstate 事件的,但是我们可以使用window.dispatchEvent来添加事件。

1. history模式的简单实现

//history路由
class HistoryRoutes {
    constructor() {
        //存储对象
        this.routes = {};
        //当前path
        this.currentPath = '';
        //绑定this.避免监听时this指向改变
        this.freshRoute = this.freshRoute.bind(this);
        //监听事件
        window.addEventListener('load', this.freshRoute, false);
        window.addEventListener('popstate', this.freshRoute, false);
        window.addEventListener('pushState', this.freshRoute, false);
        window.addEventListener('replaceState', this.freshRoute, false);
    }

    // 存储
    storeRoute(path, callback) {
        this.routes[path] = callback || function () { };
    }

    // 触发
    freshRoute() {
        this.currentPath = this.getState();
        this.routes[this.currentPath] && this.routes[this.currentPath]();
    }
    
    // 初始化
    init(path) {
        history.replaceState(null, null, path);
        this.routes[path] && this.routes[path]();
    }

    // 跳转
    go(path) {
        history.pushState(null, null, path);
        this.routes[path] && this.routes[path]();
    }
    
    // 获取路由路径
    getState() {
        const path = window.location.pathname;
        return path ? path : '/';
    }
}



参考文章:
前端路由hash、history原理及简单的实践
深入理解前端中的 hash 和 history 路由