Vue 学习 第五篇 Vue Router

Vue Router

一、URL 组成部分

路由就是根据一个地址找到对应的页面。

url组成部分:一个完整的URL由几部分组成?

https://www.icketang.com:443/static/img/banner_news.jpg?color=red&num=100#title

  协议 https://

  域名 www.icketang.com

  端口号 :443

  路径 /static/img/

  文件名 banner_news.jpg

  搜索词 ?color=red&num=100

  哈希 #title

  一个url由七部分组成,前三个部分的改变会导致跨域文件。前六个部分的改变会导致浏览器端向服务器端发送新的请求。只有hash的改变不会向服务器端发送新的请求,因此前端路由就是基于hash实现的。

二、 前端路由的实现

  由于hash的改变不会向服务器端发送新的请求,因此我们可以监听hash的变化(通过hashchange事件),根据不同的hash渲染不同的页面(通过location.hash获取当前的hash值)。

  这种不向服务器端发送请求,而实现切换页面的功能,就是单页面应用程序(SPA:single pageapplication)。

    单页面应用程序就是基于前端的hash路由实现的。特点就是:快。

01 模拟前端路由的实现.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <div id="app">
        <h3>{{msg}}</h3>
        <input type="text" v-model="msg">
        <hr>
        <component :is="page"></component>
    </div>

    <script src="./dist/00.js"></script>
    
</body>
</html>

00 .js



import Vue from 'vue';

// 定义组件
let Home = Vue.extend({
    template:`
        <div>
            <h1>Home  page</h1>
        </div>
    `
})
let List = Vue.extend({
    template:`
        <div>
            <h1>list  page</h1>
        </div>
    `
})
let Detail = Vue.extend({
    template:`
        <div>
            <h1>detail  page</h1>
        </div>
    `
})


let app = new Vue({
    el:"#app",
    data:{
        msg:"hello page",
        page:'home'
    },
    components:{
        'home':Home,
        'list':List,
        'detail':Detail
    }
})


// 定义路由方法
function router(){
    // 获取页面名称
    let page = location.hash.slice(2);
    // console.log(page);
    // 修改数据
    app.page = page;
}

// 监听 hash 的变化
// window.addEventListener('hashchange',()=>{
//     console.log("111");
// })


window.addEventListener("hashchange",router)


// 执行路由的方法
router()

效果图
在这里插入图片描述

三、Vue 路由的实现

vue为了让我们更方便的使用路由,提供了路由模块:vue-router

使用路由分成六步

  第一步 安装路由:Vue.use方法安装

  第二步 创建组件对象:定义 Vue.extend 的参数对象(简化了对组件的定义)

  第三步 定义路由规则:是一个数组,每一个成员代表一条规则

    name:代表名称

    component:代表渲染的组件,

    path:匹配规则(与express类似)

        静态路由:一个规则对应一个页面地址,

           如:/home/search,

              匹配:/home/search,

              不匹配:/home, /home/search/1, /list/search
        动态路由:一个规则对应多个页面地址

           如:/list/:page

              匹配:/list/1, /list/100, /list/demo,

              不匹配:/list, /list/1/2, /home/1

  第四步 实例化路由,new Router({ routes })。通过routes属性传递路由规则

  第五步 在vue实例化对象中,注册路由,通过router属性注册。

  第六步 在模板中,通过router-view组件,定义路由的渲染位置。

01 vue 中的 Router.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <hr>
        <!-- 第六步 渲染页面 -->
        <router-view></router-view>
    </div>

    <script src="./dist/01.js"></script>
    
</body>
</html>

01 .js


import Vue from 'vue';

import Router from 'vue-router';

// 第一步安装  路由
Vue.use(Router);

// 第二步定义组件
// 之前是
// let Home = Vue.extend({template:`<h1>Home page</h1>`})
// 简化了定义
let Home = { template:'<h1>Home page</h1>' }
// 列表
let List = { template:'<h1>List page</h1>' }
// 详情页
let Detail = { template:'<h1>Detail page</h1>' }


// 第三步 定义路由规则
let routes=[
    // 每一个成员是一个对象,代表一个规则
    { name:'home',path:'/home',component: Home },
    // 列表页
    { name:'list',path:'/list/:page',component: List },
    // 详情页
    { name:'detail',path:'/detail/:page',component:Detail }
]

// // 第四步进行实例化
let router = new Router({ routes })

let app = new Vue({
    el:'#app',
    // 第五步 注册路由
    // router:router,
    router,
    data:{
        msg:"hello",
    }
})

