前言
当下最流行的前端三大框架中的 React 和 Vue 都是 SPA(single page web application)单页面应用。单页面应用页面与页面之间的跳转就需要用到路由。今天就来记录一下单页面路由实现原理。
路由跳转主要做了二件事。
- 浏览器地址栏地址改变;
- 页面切换到相应的内容。
比如登录页面,地址栏为:localhost:8080/login。内容切换到登录页面。
实现单页面路由主要有两种方式,location 的 hash 对象 和 location 的 history 对象(H5 的新 PAI )。
hash
通过 hash 对象改变地址栏地址时,可以用 hashchange 事件来监控页面路径 hash 变化更新页面。
核心步骤1:
var path = "index";
window.location.hash = path;
核心步骤2:
window.addEventListener('hashchange',function(e){
//路由发生改变时执行的事件...
});
路由案例:
<!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 对象改变地址栏地址时,可以通过 popstate 事件来监控浏览器前进后退时视图变化。
核心步骤1:
var path = "index";
history.pushState(null,null,'#/'+path);
核心步骤2:
window.addEventListener('popstate',function(e){
//浏览器前进后退时执行的事件...
});
路由案例:
<!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>