单页面应用(SPA,Single Page Application)是一种web应用程序模型,其核心特点在于仅加载一个HTML页面,并在用户与应用程序交互时,通过动态更新该页面内容来响应。
在单页面应用中,所有的功能与交互都在这唯一加载的HTML页面内完成,首次加载时,浏览器会加载必需的HTML、CSS和JavaScript文件。后续的用户操作通过JavaScript动态地改变页面的内容,而不是通过重新加载整个页面来实现。
与传统多页面应用(MPA,Multiple Page Application)相比,SPA在用户操作时不会导致页面的重新加载或跳转,这提升了用户体验并减少了网络带宽的使用。同时,SPA也促进了前后端分离的开发模式,其中后端主要负责数据处理和API提供,而前端则负责页面的逻辑和渲染。
单页面应用,通常使用hash模式、history模式进行构建,下面简单了解如何实现单页面路由。
hash模式
hash
模式,在URL中的hash
(#号后面的部分),用于标记网页的不同部分或状态。通过hashchange监听事件,对URL变化,进行页面更新。
-
hash方式更改URL
var path="index";
window.localtion.hash=path;
-
监听hash URL变化
window.addEventListener('hashchange',function(e){
//路由发生改变时执行的事件...
});
-
hash模式案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>前端单页面路由</title>
<style>
.warp{
width:400px;
height:400px;
border:1px solid grey;
margin:0 auto;
}
.nav{
border-bottom:1px solid grey;
}
.nav li{
display:inline-block;
list-style:none;
}
.nav li a{
display:inline-block;
text-decoration: none;
padding:10px 15px;
}
.router{
padding:20px;
}
a{
cursor: pointer;
}
</style>
</head>
<body>
<section class="warp">
<div class="nav">
<ul>
<li><a href="javascript:void(0)" data-path="index">首页</a></li>
<li><a href="javascript:void(0)" data-path="news">新闻</a></li>
<li><a href="javascript:void(0)" data-path="about">关于</a></li>
</ul>
</div>
<div id="root" class="router">
<!-- 内容加载区域 -->
</div>
</section>
<script>
(function(){
//获取 DOM 元素
var aList = document.getElementsByTagName("a");
var root = document.querySelector("#root");
//路由配置
var routers = [
{path:"index",component:"首页"},
{path:"news",component:"新闻"},
{path:"about",component:"关于"}
];
//更改页面视图
function refresh(path) {
routers.map(item=>{
if(path == item.path){
root.innerHTML = item.component;
}
})
}
//点击页面时触发
for(let i=0;i<aList.length;i++){
aList[i].addEventListener('click',function (e) {
var path = this["attributes"]["data-path"].value;
window.location.hash = "/"+path;
refresh(path);
});
}
window.addEventListener('hashchange',function(e){
var oldPath = e.oldURL.split("#/")[1];
var newPath = e.newURL.split("#/")[1];
//hash值不一致时更新视图
if(oldPath != newPath){
refresh(newPath);
}
});
//浏览器刷新时触发
window.addEventListener('load',function(){
var path = "index";
if(location.hash){
path=location.hash.slice(2);
}
refresh(path);
});
})()
</script>
</body>
</html>
-
history模式
history 是 HTML5 提供的新特性,允许开发者直接更改前端路由,也就是更改 url 地址而无需向后端发送 http 请求。
因此可对 history 对象改变URL时,并通过 popstate 监听事件,进行浏览器前进后退时视图变化监听。
-
history方式更改URL
history.pushState(stete,title,url);
var path = "index";
history.pushState(null,'',path);
state:
一个对象,popState 事件触发时,state 对象会传入回调函数。如无需传参,则设置为 null 。
title:
新页面的标题,但是所有浏览器目前都忽略这个值,因此可以设置为空字符串 "" 或者 null 。
url:
新的网址地址,必须与当前页面处于同一个域下,浏览器的地址栏将显示这个网址。
-
监听 history变化
window.addEventListener('popstate',function(e){
//浏览器前进后退时执行的事件...
});;
-
history模式案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>前端单页面路由</title>
<style>
.warp{
width:400px;
height:400px;
border:1px solid grey;
margin:0 auto;
}
.nav{
border-bottom:1px solid grey;
}
.nav li{
display:inline-block;
list-style:none;
}
.nav li a{
display:inline-block;
text-decoration: none;
padding:10px 15px;
}
.router{
padding:20px;
}
a{
cursor: pointer;
}
</style>
</head>
<body>
<section class="warp">
<div class="nav">
<ul>
<li><a href="javascript:void(0)" data-path="index">首页</a></li>
<li><a href="javascript:void(0)" data-path="news">新闻</a></li>
<li><a href="javascript:void(0)" data-path="about">关于</a></li>
</ul>
</div>
<div id="root" class="router">
<!-- 内容加载区域 -->
</div>
</section>
<script>
(function(){
//获取 DOM 元素
var aList = document.getElementsByTagName("a");
var root = document.querySelector("#root");
//路由配置
var routers = [
{path:"index",component:"首页"},
{path:"news",component:"新闻"},
{path:"about",component:"关于"}
];
//更改页面视图
function refresh(path) {
routers.map(item=>{
if(path == item.path){
root.innerHTML = item.component;
}
})
}
//点击页面时触发
for(let i=0;i<aList.length;i++){
aList[i].addEventListener('click',function (e) {
var path = this["attributes"]["data-path"].value;
history.pushState({},null,'#/'+path);
refresh(path);
});
}
//浏览器前进后退时触发
window.addEventListener('popstate',function(e){
var path = location.hash.slice(2)||"index";
refresh(path);
});
//浏览器刷新时触发
window.addEventListener('load',function(){
var path = location.hash.slice(2)||"index";
history.replaceState({},null,'#/'+path);
refresh(path);
});
})()
</script>
</body>
</html>