Vue2.x 基本认识四:路由

路由认识

概念

路由(route)就是一组(key  -value)对应关系。多个路由由一个路由器(router)管理。

key 为路径,value 可能是 function 或 component 。

function 是后端路由,用于请求服务器获取数据。

component 是前端路由,用于展示页面内容。但浏览器的路径改变时,对应的组件就会显示。

作用

实现单页面应用。

SPA 应用的理解:

  1. 单页面应用(singe page web application,SPA)
  2. 整个页面只有一个完整的页面
  3. 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
  4. 数据需要通过 ajax 请求获取

安装

要使用路由,需要用到插件库:vue-router

注意:2022年2月7日,Vue3 成为了默认版本,Vuex、vue-router 也更新到了4版本了,且 4 是默认版本。这两个插件的 4 版本都只能在 Vue3 中使用。Vue2 要指定安装 3 版本的插件。

安装命令:npm i vue-router@3

安装最新版:npm i vue-router

 

路由基本使用

注意

  1. 组件分为一般组件、路由组件。写组件标签。通过 components 配置项配置的组件是一般组件;路由器控制挂载展示的组件是路由组件。
  2. 一般组件,放在在 components 文件夹中;路由组件,则放在 pages 文件夹中。这两个目录都在 src 目录下。
  3. 被切换走的路由组件,默认是被销毁的,需要的时候再去挂载。所以如果有消息订阅和全局事件注册,需要在 beforeDestroy() 函数中取消订阅和解除事件绑定。
  4. 每个组件都有自己的 $route 属性,里面存放这自己的路由信息。
  5. 整个应用只有一个 router,可以通过组件的 $router 属性获取到。

引入与应用 vue-router 插件

在安装完毕插件的基础上。

main.js 中加入下面内容:

import Vue from 'vue'
import App from "./App.vue"

// 引入 vue-router 插件
import VueRouter from 'vue-router'
// 引入自定义路由器(src/router/index.js)
import router from './router'
// 应用 vue-router 插件
Vue.use(VueRouter)

const vm = new Vue({
    render: h => h(App),
    router: router // 配置路由器
}).$mount("#root")

自定义路由器内容

  自定义路由器写在一个 js 文件中,就是 src/router/index.js

/**
 * 该文件专门用于创建整个应用的路由器
 */
// 引入 vue-router 插件
import VueRouter from 'vue-router'
// 引入自定义组件
import About from '../pages/About'
import Home from '../pages/Home'
import Message from '../pages/Message'
import News from '../pages/News'

// 创建并暴露一个路由器
export default new VueRouter({
    // routes 下面是一级路由
    routes:[
        {
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            // 定义 Home 组件的子路由规则
            children: [
                {
                    path: 'news', // 子路由 path 值前面不要斜杆
                    component: News
                },
                {
                    path: 'message',
                    component: Message
                }
            ]
        }
    ]
})

使用路由:一级路由

<template>
    <div id="app">
        <div class="row">
            <Banner/>
        </div>
        <div class="row">
            <div class="col-xs-2 col-xs-offset-2">
                <div class="list-group">
                    <!-- 使用原始 a 标签跳转页面 -->
                    <!-- <a class="list-group-item active" href="./about.html">About</a>
                    <a class="list-group-item" href="./home.html">Home</a> -->

                    <!-- 
                        使用路由标签跳转页面 
                        使用的是 router-link 标签
                        to 属性的值是路由器中的 path 路径
                    -->
                    <router-link class="list-group-item" active-class="active" to="/about">About</router-link>
                    <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
                </div>
            </div>
            <div class="col-xs-6">
                <div class="panel">
                    <div class="panel-body">
                        <!-- 指定路由组件呈现的位置:通过 router-view 标签 -->
                        <router-view></router-view>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    import Banner from "./components/Banner.vue";
    export default {
        name: 'App',
        components: {Banner},
        data() {
            return {}
        }
    }
</script>

路由使用:多级路由(嵌套路由)

