JavaScript单页面路由详解:打造现代化、高性能的Web应用

news2025/1/17 3:51:52

​🌈个人主页:前端青山
🔥系列专栏:JavaScript篇
🔖人终将被年少不可得之物困其一生

依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript-spa单页面路由

目录

# 基于SPA的单页面路由

关于单页应用

单页应用的实现

实现改变URL页面不刷新

监听URL的变化,执行页面替换逻辑

两种实现的比较

代码实现

代码升级(组件化开发)

# 基于SPA的单页面路由

关于单页应用

单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。简单来说就是用户只需要加载一次页面就可以不再请求,当点击其他子页面时只会有相应的URL改变而不会重新加载。

  • 大部分用于: 移动端 / PC端的后台管理系统

单页应用的实现

  1. 依赖hash的改变(锚点)
  2. 依赖历史记录(history)
    • 前端路由:在页面中同一个位置,根据不同的hash值显示不同的页面结构

实现改变URL页面不刷新

按照常规的逻辑我们切换URL好像就会跳转网页,但是转念一想锚链接的URL不是也改变了吗? 这里,存在两种满足需求的方式。

  • 利用URL中的hash方式
    了解http协议就会知道,url的组成部分有很多,譬如协议、主机名、资源路径、查询字段等等,其中包含一个称之为片段的部分,以“#”为标识。打开控制台,输入 location.hash,你可以得到当前url的hash部分(如果当前url不存在hash则返回空字符串)。接下来,输入 location.hash = ‘123’,会发现浏览器地址栏的url变了,末尾增加了’#123’字段,并且,页面没有被重新刷新。很显然,这很符合我们的要求。
  • 利用H5的history API
    html5引入了一个history对象,包含了一套访问浏览器历史的api,可以通过window.history访问到它。 HTML5 History API包括2个方法:history.pushState()和history.replaceState(),和1个事件:window.onpopstate。这两个方法都是对浏览器的历史栈进行操作,将传递的url和相关数据压栈,并将浏览器地址栏的url替换成传入的url且不刷新页面,而且他们的参数也相同,第一个参数用于存储该url对应的状态对象,该对象可在onpopstate事件中获取,也可在history对象中获取。第二个参数是标题,目前浏览器并未实现。第三个参数则是设定的url。一般设置为相对路径,如果设置为绝对路径时需要保证同源。。不同的是pushState 将指定的url直接压入历史记录栈顶,而 replaceState 是将当前历史记录栈顶替换成传入的数据。不过低版本对history AIP的兼容性不是很好。

监听URL的变化,执行页面替换逻辑

  1. 对于hash方式,我们通常采用监听hashchange事件,在事件回调中处理相应的页面视图展示等逻辑。

两种实现的比较

总的来说,基于Hash的路由,兼容性更好;基于History API的路由,更加直观和正式。但是,有一点很大的区别是,基于Hash的路由不需要对服务器做改动,基于History API的路由需要对服务器做一些改造。

代码实现

<div id="box">
    <div class="top">顶部通栏</div>
    <div class="bottom">
        <div class="slide">
            <a href="#/first">first</a>
            <a href="#/second">second</a>
            <a href="#/third">third</a>
            <a href="#/fourth">fourth</a>
        </div>
        <div class="content router-view"></div>
    </div>
</div>
<style>
*{
    margin: 0;
    padding: 0;
}
html,body,#box{
    width: 100%;
    height:100%;
}
#box{
    display: flex;
    flex-direction:column;
}
#box>.top{
    height: 130px;
    background: skyblue;
}
#box>.bottom{
    flex:1;
    display: flex;
}
#box>.bottom>.slide{
    width: 230px;
    background-color: orange;
    box-sizing: border-box;
    padding: 15px;
}
#box>.bottom>.slide>a{
    font-size: 22px;
    display: block;
    margin: 10px 0;
    text-decoration: none;
    color: #333;
}
#box>.bottom>.content{
    flex: 1;
    box-sizing: border-box;
    padding: 15px;
    background: purple;
    font-size: 100px;
    color: white
}
</style>
//1. 准备不同的页面显示结构
const template1 = `
				<div>
					first
				</div>
			`;
const template2 = `
				<div>
					second
				</div>
			`;
const template3 = `
				<div>
					third
				</div>
			`;
const template4 = `
				<div>
					fourth
				</div>
			`;

//2. 获取路由入口对象
let routerView = document.querySelector('.router-view');
//3. 捕获浏览器地址栏 hash值的改变
window.onhashchange = function(){
    console.log('地址栏hash值改变了');
    console.log(window.location.hash);
    //获取hash
    const {hash} = window.location;
    //判断hash值,进行不同的结构渲染
    switch(hash){
        case '#/first' : routerView.innerHTML = template1;break;
        case '#/second' : routerView.innerHTML = template2;break;
        case '#/third' : routerView.innerHTML = template3;break;
        case '#/fourth' : routerView.innerHTML = template4;break;
    }
}

