路由 router
router是由Vue官方提供的用于实现组件跳转的插件
路由插件的引用
离线
<script type="text/javascript" src="js/Vue.js" ></script>
<script type="text/javascript" src="js/Vue-router.js"></script>
在线CDN
<script src="https://unpkg.com/Vue/dist/Vue.js"></script>
<script src="https://unpkg.com/Vue-router/dist/Vue-router.js"></script>
单独路由使用案例
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title><style type="text/css">body{padding: 0px;margin: 0px;}ul{list-style: none;}ul li{display: inline; float: left; margin-left: 15px; margin-bottom: 15px;}ul li a{text-decoration: none; color: white; font-size: 18px; font-weight: bold;}ul li a:hover{color: yellow;}</style><script type="text/javascript" src="js/Vue.js" ></script><script type="text/javascript" src="js/Vue-router.js"></script></head><body><div id="container"><div style="width: 100%; height: 70px; background: #00BFFF;"><table><tr><td><img src="img/logo.png" height="70" style="margin-left:100px;"/></td><td><ul><li><router-link to="/a">首页</router-link></li><li><router-link to="/b">Java</router-link></li><li><router-link to="/c">HTML5</router-link></li><li><router-link to="/d">Python</router-link></li></ul></td></tr></table></div><div style="width: 100%; height: 680px; background: lemonchiffon;"><router-view></router-view></div></div><script type="text/javascript">// Vue的路由旨在为单页面应用开发提供便捷//1.定义链接跳转的模板(组件)const t1 = {template:`<p>index</p>`};const t2 = {template:`<p>Java</p>`};const t3 = {template:`<p>HTML5</p>`};const t4 = {template:`<p>PYTHON</p>`};const myrouter = new VueRouter({routes:[{path:"/a",component:t1},{path:"/b",component:t2},{path:"/c",component:t3},{path:"/d",component:t4}]});var vm = new Vue({el:"#container",router:myrouter});</script></body>
</html>
项目中如何定义路由
1\安装路由
npm install vue-router@3.5.1
对于vue2,我们推荐使用vue-router 3.x版本。若大于4.x,则部分功能无法在vue2中正常引入使用。
2\创建路由文件router.js以及创建相关文件
// 该文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";// 创建一个路由器
const router = new VueRouter({routes: [// {path: '/',name: 'HelloWorld',component: HelloWorld},{path: "/parent", name: "parent", component: () => import("@/components/Parent"),// 注意。除了一级路径其他的级别不加“/”children: [{path: "/childA", name: "childA", component: () => import("@/components/ChildA")},{path: "/childB", name: "childB", component: () => import("@/components/ChildB")}]},]
});export default router;
3\应用插件main.js
// 引入VueRouter
import VueRouter from "vue-router";
// 引入路由文件
import routers from "./router.js";new Vue({el: '#app',render: h => h(App),router: routers
}
4\实现切换<router-link></router-link>
Parent.vue
<template><div><router-link to="/childA">ChildA</router-link><router-link to="/childB">ChildB</router-link><router-view/></div>
</template><script>
export default {name: "Parent"
}
</script><style scoped></style>
5\展示位置 <router-view></router-view>
同上
嵌套路由children
在一级路由的组件中显示二级路由
注意
- 跳转要写完整路径
<router-link to="/a/c2">首页-c2</router-link>
- 子路由不要加“/”,会自动加
import Vue from 'vue'
import Router from 'vue-router'
// import HelloWorld from '@/components/HelloWorld'Vue.use(Router)export default new Router({routes: [// {path: '/',name: 'HelloWorld',component: HelloWorld},{path: "/parent", name: "parent", component: () => import("@/components/Parent"),// 注意。除了一级路径其他的级别不加“/”children: [{path: "/childA", name: "childA", component: () => import("@/components/ChildA")},{path: "/childB", name: "childB", component: () => import("@/components/ChildB")},{path: "/childB/:id", name: "childB", component: () => import("@/components/ChildB")},]},]
})
路由优先级
如果一个路径匹配了多个路由,则按照路由的配置顺序:路由定义的越早优先级就越高。
动态路由匹配-*
、:
在Vue中,动态路由是指路由的路径参数或查询参数可以根据实际情况动态变化。通过使用动态路由参数,我们可以在路由配置中设置参数,并在对应的组件中通过this.$route
对象获取这些参数。
动态路由案例
1、定义路由配置:
const router = new VueRouter({routes: [// 动态路由参数以冒号":“开头 { path: '/user/:id', component: User }]
})
2、在组件中获取动态路由参数:
const User = {template: '<div>User ID: {{ $route.params.id }}</div>',created() {console.log('User ID:', this.$route.params.id)}
}
3、导航到带有动态参数的路由:
// 在Vue实例中,可以使用编程方式进行导航
this.$router.push('/user/123')
在上述例子中,当访问/user/123
时,User
组件将显示内容“User ID: 123”。通过this.$route.params.id
可以访问到路由参数id
的值。
通配符
*
可以匹配任意路径
例如:
/user-*
匹配所有以user-
开头的任意路径/*
匹配所有路径
const myrouter = new VueRouter({routes:[{path:"/user-*",component:...},{path:"/*",component:...}]
});
注意
如果使用通配符定义路径,需要注意路由声明的顺序
路由参数
/a/:id
可以匹配/a/
开头的路径
<div id="container"><li><router-link to="/a/101">首页</router-link></li><router-view></router-view>
</div><script type="text/javascript">const t1 = {template:`<p>index:{{$route.params.id}}</p>`};const myrouter = new VueRouter({routes:[{path:"/a/:id",component:t1}]});var vm = new Vue({el:"#container",router:myrouter});
</script>
接收参数/获取地址栏信息$route
,$router
this.$route:当前激活的路由的信息对象。每个对象都是局部的,可以获取当前路由的 path, name, params, query 等属性。
this.$router:全局的 router 实例。通过 vue 根实例中注入 router 实例,然后再注入到每个子组件,从而让整个应用都有路由功能。其中包含了很多属性和对象(比如 history 对象),任何页面也都可以调用其 push(), replace(), go() 等方法。
$route
中存在请求信息
$route.query
获取请求信息
路由传参
router-link路由导航方式传参
<router-link to="/childA?id=123">ChildA</router-link>
<!-- 跳转并携带query参数,to的字符串写法 -->
<div v-for="item in 3"><router-link :to="`/childA?id=${item}`">ChildA-{{ item }}</router-link>
</div>
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link :to="{path: '/childA',query: {id: 456}
}">ChildA
</router-link>
调用$router实现路由传参
// 直接拼接
<button @click="clickHand(123)">path传参</button>
clickHand(id) {this.$router.push({path: `/childA?id=${id}`})
}
// 通过query来传递参数
<button @click="clickQuery()">query传参</button>
clickQuery() {this.$router.push({path: '/childA',query: {id: '789'}})
},
params传参的两种方式
前提:路由配置参数 {path: "/childB/:id", name: "childB", component: () => import("@/components/ChildB")},
第一种方式
<button @click="ClickByName()">params传参</button>
ClickByName() {// 通过路由属性name匹配路由,再根据params传递参数this.$router.push({name: 'childB',params: { // 使用params不能使用path,必须使用nameid: '666'}
})
第二种方式
<router-link :to="{name: 'childB',params: {id: 456}
}">ChildA
</router-link>
路由的props配置/不使用$route接收参数
作用,让路由更方便的接收参数,在代码中的体现就是,this. r o u t e . i d 直接就可以赋值给 i d ,省略 t h i s . route.id直接就可以赋值给id,省略this. route.id直接就可以赋值给id,省略this.route
方式一 布尔值
{path: "/childA/:id", name: "childA", component: () => import("@/components/ChildA"),props:true},
访问/#/childA/456
把路由收到的所有params参数通过props传给childA组件,然后childA组件就可以接收使用 props:['id']
,这时候childA组件就有了一个id=456的变量
方式二 对象
{path: "/childA/:id", name: "childA", component: () => import("@/components/ChildA"),props:{id:999}},
访问/#/childA/456
直接把{id:999}通过props传给childA组件,然后childA组件就可以接收使用 props:['id']
,这时候childA组件就有了一个id=999的变量
这种方式硬编码,实际中不适用
方式三 函数
{path: "/childA/:id", name: "childA", component: () => import("@/components/ChildA"),props(route) {return { id: route.query.id } // route.params.id}
},
props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
路由跳转/编程式导航($router)
路由跳转分为编程式和声明式。
声明式:
-
简单来说,就是使用 router-link 组件来导航,通过传入 to 属性指定链接(router-link 默认会被渲染成一个a标签)。
-
当需要在一个页面中嵌套子路由,并且页面不跳转的时候,这种方式不要太好用啊哈哈哈… 只需要将子页面渲染在 router-view 里面就可以了。
编程式:
- 采用这种方式就需要导入 VueRouter 并调用了。
具体使用如下
<router-link>
标签
to 属性的使用方式与.push相同
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>replace —— 点击<router-link> 时默认调用 router.push()方法,增加此属性则调用 router.replace()
<router-link :to="{ path: '/abc'}" replace></router-link>
push()
会向 history 栈添加一个新的记录,地址栏会变化
//1.字符串
this.$router.push("/a");//2.对象
this.$router.push({path:"/a"});//3.命名的路由 name参数指的是定义路由时指定的名字
this.$router.push({name:"r1",params:{id:101}});//4.URL传值,相当于/a?id=101
this.$router.push({path:"/a",query:{id:101}});
replace()
功能与push一致,区别在于replace()不会向history添加新的浏览记录
go()
参数为一个整数,表示在浏览器历史记录中前后/后退多少步 相当于
window.history.go(-1)
的作用
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)// 后退一步记录,等同于 history.back()
router.go(-1)// 前进 3 步记录
router.go(3)// 如果 history 记录不够用,则失败,不会跳转
router.go(-100)
.forward()
前进——跳转到下一路由(若浏览器存在history 记录)
.back()
后退——跳转到上一路由(若浏览器存在history 记录)
缓存路由组件keep-alive
作用:让不展示的路由组件保持挂载,不被销毁。
具体编码:
<keep-alive><router-view></routhr-view>
</keep-alive>
这样当切换组件时,router-view显示的组件的数据都不会变化
重定向redirect
访问/b
,重定向到/a
<div id="container"><router-link to="/a">路径A</router-link><router-link to="/b">路径B</router-link><router-view></router-view>
</div>
<script type="text/javascript">const t1 = {template:"<div style='width:400px; height:200px; border:blue 1px solid'>index</div>"};const myrouter = new VueRouter({routes:[{path:"/a",component:t1},{path:"/b",redirect:"/a"}]});var vm = new Vue({el:"#container",router:myrouter});
</script>
- 根据路由命名重定向
const myrouter = new VueRouter({routes:[{path:"/a",name:"r1",component:t1},{path:"/b",//redirect:"/a" //根据路由路径重定向redirect:{name:"r1"} //根据路由命名重定向}]
});
路由别名alias
/a
的别名是 /b
,意味着,当用户访问 /b
时,URL 会保持为 /b
,但是路由匹配则为 /a
,就像用户访问 /a
一样。
<div id="container"><router-link to="/a">路径A</router-link><router-link to="/wahaha">路径wahaha(别名)</router-link><router-view></router-view>
</div>
<script type="text/javascript">const t1 = {template:"<div style='width:400px; height:200px; border:blue 1px solid'>index</div>"};const myrouter = new VueRouter({routes:[{path:"/a",alias:"/wahaha",component:t1}]});var vm = new Vue({el:"#container",router:myrouter});
</script>
路由元信息 meta
通过meta可以为路由添加更多自定义的配置信息
{path: 'bar',component: Bar,meta: { requiresAuth: true }}
路由模式 mode(通常用默认的hash模式)
-
对于一个url来说,什么是hash值?#及其后面的内容就是hash值。
-
hash值不会包含在HTTP请求中,即:hash值不会带给服务器。
-
hash模式:
-
地址中永远带着#号,不美观。
-
若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
-
兼容性较好。
-
-
history模式:
- 地址干净,美观。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
const router = new VueRouter({// 不用mode: 'history'时,页面url地址后面会加上一个“#”mode: 'history',routes
})
路由基路径 base
默认值: "/"
,如果整个单页应用服务在 /app/
下,然后 base
就应该设为 "/app/"
const router = new VueRouter({base:'/app/',routes
})
路由守卫
参数
- to:要跳转的路由页面的路由信息对象 有to.path to.其他
- from:跳转前的页面的路由信息对象
- next:就是去匹配路由然后加载组件,相当于重定向,next()收尾 不传参(路由) 否则死循环
分类
-
全局路由守卫:每个路由跳转,都会执行
-
router.beforeEach((to, from, next) => {next() }) // afterEach没有next router.afterEach((to, from) => {// ... })
-
-
局部路由守卫(路由独享守卫)
-
{path: '/foo',component: Foo,beforeEnter: (to, from, next) => {// ...} }
-
-
组件内的路由守卫:在.vue文件中定义,与vue的生命周期函数同级。
-
// 导航进入该组件的对应路由时调用 // 不能获取组件实例 `this`因为当守卫执行前,组件实例还没被创建 beforeRouteEnter(to, from, next) {//应用场景://如果你想要在进入路由前,根据路由参数或查询参数,做一些初始化操作,例如获取数据或设置组件状态。// 如果你想要在进入路由前,通过路由守卫来验证某些条件,例如用户权限验证 },// 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` beforeRouteLeave(to, from, next) {// 使用场景:通常用来禁止用户在还未保存修改前突然离开const answer = window.confirm('修改未保存,确定离开吗?')if (answer) {next()} else {next(false)} }
-
全局路由守卫应用场景-权限校验
router.beforeEach((to, from, next) => {// 需鉴权的路由—— 判断路由的meta元数据中,是否requiresAuth为trueif (to.matched.some(record => record.meta.requiresAuth)) {// 判断用户是否已登录if (sessionStorage.getItem('isLogin')) {// 已登录,则跳转到目标路由(需要用户登录的页面)next()} else {// 未登录,跳转到登录页next('/login')}} else {// 公共页面,直接跳转到目标路由next()}
})
meta 元数据
{path: '/add',name: 'add',component: Add,meta: { requiresAuth: true }},
全局路由守卫应用场景-— 路由跳转时,页面标题随路由变化
router.beforeEach((to, from, next) => {/* 路由跳转时,页面标题随路由变化——将新的页面标题修改为路由名称 */if (to.name) {document.title = to.name+' -- 页面'}next()
})
自动注册路由require.context
import Vue from 'vue'
import Router from 'vue-router'Vue.use(Router)// 查询指定文件夹下的vue页面,自动注册路由
// require.context(directory,useSubdirectories,regExp)
// directory:表示检索的目录
// useSubdirectories:表示是否检索子文件夹
// regExp:匹配文件的正则表达式,一般是文件名
const contextInfo = require.context('../components/', true, /.vue$/);
let routerAry = []
contextInfo.keys().forEach(fileName => {const pathConfig = contextInfo(fileName)routerAry.push({path:"/" + fileName.substring(2,fileName.length-4),// pathConfig.default的内容,是指定的vue页面的default模块内容,等同于: () => import('xxxx')component: pathConfig.default})
})const routerList = [{path: "/Parent", name: "parent", component: () => import("@/components/Parent"),},
]
const newRouterAry = routerList.concat(routerAry)export default new Router({routes:newRouterAry
})