<template>
    <div>
        <h2>我是Home的内容</h2>
        <ul class="nav nav-tabs">
            <li>
                <!-- 使用多级路由时,to 属性必须带上父级路由的 path 路径 -->
                <router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
            </li>
            <li>
                <router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
            </li>
        </ul>
        <div>
            <!-- 路由组件展示的地方 -->
            <router-view></router-view>
        </div>
    </div>

</template>
<script>
export default {
    name: 'Home'
}
</script>

 

路由命名&路由传参

路由命名

简而言之,就是在配置路由地址时,配置 name 属性,后面跳转时可以根据 name 属性值跳转,避免多级路由时,to 属性内容写很长。

/**
 * 该文件专门用于创建整个应用的路由器
 */
// 引入 vue-router 插件
import VueRouter from 'vue-router'
// 引入自定义组件
import About from '../pages/About'
import Home from '../pages/Home'
import Message from '../pages/Message'
import News from '../pages/News'
import Detail from '../pages/Detail'

// 创建并暴露一个路由器
export default new VueRouter({
    // routes 下面是一级路由
    routes:[
        {
            name: 'aboutN',
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            // 定义 Home 组件的子路由规则
            children: [
                {
                    path: 'news', // 子路由 path 值前面不要斜杆
                    component: News
                },
                {
                    path: 'message',
                    component: Message,
                    children: [
                        {
                            name:'detailN',
                            path: 'detail',
                            component: Detail
                        }
                    ]
                }
            ]
        }
    ]
})

 

路由传参(query 传参)& 使用路由名称实现跳转

<template>
    <div>
        <ul>
            <li v-for="item in messages" :key="item.id">
                <!-- 路由跳转,并携带 query 传参,to 字符串写法 -->
                <router-link :to="`/home/message/detail?id=${item.id}&title=${item.title}`">{{ item.title }}</router-link>
                <!-- 路由跳转,并携带 query 传参,to 对象写法 -->
                <router-link 
                    :to="{
                        path: '/home/message/detail',
                        query: {
                            id: item.id,
                            title: item.title
                        }
                    }">
                    {{ item.title }}
                </router-link>
                <!-- 
                    使用路由名称跳转
                    下面 to 属性配置的对象中,name 属性值与路由配置中 detail 路由配置的 name 属性值一致 
                  -->
                <router-link 
                    :to="{
                        name: 'detailN',
                        query: {
                            id: item.id,
                            title: item.title
                        }
                    }">
                    {{ item.title }}
                </router-link>
            </li>
        </ul>
        <hr>
        <router-view></router-view>
    </div>
</template>
<script>
export default {
    name: 'Message',
    data() {
        return {
            messages:[
                {id:'001', title:'消息001'},
                {id:'002', title:'消息002'},
                {id:'003', title:'消息003'}
            ]
        }
    }
}
</script>

 

接收路由 query 传参

<template>
    <div>
        <h3>消息ID:{{ $route.query.id }}</h3>
        <h3>消息标题:{{ $route.query.title }}</h3>
    </div>
</template>
<script>
    export default {
        name: 'Detail'
    }
</script>

 

路由传参(params 传参):在 path 配置中定义占位

/**
 * 该文件专门用于创建整个应用的路由器
 */
// 引入 vue-router 插件
import VueRouter from 'vue-router'
// 引入自定义组件
import About from '../pages/About'
import Home from '../pages/Home'
import Message from '../pages/Message'
import News from '../pages/News'
import Detail from '../pages/Detail'

// 创建并暴露一个路由器
export default new VueRouter({
    // routes 下面是一级路由
    routes:[
        {
            name: 'aboutN',
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            // 定义 Home 组件的子路由规则
            children: [
                {
                    path: 'news', // 子路由 path 值前面不要斜杆
                    component: News
                },
                {
                    path: 'message',
                    component: Message,
                    children: [
                        {
                            name:'detailN',
                            /**
                             * 路由 params 传参必须要占位(下面一共两个占位,占位不懂就学node)
                             * 在 params 传参 to 字符串写法时,参数名称就是冒号后面部分
                             */
                            path: 'detail/:id/:title',
                            component: Detail
                        }
                    ]
                }
            ]
        }
    ]
})

 