效果图
在这里插入图片描述

四、路由数据

  当我们在vue中注册了路由之后,每一个组件都会具有两个属性:$route, $router

    $router 表示路由实例,包含一些切换路由的方法

        push 进入一个新页面

        replace 替换当前的页面

        back 返回上一个页面

        forward 进入下一个页面

        go 返回第一个页面

     $route 存储了路由相关数据

        路径,名称,query,动态路由数据(params)等等

           注意:在hash策略下,hash属性代表的是第二个#后面的内容,

由于这些数据都设置了特性,因此既可以在模板中使用,也可以在js中使用。

五、props

我们在定义路由规则的时候,可以传递props属性,属性值有两种情况

  第一种:属性值是true

    会将动态路由数据传递给组件

  第二种:属性值是函数

    参数是$route数据对象

    返回值表示给组件传递的数据

我们在组件中,通过props属性去接收这些数据(类似父组件向子组件通信)

六、默认路由

我们让path匹配 *。既可以定义默认路由

注意:由于*匹配的很广,因此通常定义在最后面。

默认路由:当前地址没有匹配的规则,就会渲染默认路由定义的组件。

七、路由重定向

我们通过path定义匹配的规则

我们通过redirect属性定义重定向的路径

当有与path匹配的路径就会重定向到新的路径。

bug:当重定向的时候,携带query,hash等数据,会导致路由对象解析错误。

注意:工作中,做路由重定向的时候,不要携带query等其它数据。
02 路由数据 + 默认路由 + 路由重定向.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <hr>
        <router-view></router-view>
    </div>

    <script src="./dist/02.js"></script>
</body>
</html>

02 .js


import Vue from 'vue';

import Router from 'vue-router';

// 第一步安装 
Vue.use(Router);

// 第二步定义组件
let Home = { template:'<h1>home page</h1>' };

let List = { 
    // 接收数据
    props:['page',"path","hash","color"],
    template:`
        <div>
            <h1>list page</h1>
            <hr>
            <h5>获取的参数----fullPath:{{$route.fullPath}} ---- path :{{$route.path}}  --- hash:{{ $route.hash}} ---color: {{$route.query.color}}</h5>
            <!-- 对上面的打点语法进行简化 -->
            <h6>page:{{page}} ---- path:{{path}} --- hash:{{hash}} --- color:{{color}}</h6>
        </div>
    `,
    created() {
        console.log(this,"list")
    },
}

let Detail = { template:'<h1>detail page</h1>' }


// 第三步 定义路由规则
let routes =[
    { name:'home' ,path:'/home',component:Home },
    { 
        name:'list', 
        path:'/list/:page',
        component:List,
        // 值为布尔值 只传递动态路由参数
        // props:true,
        // 传递更多的数据
        props(route){
            console.log(route,"route");
            return {
                page:route.params.page,
                path:route.path,
                hash:route.hash,
                color:route.query.color
            }
        }
    },
    { name:'detail',path:'/detail/:id',component:Detail},
    // 重定向
    // { path:'/dazhaxie',redirect:'/detail/dazhaxie' },

    // bug:  携带 query 和 hash 等数据,会出现问题,解析错误
    { path:'/dazhaxie',redirect:'/list/dzx?color=red&num=100#demo' },
    // 默认路由
    { path:'*',component:Home }
]

// 第四步实例化
let router =new Router({ routes })

let app = new Vue({
    el:"#app",
    // 第五步 注册路由
    router,
    data:{
        msg:"hello page"
    }
})

// console.log("app",app);

效果图
在这里插入图片描述

八、子路由

子路由允许我们在页面的局部切换视图。

使用子路由分成两步

  第一步 在父路由模板中,通过 router-view 组件定义子路由渲染位置。

  第二步 在父路由规则中,通过children属性定义子路由规则。

    是一个数组,每一个成员代表一条规则:path, name, component, redirect, children …

定义规则的要注意:

  如果是绝对路径:子路由的路径就是子路由的绝对路径(子)

  如果是相对路径:子路由的路径就是父路由路径+子路由的相对路径(父 + 子)

九、 路由策略

vue中默认使用的是hash策略(根据hash的变化,切换页面,实现SPA)

我们想使用path策略,可以通过设置路由实例化对象的mode属性实现

  mode: ‘history‘ 此时就是path策略。需要服务器端的配合(重定向)。

  由于切换路径会向服务器端发送请求,因此这是一个多页面应用程序

  使用多页面应用,要配置服务器

前提 配置 app.js 服务器
和html 文件 同级目录下定义 app.js


