20_路由

一、路由的概念

  • 路由(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()
            }
        })
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值