路由传参(params 传参):两种写法

<template>
    <div>
        <ul>
            <li v-for="item in messages" :key="item.id">
                <!-- 使用 params 传参,to 字符串写法 -->
                <router-link :to="`/home/message/detail/${item.id}/${item.title}`">{{ item.title }}</router-link>
                <!-- 
                    使用 paras 传参,to 对象写法。
                    注意,对象写法只能根据路由名称跳转,不能使用 path 跳转。
            且,params 对象中,key 是什么,在接收时就用什么
            这里与字符串写法参数名就是占位有点区别,但这种传参也依赖于占位,只是不用占位的名称作为参数名称
--> <router-link :to="{ name: 'detailN', params: { id: item.id, title: item.title } }"> {{ item.title }} </router-link> </li> </ul> <hr> <router-view></router-view> </div> </template> <script> export default { name: 'Message', data() { return { messages:[ {id:'001', title:'消息001'}, {id:'002', title:'消息002'}, {id:'003', title:'消息003'} ] } } } </script>

 

接收路由 params 传参

<template>
    <div>
        <h3>消息ID:{{ $route.params.id }}</h3>
        <h3>消息标题:{{ $route.params.title }}</h3>
    </div>
</template>
<script>
    export default {
        name: 'Detail'
    }
</script>

 

路由的 props 配置

功能:简化路由参数接收。

路由配置中使用 props 配置

/**
 * 该文件专门用于创建整个应用的路由器
 */
// 引入 vue-router 插件
import VueRouter from 'vue-router'
// 引入自定义组件
import About from '../pages/About'
......

// 创建并暴露一个路由器
export default new VueRouter({
    // routes 下面是一级路由
    routes:[
        {
            name: 'aboutN',
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            // 定义 Home 组件的子路由规则
            children: [
                {
                    path: 'news', // 子路由 path 值前面不要斜杆
                    component: News
                },
                {
                    path: 'message',
                    component: Message,
                    children: [
                        {
                            name:'detailN',
                            /**
                             * 路由 params 传参必须要占位(下面一共两个占位,占位不懂就学node)
                             * 在 params 传参 to 字符串写法时,参数名称就是冒号后面部分
                             */
                            // path: 'detail/:id/:title',
                            path: 'detail',
                            component: Detail,

                            // 写法一:值为对象,该对象中所有 key-value 都会以 props 的形式传给当前组件(只能写死key-value)
                            // props: {id:'007', title:'凌凌漆'}
                            // 写法二:值为布尔值,把收到的所有参数,以 props 的形式传给当前组件(只能是 params 传参方式)
                            // props: true
                            // 写法三(推荐):值为函数,把收到的所有参数,以 props 的形式传给当前组件(params 和 query参数都行)
                            props($route) {
                                return {
                                    id: $route.query.id,
                                    title: $route.query.id
                                }
                            }
                        }
                    ]
                }
            ]
        }
    ]
})

 

组件传参方式不变

<template>
    <div>
        <ul>
            <li v-for="item in messages" :key="item.id">
                <!-- 使用 query 传参,to 对象写法。 -->
                <router-link 
                    :to="{
                        name: 'detailN',
                        query: {
                            id: item.id,
                            title: item.title
                        }
                    }">
                    {{ item.title }}
                </router-link>
            </li>
        </ul>
        <hr>
        <router-view></router-view>
    </div>
</template>

 

组件中使用 props 配置接收参数

注意,接收参数名与路由配置 props 中定义的 key 一致

<template>
    <div>
        <h3>消息ID:{{ id }}</h3>
        <h3>消息标题:{{ title }}</h3>
    </div>
</template>
<script>
    export default {
        name: 'Detail',
        props: ['id', 'title']
    }
</script>

 

router-link 标签的属性

replace

控制路由跳转时,操作浏览器历史记录模式,改为 replace 模式。