// 引入 express
let express = require('express');

// 引入 ejs 
let ejs = require('ejs');

// 创建应用
let app = express();

// 更改 ejs 默认拓展名
app.engine('.html',ejs.__express);

// 静态化
app.use('/dist/',express.static('./dist/'));

// 将自定义的 json 数据进行静态化
app.use('/data/',express.static('./data/'));

// 定义 post 请求
app.post('/demo',(req,res)=>{
    res.json({ msg:'你好 DaZhaXie' })
})

// 配置路由
// app.get('*',(req,res)=>{
//     // 渲染模板
//     // 默认是 views  所以这里需要跳出

//     // 定义子路由的显示
//     // res.render('../03 定义子路由.html')

//     // 定义   子路由导航+过渡+滚动条
//     // res.render('../04 路由导航+过渡+滚动条.html')

//     // 实现路由的守卫
//     // res.render('../05 路由守卫.html')

//     // 实现 axios
//     res.render("../06 axios.html")
// })

app.get('/',(req,res)=>{
    // axios  get 请求
    // res.render("../06 axios  get 请求.html")

    // axios post 请求
    res.render("../07 axios post 请求.html")
})

// 启动服务
app.listen(3000);

注意: 右击 app.js 文件 启动服务器 指令: nodemon app.js
**使用端口号进行访问: 例如: localhost:3000 **

03 子路由 + 路由策略.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <div id="app">
        <h1>{{msg}}</h1>
        <hr>

        <!-- 使用 router-link  实现页面的跳转 -->
        <router-link to="/home">home</router-link>
        <router-link to="/list/20">list</router-link>
        <router-link to="/detail/10">detail</router-link>
        

        <router-view></router-view>
    </div>

    <!-- 相对路径 -->
    <script src="./dist/03.js"></script>

    <!-- 使用绝对路径 实现页面的跳转 -->
    <!-- <script src="dist/03.js"></script> -->

    
</body>
</html>

03 .js



import Vue from 'vue';

import Router from 'vue-router';

Vue.use(Router);

// 定义组件
let Home = { 
    template:`
        <div>
            <h6>home  page</h6>
        </div>
    ` 
}

let List = { 
    // 接收数据
    props:['page','path','hash','num'],
    template:`
        <div>
            <h6>list page</h6>
            <hr>
            <!-- 渲染页面 -->
            <strong>返回过来的数据--- {{page}} --- {{path}} --- {{hash}} --- {{num}}</strong>
        </div>
    `
}

let Detail = {
    template:`
        <div>
            <h6>detail page</h6>
            <!-- 定义子路由 -->
            <router-view></router-view>
        </div>
    `,
}

// 定义子页面
let Demo = {
    template:`
        <div>
            <ins>demo page</ins>
        </div>
    `
}

// 第三步定义路由规则
let routes = [
    { name:'home', path:'/home',component:Home },
    {
        name:'list',
        path:'/list/:page',
        component:List,
        props(route){
            // console.log(route,"list route");
            // 返回数据
            return{
                page:route.params.page,
                path:route.path,
                hash:route.hash,
                num:route.query.num
            }
        }
    },
    {
        name:'detail',
        path:'/detail/:id',
        component:Detail,
        // 定义子路由
        children:[
            // 定义绝对路径
            { path:'/demo',component:Demo },
            // 定义相对路径
            { 
                path:'search/:page',
                component:{
                    template:`
                        <div>
                            <em>search  page</em>
                        </div>
                    `,
                }
            }
        ]
    },

    // 默认显示页面
    { path:'*',component:Home }
]

// 第四步进行实例化
let router = new Router({
    routes,
    // 在 服务器端启动
    // mode:'history'
})



let app = new Vue({
    el:"#app",
    // 安装 路由
    router,
    data:{
        msg:"hello page"
    }
})

也可以 通过 服务器访问 也可以 右击浏览器访问 前提 需要改 路由策略

通过右键的方式 显示的效果图
在这里插入图片描述
通过服务器的方式显示的效果图
在这里插入图片描述

十、路由导航

为了方便我们切换路由,vue路由为我们提供了路由导航组件:router-link组件

  tag 定义渲染的标签(默认是a标签)

    渲染成a标签,通过a标签的href属性实现切换

    渲染成其它标签,通过js实现切换。

  to 定义目标地址,必须要定义的,

    即使是hash路由,也不要以#开始。

  router-link组件与a标签相比,router-link组件会适配不同的策略

十一、路由过渡

