Vue2.x 基本认识四:路由
路由认识
概念
路由(route)就是一组(key -value)对应关系。多个路由由一个路由器(router)管理。
key 为路径,value 可能是 function 或 component 。
function 是后端路由,用于请求服务器获取数据。
component 是前端路由,用于展示页面内容。但浏览器的路径改变时,对应的组件就会显示。
作用
实现单页面应用。
对 SPA 应用的理解:
- 单页面应用(singe page web application,SPA)
- 整个页面只有一个完整的页面
- 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
- 数据需要通过 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
路由基本使用
注意
- 组件分为一般组件、路由组件。写组件标签。通过 components 配置项配置的组件是一般组件;路由器控制挂载展示的组件是路由组件。
- 一般组件,放在在 components 文件夹中;路由组件,则放在 pages 文件夹中。这两个目录都在 src 目录下。
- 被切换走的路由组件,默认是被销毁的,需要的时候再去挂载。所以如果有消息订阅和全局事件注册,需要在 beforeDestroy() 函数中取消订阅和解除事件绑定。
- 每个组件都有自己的 $route 属性,里面存放这自己的路由信息。
- 整个应用只有一个 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:占位
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构