浏览器记录模式有 push 和 replace 两种模式。push 模式下,每次跳转都会有一条记录,可以前进后退;replace 模式下,每次跳转都是修改当前浏览器记录,不会新增记录,无法前进后退。

 

编程式路由跳转

就是不通过 router-link 标签跳转。

功能:在 js 逻辑中控制组件路由跳转。

主要函数:this.$router.push({...})、this.$router.replace({...})

注释:

这两个函数的作用对应浏览器历史记录的两种模式;

参数与 router-link 标签的 to 属性对象写法时一致。

代码演示:

<template>
    <div>
        <ul>
            <li v-for="m in messageList" :key="m.id">
                <!-- 跳转路由,to的对象写法 -->
                <router-link :to="{
                    name:'xiangqing',
                    query:{
                        id:m.id,
                        title:m.title
                    }
                }">
                    {{m.title}}
                </router-link>
                <button @click="pushShow(m)">push查看</button>
                <button @click="replaceShow(m)">replace查看</button>
            </li>
        </ul>
        <hr>
        <router-view></router-view>
    </div>
</template>

<script>
    export default {
        name:'Message',
        data() {
            return {
                messageList:[
                    {id:'001',title:'消息001'},
                    {id:'002',title:'消息002'},
                    {id:'003',title:'消息003'}
                ]
            }
        },
        methods: {
            pushShow(m){
                this.$router.push({
                    name:'xiangqing',
                    query:{
                        id:m.id,
                        title:m.title
                    }
                })
            },
            replaceShow(m){
                this.$router.replace({
                    name:'xiangqing',
                    query:{
                        id:m.id,
                        title:m.title
                    }
                })
            }
        },
    }
</script>

 

控制前进后退

 主要函数:this.$router.go(Number)、this.$router.back()、this.$router.forward()

<script>
    export default {
        name:'Banner',
        methods: {
            back(){
                this.$router.back()
            },
            forward(){
                this.$router.forward()
            },
            test(){
                // 参数可以是正负数
                this.$router.go(3)
            }
        },
    }
</script>

 

keep-alive 标签(保持组件活跃)

功能:keep-alive 标签可以使不展示的组件保持活跃,不被销毁。

include 属性

  • 指定保持活跃的组件。
  • include 属性的值是组件名称,不是路由里配置的 name,是组件中 name 属性的值。
  • 如果不使用 include 属性,那么就是所有在这里展示的组件都保持活跃。
  • include 属性两种写法:
  • 指定一个需要缓存(保持活跃)的组件:include="组名名"
  • 指定多个需要缓存的组件::include="['组名名1', '组名名n']"
<template>
    <div>
        <h2>Home组件内容</h2>
        <div>
            <ul class="nav nav-tabs">
                <li>
                    <router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
                </li>
                <li>
                    <router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
                </li>
            </ul>
            <!-- 使用 keep-alive 组件 -->
            <keep-alive include="News">
                <router-view></router-view>
            </keep-alive>
        </div>
    </div>
</template>

 

两个新的生命周期函数(路由组件独有的生命周期钩子)

作用:用于捕获路由组件激活状态。

激活状态:就是当前页面展示出来的路由组件,一旦组件被切换走了,就称为失活状态。

失活状态作用:如果组件通过 keep-alive 标签保持组件不被销毁,又需要停止某些定时任务等操作时,可以在这个生命周期函数中处理。

注意:既然是生命周期函数,那么肯定是与 data 配置项同级的。

函数:activated() 路由组件被激活时触发;deactivated() 路由组件失活时触发。

代码演示:

activated() {
    console.log('News组件被激活了')
    this.timer = setInterval(() => {
        console.log('@')
        this.opacity -= 0.01
        if(this.opacity <= 0) this.opacity = 1
    },16)
},
deactivated() {
    console.log('News组件失活了')
    clearInterval(this.timer)
}

 

路由守卫

作用:对路由进行权限控制。

分类:全局守卫、独享守卫、组件内守卫

守卫函数参数解释:to、from、next