我们切换页面的时候,可以添加过度动画。

  在router-view组件的外面,定义transition组件,添加过渡动画。

    mode属性定义切换方式

    appear属性定义是否在加载的时候引入动画。

十二、监听滚动

vue的路由允许我们在切换页面的时候,改变滚动条的位置。

我们在路由实例化对象中,通过scrollBehavior方法监听页面切换

  第一个参数表示当前路由对象

  第二个参数表示上一个路由对象

  第三个参数表示当前滚动条位置

    x表示横向滚动条位置

    y表示纵向滚动条位置

  返回值表示新的滚动条位置。

04 路由导航 + 过渡 + 滚动条.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <!-- <a href="#/home">home</a>
        <a href="#/list/10">list</a>
        <a href="#/detail/100">detail</a> -->
        <router-link tag="div" to="/home">home</router-link>
        <router-link  to="/list/10">list</router-link>
        <router-link  to="/detail/20">detail</router-link>
        <hr>
        <!-- 添加过渡 -->
        <transition  name="dzx" appear mode="out-in">
            <router-view></router-view>
        </transition>
    </div>

    <!-- <script src="./dist/04.js"></script> -->

    <!-- 使用服务器 -->
    <script src="/dist/04.js"></script>
    
</body>
</html>

04 .js



import Vue from 'vue';

import Router from 'vue-router';

// 注意 在这里引入样式的时候会报错  应该加载 package.json 
// 引入样式
import './demo.scss'

// 安装路由
Vue.use(Router);

// 定义组件
let Home = { template:'<div class="home"><h6>home page</h6></div>' }

let List = {
    // 接收数据
    props:['page','path','hash','num'],
    template:`
        <div class="list">
            <h6>list page</h6>
            <hr>
            <h6>直接渲染元素 ---{{page}} --- {{path}}   ----  {{hash}}   ----  {{num}}</h6>
        </div>
    `,
    created() {
        console.log(this)
    },
 }

let Detail = { 
    template:`
        <div class="detail">
            <h6>detail page</h6>
            <!-- 定义子路由 -->
            <router-view></router-view>
        </div>
    `
}

// 定义子路由
let Demo = {
    template:`
        <div>
            <em>demo  page</em>
        </div>
    `
}

// 定义路由规则
let routes = [
    { name:'home',path:'/home',component:Home },
    {
        name:'list',
        path:'/list/:page',
        component:List,
        // 传递数据
        props(route){
            return{
                page:route.params.page,
                path:route.page,
                hash:route.hash,
                num:route.query.num
            }
        }
    },
    {
        name:'detail',
        path:'/detail/:id',
        component:Detail,
        // 定义子路由
        children:[
            // 绝对路径
            { path:'/demo',component:Demo },
            // 相对路径
            { path:'search/:page',component:{
                template:`
                    <div>
                        <p>search  page</p>
                    </div>
                `
            } }
        ]
    },
    // 定义默认页面
    { path:'*',component:Home }
]

// 进行实例化
let router = new Router({ 
    routes,
    // 定义路由 策略
    // mode:'history',
    // 监听滚动条位置
    // scrollBehavior (to, from, savedPosition) {
    //     console.log(to.name,from,savedPosition,"222222");
    //     if (to.name === 'list') {
    //         return { y: 200 }
    //     } else {
    //         return { y : 0 }
    //     }
    // }
 })



let app = new Vue({
    el: '#app',
    // 安装路由
    router,
    data: {
        msg: 'hello page'
    }
})

demo.scss


.home,
.list,
.detail{
    height: 1000px;
}

.home{
    border: 1px solid green;
}

.list{
    border: 1px solid yellow;
}

.detail{
    border: 1px solid blue;
}

/* 添加过渡 */
.dzx-enter,
.dzx.line-to{
    // 注意 在添加滚动条时 这里不能设置高度 
    height: 0;
    opacity: 0;
}
.dzx-enter-active,
.dzx-live-active{
    transition: all 1s;
}


通过右键的 效果图
在这里插入图片描述
通过服务器的效果图
在这里插入图片描述

十三、路由守卫

路由守卫就是监听路由的切换(改变)。

