导读
前端路由就是Hash地址与组件之间的对应关系。
前端路由的工作方式
- 用户点击了页面上的路由链接
- 导致了URL地址栏中的hash值发生了变化
- 前端路由监听到了hash地址的变化
- 前端路由把当前Hash地址对应的组件渲染到浏览器中
路由的基本用法
vue-router安装与配置
安装vue-router
npm i vue-router@3.5.2 -S
创建路由模块
在src目录下自己创建/router/index.js 路由模块
并在路由模块index.js中初始化如下代码
// 导入Vue和VueRouter的包 import Vue from 'vue' import VueRouter from 'vue-router' // 导入需要的组件 import Home from '@/components/tabs/Home.vue' import Movie from '@/components/tabs/Movie.vue' import About from '@/components/tabs/About.vue' // 调用vue专门装插件的函数 Vue.use(),把VueRouter安装为Vue的插件 Vue.use(VueRouter) // 创建路由的实例对象 const router = new VueRouter({ // routers是定义hash地址与组件之间的对应关系 routes:[ //重定向的路由规则,访问/的时候,通过redirect属性跳转到/home对应的路由规则 {path:'/',redirect:'/home'}, //路由规则 {path:'/home',component:Home}, {path:'/movie',component:Movie}, {path:'/about',component:About} ] } ) // 向外共享路由的实例对象 export default router
在Vue项目中,必须把路由实例对象通过main.js进行挂载
import Vue from 'vue' import App from './App.vue' //import router from '@/router' 如果给定文件夹,会默认导入名为index.js的文件 import router from '@/router/index.js' Vue.config.productionTip = false new Vue({ render: h => h(App), // 在Vue项目中,必须把路由实例对象通过main.js进行挂载 // router:router router }).$mount('#app')
在app中放入路由链接和占位符
<template> <div class="box"> <h1>app</h1> <!--1. 创建hash地址 --> <router-link to="/home">首页</a> <router-link to="/movie">电影</a> <a href="#/about">关于</a> <hr> <!--2. 只要在项目中安装和配置了vue-router, 就可以使用router-view这个组件充当占位符 --> <router-view></router-view> </div> </template>
<router-view v-slot="props"> <transition name="donghua"> <keep-alive> <component :is="props.Component"></component> </keep-alive> </transition> </router-view>
嵌套路由
通过路由实现组件的嵌套展示。
在某个路由中(about.vue)放入子路由链接和占位符
<template> <div> <h1>about</h1> <hr> <!-- 子级路由链接 --> <router-link to="/about/tab1">tab1</router-link> <router-link to="/about/tab2">tab2</router-link> <hr> <!-- 子级路由占位符 --> <router-view></router-view> </div> </template> <script> export default { } </script> <style> </style>
在index.js里通过children属性声明子路由规则
// 创建路由的实例对象 const router = new VueRouter({ // routers是定义hash地址与组件之间的对应关系 routes:[ {path:'/',redirect:'/home'}, {path:'/home',component:Home}, {path:'/movie',component:Movie}, {path:'/about', component:About, //重定向的子路由规则,这里也可以使用默认子路由 redirect:'/about/tab1', children:[ // 子路由规则 {path:'tab1',component:Tab1}, {path:'tab2',component:Tab2} ]} ] } )
默认子路由
如果children数组中,某个路由规则的path值为空字符串,则这条路由规则就是默认子路由,只不过此时的对应的子路由链接要去掉子路由链接的名字。当展示父组件的时候,会立马展示这个默认子路由。
// 创建路由的实例对象 const router = new VueRouter({ // routers是定义hash地址与组件之间的对应关系 routes:[ {path:'/',redirect:'/home'}, {path:'/home',component:Home}, {path:'/movie',component:Movie}, {path:'/about', component:About, redirect:'/about/tab1', children:[ //以“/”开头的嵌套路径会被当作新的根路径, //所以子路由上可以根据情况不用加“/”; //如果不加'/',那在生成路由时,主(父亲)路由上的path会被自动添加到子路由之前, //所以子路由上的path不用在重新声明主路由上的path了。 // 空字符串所在的路由就是默认子路由 {path:'',component:Tab1}, {path:'tab2',component:Tab2} ]} ] } )
子路由链接修改为下面部分,相当于第二个路由链接,少了个路径参数。
<template> <div> <h1>about</h1> <hr> <!-- 子级路由链接 --> <router-link to="/about">tab1</router-link> <router-link to="/about/tab2">tab2</router-link> <hr> <!-- 子级路由占位符 --> <router-view></router-view> </div> </template> <script> export default { } </script> <style> </style>
动态路由
它可以提高路由规则的复用性
在index.js中动态路由使用:把Hash地址中可变的部分定义为动态参数项。
// 创建路由的实例对象 const router = new VueRouter({ // routers是定义hash地址与组件之间的对应关系 routes:[ {path:'/',redirect:'/home'}, {path:'/home',component:Home}, //根据id的值展示电影的详细信息 {path:'/movie/:mId',component:Movie}, {path:'/about', component:About, redirect:'/about/tab1', children:[ // 子路由规则 {path:'',component:Tab1}, {path:'tab2',component:Tab2} ]} ] } )
在app中定义路由链接
<template> <div class="box"> <h1>app</h1> <!--1. 创建hash地址 --> <a href="#/home">首页</a> <router-link to="/movie/1">西游记</router-link> <router-link to="/movie/2">水浒传</router-link> <router-link to="/movie/3">三国演义</router-link> <a href="#/about">关于</a> <hr> <!--2. 只要在项目中安装和配置了vue-router, 就可以使用router-view这个组件充当占位符 --> <router-view></router-view> </div> </template>
可以在对应的路由组件中获得mId(方法1)。
<template> <div> <!--this可以省略,并且$route是路由的参数对象,而$router是路由的导航对象--> <h1>movie---{{this.$route.params.mId}}</h1> <button @click="showthis">打印this</button> <hr> </div> </template> <script> export default { name:'Movie', methods:{ showthis(){ console.log(this) } } } </script> <style> </style>
为路由规则开启props传参(方法2)
在路由规则中修改成下面的代码
{path:'/movie/:mId',component:Movie, props:true}, 可以动态拿到:mId的值
在组件中接收传过来的值
<template> <div> <h1>movie---{{mId}}</h1> <button @click="showthis">打印this</button> <hr> </div> </template> <script> export default { name:'Movie', props:['mId'], methods:{ showthis(){ console.log(this) } } } </script> <style> </style>
query与fullPath
- 在hash地址中,/后面的参数项,叫做路径参数
- 在路由“参数对象”中,需要使用this.route.params来访问路径参数
- 在hash地址中,?后面的参数项,叫查询参数
- 在路由“参数对象中”,需要使用$route.query来访问查询参数
- 在$route中,path只是路径部分,fullPath是完整的地址
keep-alive
它可以保证被隐藏的组件不会被销毁。原理是它可以把内部的组件进行缓存,而不是销毁组件。
<keep-alive> <component :is="flag"></component> </keep-alive>
keep-alive对应的生命周期函数
- 当组件被缓存时,会自动触发组件的deactivated生命周期函数。
- 当组件被激活时,会自动触发组件的activated生命周期函数。
在left组件中使用的例子:
data(){ return { str:'zzl', count:0 } }, //当组件第一次被创建的时候,既会执行created生命周期函数,也会执行activeated生命周期函数, //当组件被激活的时候,只会触发activated生命周期,不再触发created,因为组件没有被重新创建。 created(){ console.log('组件被创建了') }, deactivated(){ //在App组件里隐藏left组件 console.log('组件被缓存了') }, //在App组件里显示left组件 activated(){'组件被激活了'},
include 属性
它可以指定哪些组件可以被缓存。left这些组件是data拿到的。
//指定left和right组件可以被缓存 <keep-alive include="left,right> <component :is="flag"></component> </keep-alive>
exclude属性
它可以指定哪些组件不可以被缓存,即排除,它和include不能同时使用。
实际开发中,在子组件export default中如果指定了name名称,那么在其他组件中指定哪些组件可以被缓存的时候就要使用这个指定的name名称,如果不指定就是按之前学的components,只是注册的名称,仅供当标签使用。
应用场景
- 组件的注册名称:以标签的形式,把注册好的组件,渲染和使用到页面结构中
- 组件声明时候的name名称:结合<keep-alive>标签实现组件缓存功能,以及在调试工具中看到组件的name名称。
导航
声明式导航
- 在浏览器中,点击链接实现导航的方式,称为声明式导航,例如<a>链接与vue项目中的<router-link>。
编程式导航
- 在浏览器中,调用API方法实现导航的方式,称为编程式导航,例如普通网页中调用浏览器的location.href跳转到新页面的方式。
编程式导航
vue-router提供了很多编程式导航API,但是最常用的是:
- this.$router.push(‘hash 地址’)
- 跳转到指定hash地址,并增加一条历史记录。
methods:{ goLink(){ this.$router.push('/movie/1') } }
- this.$router.replace(‘hash地址’)
- 跳转到指定地址,不增加历史记录。
- this.$router.go(数值)
- 可以在浏览历史中前进和后退(超过上限就会原地不动)。不过也可以这样写
- $router.back() 后退上一个页面
- $router.forward() 前进下一个页面
- 可以在浏览历史中前进和后退(超过上限就会原地不动)。不过也可以这样写
导航守卫
它可以控制路由的访问权限。有很多导航守卫,可以在router官网找到相应api。
全局前置守卫
每次发生路由的导航切换之前,都会触发全局前置守卫。
router.beforeEach((to, from) => { // return false //不进行导航 // return 字符串 //跳转到对应的路径中 //return 对象 //{path:'',...} if (to.meta.login_require && !getStore('token')) { return { path: '/login', query: { redirect: to.fullPath }, } } })
//创建路由实例对象 const router = new VueRouter({...}) //声明全局前置守卫,接收回调函数,只要发生了路由的跳转,必然会触发这个回调函数 //router.beforeEach(fn) router.beforeEach(function(to,from,next){ //to是将要访问的路由的信息对象 //from是将要离开的路由的信息对象 //next是一个函数,调用next()表示放行,允许这次路由导航。 } )
next函数的3种调用方式
- 比如,当用户拥有访问权限,直接放行: next()
- 没有访问权限,强制跳转到登陆页面:next(‘/login’)
- 没有访问权限,直接忽略,即原地不动: next(false)
举例
router.beforeEach(function(to,from,next){ // 1.要拿到用户将要访问的地址 // 2.判断地址是否等于需要权限的页面地址 // 2.1如果等于就需要登陆之后才能访问,如果不等于需要权限的地址,则next()放行 // 3.如果访问的地址是/main,则需要读取localStorage中的token值 // 3.1.如果等于,则可以访问,否则需要登陆 if(to.path==='/main'){//判断是否想去需要权限的页面地址 const token=localStorage.getItem('token') if(token){ next()//访问的是后台主页,且有token的值 }else{ //访问的是后台主页,但是没有token值,所以跳转到登陆页面 next('/login') } }else{ next()//访问的不是后台主页,直接放行 } })
路由后置守卫
初始化的时候被调用、每次路由切换之前被调用
router.afterEach((to,from)=>{ document.title=to.meta.title })
路由元信息
当需要登陆验证的网页多了,比如用户信息里的各种列表(user/nicheng、user/orders、user/info等),那么如果还用全局前置守卫里的举例就不合适了,此时可以用路由元信息。它可以实现只要地址中包含user就会被限制,即登陆验证。
配置路由的时候添加一个自定义meta对象,在meat中设置一个状态,用来判断这个路由是否需要登陆验证。
path:'/home',component:Home, meta:{login_require:true} }
然后在beforeEach中判断是否为true就行,为true就登陆验证。
router.beforeEach((to, from, next) => { if (to.matched.some(function (roterPeizhi) { return routerPeizhi.meta.login_require })) { next('/login') } else next() })
动态添加路由
在vue-router4中,当需要对不同用户注册不同路由时,可以使用动态添加路由。
import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/login', name: 'Login', component: () => import(/* webpackChunkName: "user" */ '../views/Login.vue') }, ] const router = createRouter({ history: createWebHistory(), routes, }) // 动态添加路由 //1 const cate = { path:'/cate', component:() => import(/* webpackChunkName: "user" */ '../views/Userinfo.vue') } // if(管理员权限) // 添加顶级路由 //2 router.addRoute(cate) //也可以在name为login下添加二级路由对象 router.addRoute('Login',{ path:'test', component:() => import(/* webpackChunkName: "user" */ '../views/Userinfo.vue') }) export default router