[JavaScript进阶] 路由跳转原理 之 Hash 模式

这篇具有很好参考价值的文章主要介绍了[JavaScript进阶] 路由跳转原理 之 Hash 模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

路由跳转原理 之 Hash

一. 路由跳转的原理

首先讲讲路由跳转的原理, 其实没有什么神秘的, 以变量类比:

// 首先定义一个变量名为 container , 赋予初始值 'index'
let container = 'index';

// 监听一个点击事件
window.addEventListener('click', (e) => {
  // 当点击事件的触发元素的 id 为 'index' 的时候
	if (e.target.id === 'index') {
    // 改变变量的值为 'index'
  	container = 'index';
  }
  // 当点击事件的触发元素的 id 为 'news' 的时候
  else if (e.target.id === 'news') {
		// 改变变量的值为 'news'
		container = 'news';
  }
});

在上文的代码中, 在监听到点击事件的时候, 会改变变量的值. 那么, 如果不再监听点击事件, 而是 监听页面路径改变 ; container 也不是一个变量而是一个HTML元素, 当监听回调触发时, 修改的是这个 container 元素内部的HTML片段, 那么其实就是路由跳转了:

// 定义一套路由
const ComponentIndex = `<div>Index Page</div>`;
const ComponentNews = `<div>News Page</div>`;

// 获取 `container` 容器
let container = document.querySelector('#container');
// 赋予 `container` 容器初始值
container.innerHTML = ComponentIndex;

// 监听一个页面跳转事件 (不存在 pageURLChange 事件, 仅作为一个示例)
window.addEventListener('pageURLChange', (e) => {
  // 假设传入的回调参数就是跳转的页面
  
  // 当跳转的页面是 'index' 时
	if (e === 'index') {
    // 将 container 容器的内部HTML片段修改为Index路由的内容
  	container.innerHTML = ComponentIndex;
  }
  // 当跳转的页面是 'news' 时
  else if (e === 'news') {
		// 将 container 容器的内部HTML片段修改为News路由的内容
		container.innerHTML = ComponentNews;
  }
});

二. Hash跳转的实现原理

路由跳转目前主要有两种, hash 模式和 history 模式, 这其实是对应了 JavaScript 中两种无刷新改变网页URL的方式: location.hashhistory.pushState . 本文主要讲的就是第一种: Hash模式 .

Hash 模式和 History 模式原理都是一样的, 不过是监听页面路径跳转的方式不同而已.