在vue中有三种方式可以监听路由的改变:

  第一种 全局路由守卫
    可以监听所有的页面的切换

    通过对路由实例化对象定义beforeEach,afterEch等方法,来监听

      第一个参数表示当前路由对象

      第二个参数表示上一个路由对象

      如果是beforeEach有第三个参数,表示next方法,必须要执行,否则看不到新的页面。

  第二种 局部路由守卫

    可以监听当前页面的路由的切换。

    在组件实例化对象中,定义beforeRouteEnter,beforeRouteLeave,beforeRouteUpdate等

    方法监听。

      第一个参数表示当前路由对象,

      第二个参数表示上一个路由对象,

      第三个参数表示next方法,必须执行,

  第三种 在watch监听器中,监听$route数据的改变。

    第一个参数表示当前的路由对象,

    第二个参数表示上一个路由对象

  注意:

    为了页面载入的时候可以被监听,可以配合局部路由守卫一起使用。

    为了让页面消失的时候也可以监听路由,可以配置keep-alive组件使用。
05 路由守卫.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <div id="app">
        <h1>{{msg}}</h1>
        <hr>
        <router-link to='/home'>home</router-link>
        <router-link to='/list/10'>list</router-link>
        <router-link to='/detail/20'>detail</router-link>

        <hr>
        <transition name="dzx" appear mode="out-in">
            <!-- 防止组件被销毁 -->
            <keep-alive>
                <router-view></router-view>
            </keep-alive>
        </transition>
    </div>

    <!-- 相对路径 -->
    <!-- <script src="./dist/05.js"></script> -->

    <!-- 绝对路径 -->
    
    <script src="dist/05.js"></script>
</body>
</html>

05 .js



import Vue from 'vue';

import Router  from 'vue-router';

// 引入样式
import './demo.scss';

Vue.use(Router);


// 定义组件
let Home = { template:'<div class="home"><h6>home page</h6></div>' }

let List = {
    // 接收数据
    props:['page','path','hash','num'],
    template:`
        <div class="list">
            <h6>list page</h6>
            <hr>
            <h6>直接渲染元素 ---{{page}} --- {{path}}   ----  {{hash}}   ----  {{num}}</h6>
        </div>
    `,
    // 局部的路由守卫
    // beforeRouteEnter (...args) {
    //     console.log(args,"局部的路由守卫");
    // },
    // 监听路由变化
    beforeRouteEnter (to, from, next) {
        // console.log(to,from,next);
        // 执行放行函数
        next()
    },
    // watch 监听 路由 变化
    watch:{
        $route(){
            console.log(arguments,"arguments");
        }
    },
    created() {
        console.log(this)
    },
 }

let Detail = { 
    template:`
        <div class="detail">
            <h6>detail page</h6>
            <!-- 定义子路由 -->
            <router-view></router-view>
        </div>
    `
}

// 定义子路由
let Demo = {
    template:`
        <div>
            <em>demo  page</em>
        </div>
    `
}


// 定义路由规则
let routes = [
    { name:'home',path:'/home',component:Home },
    {
        name:'list',
        path:'/list/:page',
        component:List,
        // 传递数据
        props(route){
            return{
                page:route.params.page,
                path:route.page,
                hash:route.hash,
                num:route.query.num
            }
        }
    },
    {
        name:'detail',
        path:'/detail/:id',
        component:Detail,
        // 定义子路由
        children:[
            // 绝对路径
            { path:'/demo',component:Demo },
            // 相对路径
            { path:'search/:page',component:{
                template:`
                    <div>
                        <p>search  page</p>
                    </div>
                `
            } }
        ]
    },
    // 定义默认页面
    { path:'*',component:Home }
]



// 进行实例化
let router = new Router({
    routes,
    // 定义路由 策略
    mode:'history',
})

// 添加全局的路由守卫
// router.afterEach((...args)=> console.log(args,"全局的路由守卫"))
// router.beforeEach((route,oldRoute,next)=>{
//     console.log(route,oldRoute,next);
//     // 必须执行放行函数
//     next()
// })

let app = new Vue({
    el:"#app",
    // 注册路由
    router,
    data:{
        msg:'hello page'
    }
})

demo.scss


.home,
.list,
.detail{
    height: 1000px;
}

.home{
    border: 1px solid green;
}

.list{
    border: 1px solid yellow;
}

.detail{
    border: 1px solid blue;
}

/* 添加过渡 */
.dzx-enter,
.dzx.line-to{
    // 注意 在添加滚动条时 这里不能设置高度 
    height: 0;
    opacity: 0;
}
.dzx-enter-active,
.dzx-live-active{
    transition: all 1s;
}


通过 右键的方式访问 效果图
在这里插入图片描述
** 通过 服务器访问的 效果图**
在这里插入图片描述
注意 : 在使用服务器启动的时候 应该先放开 当前 js 文件 路由实例中的 mode:'history"
如果正常右键启动 应该注释此位置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大闸蟹~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值