to:要去哪(路由跳转的目标组件)
from:来自于哪(被点击的路由组件)
to 和 from 包含的属性:name(路由名称)、path(路由路径)、fullpath(路由全路径)、params(params传参是的参数)、query(query传参时的参数)、meta(路由元信息,程序员自定义信息)
next:这是一个函数,表示放行,如果不主动调用这个函数,那么每次路由切换都会被拦截。

 

全局守卫

全局前置守卫:初始化时执行、每次路由切换前执行

//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
   console.log('beforeEach',to,from)
   if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
      if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
         next() //放行
      }else{
         alert('暂无权限查看')
         // next({name:'guanyu'})
      }
   }else{
      next() //放行
   }
})

全局后置守卫:初始化时执行、每次路由切换后执行

//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
   console.log('afterEach',to,from)
   if(to.meta.title){ 
      document.title = to.meta.title //修改网页的title
   }else{
      document.title = 'vue_test'
   }
})

全局守卫:全代码

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
......

//创建并暴露一个路由器
const router =  new VueRouter({
    routes:[
        {
            name:'guanyu',
            path:'/about',
            component:About,
            meta:{title:'关于'}
        },
        {
            name:'zhuye',
            path:'/home',
            component:Home,
            meta:{title:'主页'},
            children:[
                {
                    name:'xinwen',
                    path:'news',
                    component:News,
                    meta:{isAuth:true,title:'新闻'}
                },
                {
                    name:'xiaoxi',
                    path:'message',
                    component:Message,
                    meta:{isAuth:true,title:'消息'},
                    children:[
                        {
                            name:'xiangqing',
                            path:'detail',
                            component:Detail,
                            meta:{isAuth:true,title:'详情'}
               } ] } ] } ] })
//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用 router.beforeEach((to,from,next)=>{ console.log('前置路由守卫',to,from) if(to.meta.isAuth){ //判断是否需要鉴权 if(localStorage.getItem('school')==='atguigu'){ next() }else{ alert('学校名不对,无权限查看!') } }else{ next() } }) //全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用 router.afterEach((to,from)=>{ console.log('后置路由守卫',to,from) document.title = to.meta.title || '硅谷系统' }) export default router

 

独享守卫

即每个路由单独的守卫。

代码:(注意,这是写在某个路由的具体配置中的,该函数与 path 配置同级)

beforeEnter(to,from,next){
   console.log('beforeEnter',to,from)
   if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
      if(localStorage.getItem('school') === 'atguigu'){
         next()
      }else{
         alert('暂无权限查看')
         // next({name:'guanyu'})
      }
   }else{
      next()
   }
}

 

组件内守卫

级别:与 Vue 组件的 data 配置项平级

两种情况触发:进入组件前、离开组件时

解释:离开组件时和前面的全局守卫切换后触发不同,全家的后置守卫可以理解为切换成功之后触发,组件内的离开时守卫是在离开该组件的前一刻触发。

//通过路由规则,进入该组件前被调用
beforeRouteEnter (to, from, next) {
    console.log('About--beforeRouteEnter',to,from)
    if(to.meta.isAuth){ //判断是否需要鉴权
        if(localStorage.getItem('school')==='atguigu'){
            next()
        }else{
            alert('学校名不对,无权限查看!')
        }
    }else{
        next()
    }
},

//通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
    console.log('About--beforeRouteLeave',to,from)
    next()
}

 

路由器的两种工作模式

1. 对于一个 URL 来说,什么是hash值?—— #及其后面的内容就是hash值。
2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
3. hash模式:

  • 地址中永远带着#号,不美观 。
  • 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
  • 兼容性较好。

4. history模式:

  • 地址干净,美观 。
  • 兼容性和 hash 模式相比略差。
  • 应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题。
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
...

//创建并暴露一个路由器
export default new VueRouter({
    mode:'history', // 两个参数 hash 和 history
    routes:[...]
        
})

 

 

 

 

 

 

 

ps:占位

posted @ 2023-06-26 23:40    阅读(19)  评论(0编辑  收藏  举报