單頁面應(yīng)用(SPA,Single Page Application)是一種web應(yīng)用程序模型鬓催,其核心特點(diǎn)在于僅加載一個(gè)HTML頁面同诫,并在用戶與應(yīng)用程序交互時(shí)尊勿,通過動(dòng)態(tài)更新該頁面內(nèi)容來響應(yīng)。
在單頁面應(yīng)用中畜侦,所有的功能與交互都在這唯一加載的HTML頁面內(nèi)完成元扔,首次加載時(shí),瀏覽器會(huì)加載必需的HTML旋膳、CSS和JavaScript文件澎语。后續(xù)的用戶操作通過JavaScript動(dòng)態(tài)地改變頁面的內(nèi)容,而不是通過重新加載整個(gè)頁面來實(shí)現(xiàn)验懊。
與傳統(tǒng)多頁面應(yīng)用(MPA擅羞,Multiple Page Application)相比,SPA在用戶操作時(shí)不會(huì)導(dǎo)致頁面的重新加載或跳轉(zhuǎn)义图,這提升了用戶體驗(yàn)并減少了網(wǎng)絡(luò)帶寬的使用减俏。同時(shí),SPA也促進(jìn)了前后端分離的開發(fā)模式碱工,其中后端主要負(fù)責(zé)數(shù)據(jù)處理和API提供娃承,而前端則負(fù)責(zé)頁面的邏輯和渲染。
單頁面應(yīng)用怕篷,通常使用hash模式历筝、history模式進(jìn)行構(gòu)建,下面簡單了解如何實(shí)現(xiàn)單頁面路由廊谓。
hash模式
hash
模式梳猪,在URL中的hash
(#號(hào)后面的部分),用于標(biāo)記網(wǎng)頁的不同部分或狀態(tài)蒸痹。通過hashchange監(jiān)聽事件春弥,對(duì)URL變化,進(jìn)行頁面更新叠荠。
-
hash方式更改URL
var path="index";
window.localtion.hash=path;
-
監(jiān)聽hash URL變化
window.addEventListener('hashchange',function(e){
//路由發(fā)生改變時(shí)執(zhí)行的事件...
});
-
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">關(guān)于</a></li>
</ul>
</div>
<div id="root" class="router">
<!-- 內(nèi)容加載區(qū)域 -->
</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:"關(guān)于"}
];
//更改頁面視圖
function refresh(path) {
routers.map(item=>{
if(path == item.path){
root.innerHTML = item.component;
}
})
}
//點(diǎn)擊頁面時(shí)觸發(fā)
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值不一致時(shí)更新視圖
if(oldPath != newPath){
refresh(newPath);
}
});
//瀏覽器刷新時(shí)觸發(fā)
window.addEventListener('load',function(){
var path = "index";
if(location.hash){
path=location.hash.slice(2);
}
refresh(path);
});
})()
</script>
</body>
</html>
-
history模式
history 是 HTML5 提供的新特性匿沛,允許開發(fā)者直接更改前端路由,也就是更改 url 地址而無需向后端發(fā)送 http 請(qǐng)求蝙叛。
因此可對(duì) history 對(duì)象改變URL時(shí)俺祠,并通過 popstate 監(jiān)聽事件,進(jìn)行瀏覽器前進(jìn)后退時(shí)視圖變化監(jiān)聽借帘。
-
history方式更改URL
history.pushState(stete,title,url);
var path = "index";
history.pushState(null,'',path);
state:
一個(gè)對(duì)象蜘渣,popState 事件觸發(fā)時(shí),state 對(duì)象會(huì)傳入回調(diào)函數(shù)肺然。如無需傳參蔫缸,則設(shè)置為 null 。
title:
新頁面的標(biāo)題际起,但是所有瀏覽器目前都忽略這個(gè)值拾碌,因此可以設(shè)置為空字符串 "" 或者 null 吐葱。
url:
新的網(wǎng)址地址,必須與當(dāng)前頁面處于同一個(gè)域下校翔,瀏覽器的地址欄將顯示這個(gè)網(wǎng)址弟跑。
-
監(jiān)聽 history變化
window.addEventListener('popstate',function(e){
//瀏覽器前進(jìn)后退時(shí)執(zhí)行的事件...
});;
-
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">關(guān)于</a></li>
</ul>
</div>
<div id="root" class="router">
<!-- 內(nèi)容加載區(qū)域 -->
</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:"關(guān)于"}
];
//更改頁面視圖
function refresh(path) {
routers.map(item=>{
if(path == item.path){
root.innerHTML = item.component;
}
})
}
//點(diǎn)擊頁面時(shí)觸發(fā)
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);
});
}
//瀏覽器前進(jìn)后退時(shí)觸發(fā)
window.addEventListener('popstate',function(e){
var path = location.hash.slice(2)||"index";
refresh(path);
});
//瀏覽器刷新時(shí)觸發(fā)
window.addEventListener('load',function(){
var path = location.hash.slice(2)||"index";
history.replaceState({},null,'#/'+path);
refresh(path);
});
})()
</script>
</body>
</html>