History模式等有空了会写的...(咕

2.1 Hash是什么

URL 路径中可以存在 锚点 , 通过一个符号 # 表示. 当 URL 中存在锚点的时候, 锚点后面的字符串将不会被请求上服务器, 仅作为本地浏览器数据访问, 这个值被称为 Hash 值.

比如, 下面这两串网址访问百度服务器的时候, 百度都只会接收到 https://www.baidu.com/ 这一串地址请求, Hash 值并不会通过网络请求发送给服务器.

https://www.baidu.com
https://www.baidu.com#12345

[JavaScript进阶] 路由跳转原理 之 Hash 模式

关于锚点的概念, 其实在初学 HTML 的时候就接触到了: 在学习锚元素 <a> 的时候其实就已经了解过了, 当时讲的是 a 元素可以通过 #id 跳转至页面的某一个 id 的位置, 这本质上就是利用到了锚点.

参考文档: [MDN - 文本片段]

2.2 改变 hash 的原理

通过 location.hash 属性, 可以更改页面的 Hash , 并且不会刷新页面只改变 URL 路径; 当 Hash 改变的时候, 会触发一个 hashchange 事件, 以此我们就可以通过监听 hashchange 事件事件去改变页面的内容了.

同时, 当页面 Hash 改变的时候, 也会向浏览器的访问历史添加一个记录, 所以也可能通过 history.go() 去控制页面访问历史.

因为这两个API都比较简单所以就不单独列出来说明了, 可以自行参照文档阅读. 直接看下文代码也是可以的, 都是一些很基础的应用并且我会作出一定的说明.

参考文档:

  • [MDN - Location: Hash]
  • [MDN - HashChangeEvent]

2.3 通过 hashchange 事件监听页面路径改变

2.3.1 location.hash

当 URL 中没有锚点的时候, 直接输出会输出空字符串:

/* URL: www.baidu.com */

console.log(location.hash);
// -> ''

当向没有锚点的 URL 改变 hash 时, 会自动添加锚点:

输入时不用添加锚点, 但是输出时会输出锚点(见下例).

/* URL: www.baidu.com */

location.hash = 'index';
/* URL: www.baidu.com#index */

console.log(location.hash);
// -> '#index'

通常为了让路由跳转后的 URL 更像一个地址, 我们会在 Hash 前添加一个斜杠 / :

/* URL: www.baidu.com */

location.hash = '/index';
/* URL: www.baidu.com#/index */

2.3.2 hashchange 事件

当页面中的 Hash 发生改变的时候, 会触发事件 hashchange , 在事件的回调参数 e 中有两个可以利用到的属性: e.newURLe.oldURL . 见名思意, 分别是改变后的 URL 和改变前的 URL .

// 监听 hash 改变事件
window.addEventListener('hashchange', (e) => {
  // 防止重复跳转
  if (e.newURL === e.oldURL) {
      return;
  }
  
  /*
   * 判断完重复跳转的情况之后, 直接使用页面的 `location.hash` 就可以了
   * e.newURL 是一个字符串, 获取 hash 还需要额外处理
   * 之前说过输出 hash 的时候会输出锚点 所以通过 `.slice()` 方法将第一个锚点符号删除.
   */
  console.log(location.hash.slice(1))
}

/* URL: www.baidu.com */

location.hash = '/index';
/* URL: www.baidu.com#/index */
// -> /index

其实这个回调参数是可以不使用的, 因为如果 e.newURL === e.oldURL , Hash 根本不会发生改变, hashchange 事件也不会触发, 这里仅仅只是作为一个 示例 .

2.4 通过 history.go() 控制页面访问历史

每一次调用 location.hash 都会往浏览器中写入一条历史记录, 理所应当 history.go() 也能控制 Hash 改变产生的历史记录:

/* URL: www.baidu.com */

location.hash = '/index';
/* URL: www.baidu.com#/index */

location.hash = '/news';
/* URL: www.baidu.com#/news */

history.go(-1);
/* URL: www.baidu.com#/index */

history.go(-1);
/* URL: www.baidu.com */

三. 实现一个 HashRouter 库

在前文我们已经对 Hash 模式的路由跳转进行了简单的剖析, 现在可以试着做一个简易的 Router 路由跳转库了.

3.1 规范

首先, 我们需要对一些格式进行一定的规范, 这样我们就可以基于这些规范写一个标准库:

3.1.1 HTML 规范

对于 HTML , 我们使用 dataset 进行标记:

  • data-router-link-container: 表示一个路由跳转容器.
    • data-router-link: 表示一个路由跳转链接, 该属性的值就是跳转的路由地址.
  • data-router-view: 表示一个路由内容显示容器, 路由跳转后显示的内容会在该元素内显示.

3.1.2 JavaScript 规范

编写一个 HashRouter 类, 构造函数的参数 options 的类型为:

options = {
	routes: Array<{
    path: string, 
    component: {
    	template: string,
    }
  }>
}
  • routes: 路由数组
    • path: 路由的路径
    • component: 路由组件的内容
      • template: 路由组件的 HTML 片段

参考文档:

  • [HTMLElement.dataset]

3.2 编写库

这部分内容就简单讲了, 内容都在代码块中, 主要就是一个元素获取以及 dataset 的值获取.

下面的方法都是类 HashRouter 中的方法:

3.2.1 跳转路由

/**
 * 监听路由跳转容器点击, 跳转路由
 *  绑定一个具有 [data-router-link-container] 属性的容器, 监听这个容器内冒泡出来的 `click` 事件
 *  当 `click` 事件触发时, 判断触发的元素是否有 `data-router-link` 属性
 *  如果存在, 则改变当前页面的 Hash 为 `data-router-link` 属性的值
 */
bindRouterLinkEvent() {
    // 找到具有 [data-router-link-container] 属性的容器
    document.querySelector('[data-router-link-container]')
        // 监听容器内冒泡出来的 click 事件
        ?.addEventListener('click', (e) => {
            // 排除非 [data-router-link] 属性的容器
            if (!e.target.dataset['routerLink']) {
                return;
            }

            // 阻止标签跳转
            e.preventDefault();

            // 更改页面 Hash
            window.location.hash = `${e.target.dataset['routerLink']}`
        });
}

3.2.2 监听路由跳转

/**
 * 监听 URL hash 的改变, 并且更新 [data-router-view] 容器内的 HTML 片段.
 */
listenHashChange() {
    window.addEventListener('hashchange', () => {
        // 寻找跳转路径
        const route = this.routes.find(
            route => route.path === window.location.hash.slice(1)
        );

        // 如果找不到跳转路径, 报错
        if (!route) {
            console.error('找不到跳转路径');
            return;
        }

        // 改变 [data-router-view] 容器内的 HTML 片段
        const viewContainer = document.querySelector('[data-router-view]');
        if (viewContainer) {
            viewContainer.innerHTML = route.component.template;
        }
    })
}

3.2.3 浏览历史操作

/**
 * 历史记录跳转
 *
 * @param {number} step - 跳转的步数
 *
 * @example HashRouter.go(1) 前进一步历史
 * @example HashRouter.go(-1) 后退一步历史
 */
go(step) {
    history.go(step);
}

/**
 * 历史记录跳转 - 后退一步
 */
back(){
    this.go(-1);
}

/**
 * 历史记录跳转 - 前进一步
 */
forward(){
    this.go(1);
}

3.3 HashRouter.js

/* HashRouter.js */
class HashRouter {
    /**
     * @constructor
     * @param {{routes: [{path: string, component: {template: string}}]}} options
     * */
    constructor(options) {
        this.routes = options.routes;
        this.bindRouterLinkEvent();
        this.listenHashChange();
    }

    /**
     * 匹配路由
     *  绑定一个具有 [data-router-link-container] 属性的容器, 监听这个容器内冒泡出来的 `click` 事件
     *  当 `click` 事件触发时, 判断触发的元素是否有 `data-router-link` 属性
     *  如果存在, 则改变当前页面的 Hash 为 `data-router-link` 属性的值
     */
    bindRouterLinkEvent() {
        // 找到具有 [data-router-link-container] 属性的容器
        document.querySelector('[data-router-link-container]')
            // 监听容器内冒泡出来的 click 事件
            ?.addEventListener('click', (e) => {
                // 排除非 [data-router-link] 属性的容器
                if (!e.target.dataset['routerLink']) {
                    return;
                }

                // 阻止标签跳转
                e.preventDefault();

                // 更改页面 Hash
                window.location.hash = `${e.target.dataset['routerLink']}`
            });
    }

    /**
     * 监听 URL hash 的改变, 并且更新 [data-router-view] 容器内的 HTML 片段.
     */
    listenHashChange() {
        window.addEventListener('hashchange', () => {
            // 寻找跳转路径
            const route = this.routes.find(
                route => route.path === window.location.hash.slice(1)
            );

            // 如果找不到跳转路径, 报错
            if (!route) {
                console.error('找不到跳转路径');
                return;
            }

            // 改变 [data-router-view] 容器内的 HTML 片段
            const viewContainer = document.querySelector('[data-router-view]');
            if (viewContainer) {
                viewContainer.innerHTML = route.component.template;
            }
        })
    }

    /**
     * 历史记录跳转
     *
     * @param {number} step - 跳转的步数
     *
     * @example HashRouter.go(1) 前进一步历史
     * @example HashRouter.go(-1) 后退一步历史
     */
    go(step) {
        history.go(step);
    }

    /**
     * 历史记录跳转 - 后退一步
     */
    back(){
        this.go(-1);
    }

    /**
     * 历史记录跳转 - 前进一步
     */
    forward(){
        this.go(1);
    }
}

export default HashRouter;

3.4 示例

目录结构:

| HashRouter.js

| index.html

<!-- index.html -->
<nav class="route-nav" data-router-link-container>
    <a class="toPageIndex" data-router-link="/index">Index</a>
    <a class="toPageNews" data-router-link="/news">News</a>
</nav>
<hr>
<div data-router-view></div>
<script type="module">
  	// 引入 HashRouter.js
    import HashRouter from './HashRouter.js';

    // 声明路由模板
    const IndexPage = {
        template: '<div>IndexPage</div>'
    }
    const NewsPage = {
        template: '<div>NewsPage</div>'
    }

    // 注册路由
    new HashRouter({
        routes: [
            {
                path: '/',
                component: IndexPage
            },
            {
                path: '/index',
                component: IndexPage
            },
            {
                path: '/news',
                component: NewsPage
            }
        ]

    })
</script>

[JavaScript进阶] 路由跳转原理 之 Hash 模式

[JavaScript进阶] 路由跳转原理 之 Hash 模式

3.5 一些问题

HashRouter.js 存在的一些问题, 提供思考, 感兴趣的也可以想一想如何改造 HashRouter.js 使其功能更加强大:文章来源地址https://www.toymoban.com/news/detail-746033.html

  • 无法传参
  • 无法实现子路由
  • 无法通过函数跳转路由
  • ...

到了这里,关于[JavaScript进阶] 路由跳转原理 之 Hash 模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 路由的hash和history模式的区别

    目录 ✅ 路由模式概述 一. 路由的hash和history模式的区别 1. hash模式 2. history模式 3. 两种模式对比 二. 如何获取页面的hash变化 单页应用是在移动互联时代诞生的,它的目标是不刷新整体页面,通过地址栏中的变化来决定内容区域显示什么内容。 要达成这个目标,我们要用到前

    2024年02月14日
    浏览(29)
  • vue3切换路由模式——Hash 、histoary

    1、history模式 使用createWebHistory 2、hash模式 使用createWebHashHistory 综上所述: history 对应 createWebHistory hash 对应 createWebHashHistory

    2024年02月02日
    浏览(34)
  • hash 模式和 history 模式的实现原理

    #后面的 hash 值的变化不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新页面。通过监听 hashchange 事件的变化可以知道 hash 值发生了哪些变化,然后根据 hash 值的变化来实现更新页面部分内容的操作 history 模式的实现主要是通过 HTML5 标准发布的两个 api,pus

    2024年02月14日
    浏览(27)
  • vue路由的两种模式 hash与history

    Vue 路由是 Vue.js 框架提供的一种机制,用于实现单页面应用(Single-Page Application,简称 SPA)中的前端路由功能。它允许通过定义不同的路由路径和对应的组件,来管理应用程序中不同页面或视图的展示和切换。 Vue 路由使用了浏览器的 History API 或 hash(#)来实现路由导航。通

    2024年02月10日
    浏览(40)
  • 路由hash模式改成history模式的前端vue配置与后端配置

    示例项目地址:http://172.00.00.000:8888/Web/static/index.html/index ,其中: /Web/static/ 表示项目部署路径,每个人的路径不一样,vue默认路径是根路径/,如果你项目不是部署在根路径,那就需要修改一些配置 index.html 表示项目入口文件 /index 表示项目首页的路由地址 vue前端配置 1.配置路

    2024年02月15日
    浏览(38)
  • vue3如何切换hash/history两种路由模式

    本文介绍了在vue3中,如何使用history和hash两种路由模式的方法 1、history模式 使用createWebHistory 2、hash模式 使用createWebHashHistory 综上所述: history 对应 createWebHistory hash 对应 createWebHashHistory

    2024年02月13日
    浏览(31)
  • Vue学习笔记 之 History 路由 和 Hash 路由的区别 及 History 模式时,Nginx的配置方式

    一、History 模式、Hash 模式   Vue Router 是 Vue.js 官方的路由管理器,用于构建单页应用的前端路由。它允许你通过定义路由配置来映射不同的 URL 到对应的组件,实现页面间的跳转和导航。Vue Router 支持两种路由模式:history 模式和 hash 模式。 1、History 模式   在 History 模式

    2024年02月07日
    浏览(30)
  • react 11之 router6路由 (两种路由模式、两种路由跳转、两种传参与接收参数、嵌套路由,layout组件、路由懒加载)

    npm i react-router-dom 两种模式 Router:所有路由组件的根组件,包裹路由的最外层容器 Link:跳转路由组件 Routes :用于定义和渲染路由规则( 用于替换 Switch 组件) Route:路由规则匹配组件,显示当前规则对应的组件 exact = 精确匹配,只有当 path 和 pathname 完全匹配时才会展示该路由

    2024年02月12日
    浏览(32)
  • Redis进阶底层原理 - 高可用哨兵模式

    Redis底层原理篇

    2024年02月17日
    浏览(24)
  • Vue入门六(前端路由的概念与原理|Vue-router简单使用|登录跳转案例|scoped样式|混入(mixin)|插件)

    路由(英文:router)就是对应关系 SPA指的是一个web网站只有一个唯一的一个HTML页面, 所有组件的展示与切换 都在唯一的一个页面内完成。 此时, 不同组件之间的切换 需要通过 前端路由 来实现 总结:在SPA项目中, 不同功能之间的切换 ,要 依赖于前端路由 来完成 通俗移动

    2024年01月22日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包