代码升级(组件化开发)

  1. index.html

    <div id="box">
        <div class="top">顶部通栏</div>
        <div class="bottom">
            <div class="slide">
                <a href="#/first">first</a>
                <a href="#/second">second</a>
                <a href="#/third">third</a>
                <a href="#/fourth">fourth</a>
            </div>
            <div class="content router-view"></div>
        </div>
    </div>
    
    *{
        margin: 0;
        padding: 0;
    }
    html,body,#box{
        width: 100%;
        height:100%;
    }
    #box{
        display: flex;
        flex-direction:column;
    }
    #box>.top{
        height: 130px;
        background: skyblue;
    }
    #box>.bottom{
        flex:1;
        display: flex;
    }
    #box>.bottom>.slide{
        width: 230px;
        background-color: orange;
        box-sizing: border-box;
        padding: 15px;
    }
    #box>.bottom>.slide>a{
        font-size: 22px;
        display: block;
        margin: 10px 0;
        text-decoration: none;
        color: #333;
    }
    #box>.bottom>.content{
        flex: 1;
        box-sizing: border-box;
        padding: 15px;
        background: purple;
        font-size: 100px;
        color: white
    }
    
    <script src="./index.js" type="module"></script>
    
  2. components 组件

    • first.js

      //组件的html结构
      const template = `
      	<div id="first">
      		first
      	</div>
      `;
      //获取到路由出口对象
      const routerView = document.querySelector('.router-view');
      //准备一个渲染函数
      function render(){
      	routerView.innerHTML = template;
      }
      //导出渲染函数
      export default render;
      
    • second.js

      //组件的html结构
      const template = `
      	<div id="second">
      		second
      	</div>
      `;
      //获取到路由出口对象
      const routerView = document.querySelector('.router-view');
      //准备一个渲染函数
      function render(){
      	routerView.innerHTML = template;
      }
      //导出渲染函数
      export default render;
      
    • third.js

      //组件的html结构
      const template = `
      	<div id="third">
      		third
      	</div>
      `;
      //获取到路由出口对象
      const routerView = document.querySelector('.router-view');
      //准备一个渲染函数
      function render(){
      	routerView.innerHTML = template;
      }
      //导出渲染函数
      export default render;
      
    • fourth.js

      //组件的html结构
      const template = `
      	<div id="fourth">
      		fourth
      	</div>
      `;
      //获取到路由出口对象
      const routerView = document.querySelector('.router-view');
      //准备一个渲染函数
      function render(){
      	routerView.innerHTML = template;
      }
      //导出渲染函数
      export default render;
      
  3. router.js

    //导入组件模块
    import firstCom from './components/first.js';
    import secondCom from './components/second.js';
    import thirdCom from './components/third.js';
    import fourthCom from './components/fourth.js';
    // 定义路由表
    const router = [
    	{
    		name : '/first',
    		component: firstCom
    	},
    	{
    		name: '/second',
    		component: secondCom
    	},
    	{
    		name: '/third',
    		component: thirdCom
    	},
    	{
    		name: '/fourth',
    		component: fourthCom
    	}
    ];
    //导出路由
    export default router;
    
  4. index.js

    //1. 导入路由表
    import router from './router.js';
    //2. 注册hashchange 事件
    window.onhashchange = hashChange;
    hashChange();
    //3. 处理程序
    function hashChange(){
    	console.log('根据当前hash值,进行改变');
    	//获取当前的hash值
    	const hash = window.location.hash.slice(1) || '/';
    	// console.log(hash);
    	//从路由表中找到hash对应的那一个信息
    	const info = router.find(item => item.name === hash);
    	console.log(info);
    	//如果没有内容,不需要后续操作
    	if(!info){
    		return;
    	}
    	//渲染页面
    	info.component();
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1293978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

编程实战:自己编写HTTP服务器(系列2:请求)

系列入口&#xff1a;编程实战&#xff1a;自己编写HTTP服务器&#xff08;系列1&#xff1a;概述和应答&#xff09;-CSDN博客 本文介绍如何处理请求。 目录 一、概述 二、成员变量 三、接收并分解请求 四、完整代码 五、HTTP处理框架 一、概述 请求和应答结构其实差不多…

Axure原型图表组件库,数据可视化元件(Axure9大屏组件)

针对Axure制作的大屏图表元件库&#xff0c;帮助产品经理更高效地制作高保真图表原型&#xff0c;是产品经理必备元件工具。现分享完整的组件库&#xff0c;大家一起学习。 本组件库的图表模块&#xff0c;已包含所有常用的图表&#xff0c;以下为部分组件截图示意。文末可下载…

Axure原型组件库,数据可视化动态元件库(超详细Axure9可视化素材)

专门针对Axure制作的动态图表元件库&#xff0c;帮助产品经理更高效地制作高保真图表原型&#xff0c;是产品经理必备元件工具。现分享完整 Axure 9 的组件库&#xff0c;大家一起学习。&#xff08;如需 Axure 8组件请详见文末&#xff09; 每一个动态组件在原型文件中都配有…

NLP自然语言处理学习笔记

参考&#xff1a;NLP&#xff08;自然语言处理&#xff09;介绍 - 知乎 (zhihu.com) 一、NLP是什么 自然语言处理( Natural Language Processing, NLP)是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。自…

小航助学2023年6月GESP_Scratch二级真题(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号 单选题3.00分 删除编辑附件图文 答案:D 第1题高级语言编写的程序需要经过以下&#xff08; &#xff09;操作&#xff0c;可以生成在计算机上运行的可执行代码。 A、编辑B、…

Liunx系统使用超详细(四)~文件/文本相关命令2

承接文章Liunx系统使用超详细(四)~文件/文本相关命令1http://t.csdnimg.cn/f7G6S 目录 一、awk命令(三剑客之一) 1.1工作原理 1.2工作流程 1.3语法格式 1.3.1格式注释&#xff1a; 1.3.2模式&#xff08;pattern&#xff09;的类型&#xff1a; 1.3.3动作&#xff08;ac…

Java期末复习题之继承

点击返回标题->23年Java期末复习-CSDN博客 第1题. 设计一个学生类Student&#xff0c;其数据成员有name(姓名)、age(年龄)和degree(学位)。由Student类派生出本科生类Undergraduate和研究生类Graduate&#xff0c;本科生类Undergraduate增加成员specialty(专业)&#xff0c;…

vue-tree-color 组件实现组织架构图遇到的坑和解决方案以及实现

**1、前期工作可以先看看大佬的文章 **https://blog.csdn.net/Try_your_best_l/article/details/120173192?spm1001.2101.3001.6650.5&utm_mediumdistribute.pc_relevant.none-task-blog-2defaultCTRLISTRate-5-120173192-blog-128109597.235%5Ev38%5Epc_relevant_default…

案例精选|聚铭网络助力莱阳市人民医院打造合规性网络安全保障体系

莱阳市人民医院是一所集医疗、教学、科研、急救、康复、医养结合于一体的大型二级甲等综合性公立医院&#xff0c;占地总面积约3万平方米&#xff0c;建筑面积约7万平方米&#xff0c;设置科室48个&#xff0c;开放床位500张。医院先后获得山东省首批医养结合典型、山东省卒中防…

案例060:基于微信小程序考试系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

学习设计模式的一个好网址

常用设计模式有哪些&#xff1f; (refactoringguru.cn)https://refactoringguru.cn/design-patterns

【Proteus仿真】【STM32单片机】蓝牙遥控小车

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使LCD1602液晶&#xff0c;L298电机&#xff0c;直流电机&#xff0c;HC05/06蓝牙模块等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显…

Matlab 点云对称性检测

文章目录 一、简介二、实现代码三、实现效果参考文献一、简介 这是一个很有趣的功能,它的思路其实与ICP算法的思路有些相似: 首先,它会初始化两个旋转角度,即绕x轴旋转与绕y轴旋转,初始的过程是将点对称(镜像)过去,计算与匹配点之间的距离误差,误差最小者为最优初始值…

【Docker二】docker网络模式、网络通信、数据管理

目录 一、docker网络模式&#xff1a; 1、概述 2、docker网络实现原理&#xff1a; 3、docker的网络模式&#xff1a; 3.1、bridge模式&#xff1a; 3.2、host模式&#xff1a; 3.3、container模式&#xff1a; 3.4、none模式&#xff1a; 3.5、自定义网络模式&#xf…

Spring基于注解存储对象

小王学习录 前言基于注解存储对象Controller (控制器存储)Service (服务存储)Repository (仓库存储)Component (组件存储)Configuration (配置存储)Bean(方法注解) 前言 上一篇文章中已经介绍了在Spring中存储Bean和取Bean的方法. 而在 Spring 中想要更简单的存储和读取对象的…

【面试经典150 | 二叉树】从前序与中序遍历序列构造二叉树

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;递归 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等内容…

docker基本管理和概念

1、定义&#xff1a;一个开源的应用容器引擎&#xff0c;基于go语言开发&#xff0c;运行在liunx系统中的开源的、轻量级的“虚拟机” docker的容器技术可以在一台主机上轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器 docker的宿主机是liunx系统&#xff0c;集…

Halcon threshold_sub_pix (Operator)

read_image(Image,fabrik) threshold_sub_pix(Image,Border,35) dev_display(Border)Image是输入的原始图像&#xff0c;Threshold是设定的阈值&#xff0c;Width和Height是像素值计算区域的大小&#xff0c;ThresholdedRegion是经过分割后得到的二值化结果。 在对图像进行二值…

Stable Diffusion AI绘画系列【20】:美丽动人的雀羽婚纱风,你心动了吗?

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

springboot基础(80):redis geo的应用

文章目录 前言redis GEO如何从地图上获取经纬度springboot 的相关方法调用准备redis服务器引用的依赖预设位置的keyGEOADD 添加位置GEORADIUS 获取指定经纬度附件的停车场&#xff08;deprecated&#xff09;GEORADIUS 获取指定成员附件的停车场&#xff08;deprecated&#xf…