一、路由的概念
-
路由(router)的本质就是一种 对应关系
-
根据不同的URL请求,返回对应不同的资源
-
url地址和资源之间的对应关系
-
url地址: 就是我们一直说的网址
- https://www.baidu.com/img/xxx.png
- https:协议
- www.baidu.com: 域名
- /img/xxx.png : 路径
-
资源:html页面,json文件,图片,视频,…
-
-
就是路由
1.后端路由
- 由服务器端进行实现并实现资源映射分发
- 根据不同的用户URL请求,返回不同的内容(地址与资源产生对应关系)
- 本质:URL请求地址与服务器资源之间的对应关
2.前端路由
- 根据不同的事件来显示不同的页面内容
- 是事件与事件处理函数之间的对应关系
- 根据不同的用户事件,显示不同的页面内容(地址与事件产生对应关系)
- 本质:用户事件与事件处理函数之间的对应关系
二、SPA单页面应用程序
- 单页面应用程序
- 基于前端路由
- 整个网站只有一个页面
- 通过监听地址栏中的变化事件
- 来通过Ajax局部更新内容信息显示
- 同时支持浏览器地址栏的前进和后退操作
三、hash路由
<div>
<h3>前端hash路由</h3>
<a href="#/a">hash页面a</a>
<a href="#/b">hash页面b</a>
<a href="#/c">hash页面c</a>
<a href="#/d">hash页面d</a>
</div>
<div>
<h3>路由匹配后显示内容的地方,默认显示a路由内容</h3>
<div id='content'>
显示的是a路由对应的内容
</div>
</div>
<button id='t'>click me</button>
window.onhashchange = function(){
switch(location.hash){
case "#/a":
content.innerHTML="显示的是hash-a路由对应的内容";
break;
case "#/b":
content.innerHTML="显示的是hash-b路由对应的内容";
break;
case "#/c":
content.innerHTML="显示的是hash-c路由对应的内容";
break;
case "#/d":
content.innerHTML="显示的是hash-d路由对应的内容";
break;
}
}
四、hashchange
事件
五、前端hash
路由实现
<body>
<ul>
<li>
<a href="#/discover">推荐</a>
</li>
<li>
<a href="#/discover/rank">排行</a>
</li>
<li>
<a href="#/discover/playlist">歌单</a>
</li>
<li>
<a href="#/discover/djradio">主播电台</a>
</li>
<li>
<a href="#/discover/singer">歌手</a>
</li>
<li>
<a href="#/discover/album">新碟上架</a>
</li>
</ul>
<div id="box"></div>
<script>
/*
需求分析:
1 页面第一次打开的时候,显示的是推荐的内容
2 每个li都可以点击,地址栏hash会发生变化,div里面的内容会变化,页面的title也会变
3 手动输入不同的hash,div里面的内容会变化,页面的title也会变
4 点击前进和后退,hash,div和title也会变
*/
// 1 页面第一次打开的时候,显示的是推荐的内容
discoverRender()
// 2 每个li都可以点击:只要li里面是a标签就可以了
// 3 只要hash变化,不管是点击还是直接输入地址还是前进后退,都要div和title变化
window.addEventListener('hashchange',function(){
let {hash} = location; // 等价于 let hash = location.hash;
switch(hash){
case '#/discover':discoverRender();break;
case '#/discover/rank':rankRender();break;
case '#/discover/playlist':playlistRender();break;
case '#/discover/djradio':djradioRender();break;
case '#/discover/singer':singerRender();break;
case '#/discover/album':albumRender();break;
default: // 如果hash一个都不符合,都去推荐
location.href = '#/discover'
}
})
// 事先定义6个渲染函数,分别用于渲染不同的hash对应的div的内容
// 推荐
function discoverRender(){
// 先获取div里面要显示的推荐内容的数据
let data = [
{ pic:"http://p1.music.126.net/O__ztFTUL84GOTUFLY3u7g==/1391981724404463.jpg?param=140y140",
name:"「欧美」撩拨前奏控 独特得欲罢不能",
play:'1768万'
},
{
pic:"http://p1.music.126.net/2xsgqNgNDcV8I5ZJ8kVjvA==/109951163032091575.jpg?param=140y140",
name:"「英伦摇滚」唱片中的昔日时光",
play:'2798万'
},
{
pic:"http://p1.music.126.net/YLSq_cppo-9Sl6cNu6-GiQ==/109951163548668843.jpg?param=140y140",
name:"以前喜欢一个人,现在喜欢一个人",
play:'1418万'
}
]
// div里面的内容会变化
let htmlstr = '';
// 遍历data
data.forEach(item=>{
htmlstr += `<div class="item">
<img src="${item.pic}" alt="">
<p>播放量:${item.play}</p>
<p>${item.name}</p>
</div>`
})
document.querySelector('#box').innerHTML = htmlstr;
// 页面的title也会变
document.title = "网易云音乐-推荐"
}
// 排行
function rankRender(){
// div里面的内容会变化
document.querySelector('#box').innerHTML = "我是排行的内容"
// 页面的title也会变
document.title = "网易云音乐-排行"
}
// 歌单
function playlistRender(){
// div里面的内容会变化
document.querySelector('#box').innerHTML = "我是歌单的内容"
// 页面的title也会变
document.title = "网易云音乐-歌单"
}
// 主播电台
function djradioRender(){
// div里面的内容会变化
document.querySelector('#box').innerHTML = "我是主播电台的内容"
// 页面的title也会变
document.title = "网易云音乐-主播电台"
}
// 歌手
function singerRender(){
// div里面的内容会变化
document.querySelector('#box').innerHTML = "我是歌手的内容"
// 页面的title也会变
document.title = "网易云音乐-歌手"
}
// 新碟上架
function albumRender(){
// div里面的内容会变化
document.querySelector('#box').innerHTML = "我是新碟上架的内容"
// 页面的title也会变
document.title = "网易云音乐-新碟上架"
}
</script>
</body>
六、history
路由
- 可以改变地址栏的显示,但是不会跳转页面
- 语法:history.pushState(state,title,url)
- state:携带的地址信息
- title:保留参数位置,目前没有实际意义,但是以后可能会用到
- url: 地址栏要显示的内容
<div id="app">
<h3>前端history路由</h3>
<ul>
<li><a href="/todos">任务</a></li>
<li><a href="/regist">注册</a></li>
<li><a href="/login">登陆</a></li>
</ul>
<div id='view'></div>
</div>
const btns = document.querySelectorAll('a');
for(let i=0;i<btns.length;i++){
btns[i].onclick = function(){
history.pushState({title:this.innerHTML},null,this.href);
document.getElementById('view').innerHTML = this.innerHTML+'组件'
return false;
}
}
七、pushState()
方法、popstate
事件
history.pushState(state, title[, url])
- state:状态对象
- title : 保留参数
- url:新历史记录条目的 URL 由此参数指定
const state = { 'page_id': 1, 'user_id': 5 }
const title = ''
const url = 'hello-world.html'
// 地址栏发生变化,但是不会跳转页面
history.pushState(state, title, url)
window.onpopstate = function(){}
- 调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。
- popstate 事件只会在浏览器某些行为下触发,比如点击后退按钮(或者在 JavaScript 中调用 history.back() 方法)
// 假如当前网页地址为 http://example.com/example.html,则运行下述代码将触发警告对话框
window.onpopstate = function(event) {
alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
history.pushState({page: 1}, "title 1", "?page=1");
history.pushState({page: 2}, "title 2", "?page=2");
history.replaceState({page: 3}, "title 3", "?page=3");
history.back(); // 弹出 "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // 弹出 "location: http://example.com/example.html, state: null
history.go(2); // 弹出 "location: http://example.com/example.html?page=3, state: {"page":3}
// 注意,即便是没有关联 state 对象的原始的历史条目(比如 http://example.com/example.html),popstate 事件也仍会在我们第二次调用 history.back(),激活这一历史条目后触发
八、前端history
路由实现
/*
需求分析
1 页面一打开的时候,地址栏变成/discover,div里面显示推荐的内容
2 点击每个有path属性的li,地址会变,div里面显示对应内容,title显示对应内容
3 点击前进或者后退,地址会变,div里面显示对应内容,title显示对应内容
*/
// 1 页面一打开的时候,地址栏变成/discover
history.pushState({content:'推荐',url:'/discover'},'','/discover');
// div里面显示推荐的内容
discoverRender()
// 2 点击每个有path属性的li
// 获取所有有path属性的li
let routers = document.querySelectorAll('[path]');
// 循环给每个li绑定点击事件
routers.forEach(item=>{
item.onclick = function(){
// 获取当前li标签上的path属性值
let url = this.getAttribute('path'); // /discover/rank
// 地址会变
history.pushState({content:this.innerHTML,url:url},'',url)
// div里面显示对应内容,title显示对应内容
switch(url){
case '/discover':discoverRender();break;
case '/discover/rank':rankRender();break;
case '/discover/playlist':playlistRender();break;
case '/discover/djradio':djradioRender();break;
case '/discover/singer':singerRender();break;
case '/discover/album':albumRender();break;
}
}
})
// 3 点击前进或者后退,地址会变
// 当历史记录前进/后退到的历史记录条目是由history.pushState创建的,就会触发window的popstate事件
// 直接调用pushState改变历史记录不会触发popstate
window.onpopstate = function(e){
// popstate的事件对象e里有一个属性叫做state里面就是当时pushState的第一个参数
console.log(e.state)
// 根据state里面的url判断调用哪个渲染函数
switch(e.state.url){
case '/discover':discoverRender();break;
case '/discover/rank':rankRender();break;
case '/discover/playlist':playlistRender();break;
case '/discover/djradio':djradioRender();break;
case '/discover/singer':singerRender();break;
case '/discover/album':albumRender();break;
}
}
九、封装路由函数
/*
我的函数router跟定了你要传入的参数是一个数组
数组里面每一个对象的path就是要匹配的hash,对象里面的component就是匹配hash以后要调用的函数
[
{path:'#/a',compoent:匹配path以后要调用的函数的名字}
]
*/
function router(routes){
// 调用我的函数,就可以帮你绑定hashchange事件
window.onhashchange = function(){
// 获取当前的hash值
let {hash} = location;
// 根据hash值,执行对应的渲染函数
routes.forEach(item=>{
if(item.path == hash){
item.component()
}
})
}
}