一、概念
vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。
(vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。路由实际上就是可以理解为指向,就是我在页面上点击一个按钮需要跳转到对应的页面,这就是路由跳转)
二、安装和使用
vue3
npm i vue-router
vue2
npm ivue-router@3
导入下载的vue-router,使用这个插件,并挂在vm的router上
main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from "vue-router"
import router from "./router/index.js"
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
render: h => h(App),
router,
}).$mount('#app')
三、简单路由
新建一个router文件夹,把路由配置文件放到index.js里
创建需要的组件导入,用导入的vue-router方法实例化对象,在对象
里设置routes这个对象数组,对象数组里设置path和component
以及path带“/”,子级不带。
import VueRouter from "vue-router"
import About from "../pages/about.vue";
import Home from "../pages/home.vue";
import News from "../pages/news.vue";
import Message from "../pages/message.vue";
import Detail from '../pages/Detail.vue';
import detail2 from "../pages/detail2.vue";
import test1 from "../pages/t1.vue"
const Router= new VueRouter({
routes:[
{
path:"/about",
component:About,
},
{
path:"/home",
component:Home,
},
]
})
export default Router
在需要跳转的组件里配置路由跳转标签
<router-link class="list-group-item " active-class="active" to="/about">
about
</router-link>
<!--replace这次点击替换上一次-->
<router-link class="list-group-item " active-class="active" to="/home">
home
</router-link>
router标签解析后会变成a标签,根据to属性写的路径决定跳转,
active-class是显示当前路径是否包括to里的指向组件的路径,直观感受当前组件是否
显示,如果显示则该类激活。
<router-view></router-view>
还有个给路由组件展示的标签
四、嵌套路由
设计到一个children属性
在路由表里面设置
import VueRouter from "vue-router"
import About from "../pages/about.vue";
import Home from "../pages/home.vue";
import News from "../pages/news.vue";
import Message from "../pages/message.vue";
import Detail from '../pages/Detail.vue';
import detail2 from "../pages/detail2.vue";
import test1 from "../pages/t1.vue"
const Router= new VueRouter({
mode:"history",
routes:[
{
name:"aboutroute",
path:"/about",
component:About,
},
{
name:"homeroute",
path:"/home",
component:Home,
children:[
{
name:"NEWS",
path:"news",
component:News,
},
{
name:"messageroute",
path:"message",
component:Message,
}
]
},
]
})
export default Router
这里将home对应的子路由设置在home的children下,涉及到一个新属性name,
用来简化路径的和设计后面传参的,在多级路由的时候可以使用name来跳转
<router-link class=" list-group-item" active-class="active" :to="{name:'messageroute'}">
message
</router-link>
<router-link class=" list-group-item" active-class="active" to="/home/news">
news
</router-link>
五、路由传参
1、模板字符串传参
<router-link class="list-group-item" :to="`/home/message/detail?id=${m.id}&title=${m.title}`">
{{ m.title }}
</router-link>
?跟的是需要传的参数,利用es6里面的模板字符串可以传变量的特性
2、query传参
vm和vc都可以访问自己独特的route属性,这个route属性里面有个query对象属性可以接受跳转过来的组件携带参数,这样想列表或者可以复用数据的组件可以很好的利用
<router-link class="list-group-item" :to="{
name:'detailroute',
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
</router-link>
读取
<li>消息标题:{{ $route.query.title }}</li>
这里的对象也可以写path
3、params传参
vm这个route属性里面有个params对象属性可以接受跳转过来的组件携带参数
params只能用name
<router-link class="list-group-item" :to="{
name:'detail2route',
params:{
id:news.id,
title:news.title
}
}">
{{ news.title }}
</router-link>
读取
<li>消息标题:{{ $route.params.title }}</li>
4、props属性的开启方式
(1)对象形式
children:[
{
name:"detailroute",
path:"detail",
component:Detail,
props:{a:1,b:"h1"}
}
]
这个用的很了解一下
(2)布尔值形式(仅限params)
children:[
{
name:"detailroute",
path:"detail",
component:Detail,
props:true
}
]
这样会使得所有传给这个组件的params参数作为props传给这个组件
(3)函数形式
props({query:{id,title}}){//两层解构赋值
return {id,title}
},
这里通过 return 返回的值来发送props,这个函数可以得到route参数,通过解构赋值能很好的传参,做到query的props
接收就和普通的props接收一模一样
props:["id"]
六、编程式路由
1、replace和push
这个路由的历史纪录是压栈式,如果你用的是push历史记录会一直进到一个栈里
如果是replace模式会使得这次浏览记录替换上一次的浏览记录
默认是push
<router-link class=" list-group-item" active-class="active" to="/home/news" replace>
news
</router-link>
2、非router-link跳转
router-link默认被浏览器解析成a标签,有时候不符合我的需求
这时候只需要在我们需要跳转地方添加相应的事件如点击事件
绑定相应的回调里面有这个方法执行就好
methods:{
pushShow(m){
this.$router.push({
name:"detailroute",
query:{
id:m.id,
title:m.title
}
})
},
replaceShow(m){
this.$router.replace({
name:"detailroute",
query:{
id:m.id,
title:m.title
}
})
}
}
七、缓存路由和路由特有的钩子
1、缓存路由
由于路由跳转后原来的路由就被销毁了,我们在表格填写的信息没有提交的话就一起消失了,为了避免这种情况,设置了缓存路由
被标签包住的就可以不被销毁
<keep-alive include="news">
<!---这里发include放的是组件名 也可以放几个: :include="['news','message']" 不写就全缓存-->
<router-view></router-view>
</keep-alive>
2、路由特有的钩子
<h1 :style="{opacity:opacity}">学习Vue是一个长期的事情</h1>
activated(){//路由组件独有的,被激活的生命周期
this.timer=setInterval(() => {
this.opacity-=0.01
if(this.opacity<=0){
this.opacity=1
}
console.log("run")
}, 40);
},
deactivated(){//路由组件独有的,被切换走的生命周期
clearInterval(this.timer)
}
设置了缓存之后组件跳转后不会销毁,这时候用mounted,beforeDestory不在合适
八、路由守卫
1、全局路由守卫
(1)前置路由守卫
//全局前置路由守卫,初始化和切换前调用
Router.beforeEach((to,from,next)=>{
if(to.meta.isAuth){
if(localStorage.getItem("school")=='hy'){
next()
}
else{
alert("学校名不对")
}
}
else{
next()
}
})
前置路由守卫是指在路由发生跳转时路由对用户的验证,查看用户是否有权限访问这个组件,用next()决定是否访问
这个meta是路由表的一个属性,叫做元数据,因为路由表的属性都是固定的,不可随便更改,故我们要加信息在路由表里都设置在meta里
例如我设置是否要验证的属性isAuth
children:[
{
name:"detail2route",
path:"detail2/:id/:title",
component:detail2,
props:true, //所有的params传参数作为props传到组件。
meta:{
title:"详情2"
},
},
{
path:"test1",
name:" test1",
component:test1,
meta:{
title:"测试1"
},
}
],
meta:{
isAuth:true,
title:"新闻"
}
},
{
name:"messageroute",
path:"message",
component:Message,
children:[
{
name:"detailroute",
path:"detail",
component:Detail,
//改对象的key-value会以props的形式传给组件,只适用于params
// props:{a:1,b:"h1"}
props({query:{id,title}}){//两层解构赋值
return {id,title}
},
meta:{
title:"详情1"
}
}
]
(2)后置的路由守卫
//后置守卫在初始化和切换后被调用了
Router.afterEach((to,from)=>{
document.title=to.meta.title|| "demo"
})
2、独享路由守卫
全局的是每次路由跳转都要检查,也是写在路由表外面,
独享是写在对应的路由表里面,只检查对应的路由表
{
name:"NEWS",
path:"news",
component:News,
beforeEnter:(to,from,next)=>{
console.log("独享路由守卫被使用")
next()
}
}
独享只有一种守卫
3、组件守卫
要注意的是这个beforeRouterLeave是下一次跳转的时候才触发的
beforeRouteEnter(to,from,next){
console.log("about的组件路由守卫被触发")
next()
},
beforeRouteLeave(to,from,next){
console.log("about下一次离开被调用")
next()
}
九、hash模式和history模式
在路由表的对象里可以设置mode属性
const Router= new VueRouter({
mode:"history",
routes:[
{
name:"aboutroute",
path:"/about",
component:About,
meta:{
title:"关于"
}
}
]
history模式下就是简单的网址加路由路径
默认是hash模式,下面是网址加/#/加路由路径
一般来说hash的兼容性更好,因为服务器不会处理/#/(hash符号)后年的哈希值(是前端的hash值和加密无关)
如果是history模式下,因为发网络请求网址直接带着路由路径,而服务器里没有相应的资源,会出现得不到的现象。
当然借助后端插件或者正则都可以解决
比如在npm网站里的
link
就是输入npm install --save connect-history-api-fallback
然后使用就history没问题
hash对于一些手机app会认为非法。
十、样例源码分享
public/index.js
<!DOCTYPE html>
<html lang="">
<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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" >
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
上面的直接引用bootstarp
router/index.js
import VueRouter from "vue-router"
import About from "../pages/about.vue";
import Home from "../pages/home.vue";
import News from "../pages/news.vue";
import Message from "../pages/message.vue";
import Detail from '../pages/Detail.vue';
import detail2 from "../pages/detail2.vue";
import test1 from "../pages/t1.vue"
const Router= new VueRouter({
mode:"history",
routes:[
{
name:"aboutroute",
path:"/about",
component:About,
meta:{
title:"关于"
}
},
{
name:"homeroute",
path:"/home",
component:Home,
meta:{
title:"主页"
},
children:[
{
name:"NEWS",
path:"news",
component:News,
beforeEnter:(to,from,next)=>{
console.log("独享路由守卫被使用")
next()
},
children:[
{
name:"detail2route",
path:"detail2/:id/:title",
component:detail2,
props:true, //所有的params传参数作为props传到组件。
meta:{
title:"详情2"
},
},
{
path:"test1",
name:" test1",
component:test1,
meta:{
title:"测试1"
},
}
],
meta:{
isAuth:true,
title:"新闻"
}
},
{
name:"messageroute",
path:"message",
component:Message,
children:[
{
name:"detailroute",
path:"detail",
component:Detail,
//改对象的key-value会以props的形式传给组件,只适用于params
// props:{a:1,b:"h1"}
props({query:{id,title}}){//两层解构赋值
return {id,title}
},
meta:{
title:"详情1"
}
}
],
meta:{
isAuth:true,
title:"消息"
}
}
]
},
]
})
//全局前置路由守卫,初始化和切换前调用
Router.beforeEach((to,from,next)=>{
if(to.meta.isAuth){
if(localStorage.getItem("school")=='hy'){
next()
}
else{
alert("学校名不对")
}
}
else{
next()
}
})
//后置守卫在初始化和切换后被调用了
Router.afterEach((to,from)=>{
document.title=to.meta.title|| "demo"
})
export default Router
main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from "vue-router"
import router from "./router/index.js"
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
render: h => h(App),
router,
}).$mount('#app')
components/banner.vue
<template>
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header">
<h2>Vue Router Demo</h2>
<button @click="back">后退</button>
<button @click="forward">前进</button>
</div>
</div>
</template>
<script>
export default {
name:"banner",
methods:{
forward(){
this.$router.forward()
},
back(){
this.$router.back()
}
}
}
</script>
<style>
</style>
pages/about.vue
<template>
<div>
<h2>这是about</h2>
</div>
</template>
<script>
export default {
name:"about",
//由路由规则的进入组件之前调用,组件路由守卫
beforeRouteEnter(to,from,next){
console.log("about的组件路由守卫被触发")
next()
},
beforeRouteLeave(to,from,next){
console.log("about下一次离开被调用")
next()
}
}
</script>
<style>
</style>
pages/home
<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="{name:'messageroute'}">message</router-link>
</li>
</ul>
<keep-alive include="news"> <!---这里发include放的是组件名 也可以: :include="['news','message']"-->
<router-view></router-view>
</keep-alive>
</div>
</div>
</template>
<script>
export default {
name:"home"
}
</script>
<style>
</style>
page/message
<template>
<div>
<ul>
<li v-for="m in messagelist" :key="m.id">
<!-- <router-link class="list-group-item" :to="`/home/message/detail?id=${m.id}&title=${m.title}`">
{{ m.title }}
</router-link> -->
<router-link class="list-group-item" :to="{
name:'detailroute',
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>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:"message",
data(){
return {
messagelist:[
{id:"001",title:"消息1"},
{id:"002",title:"消息2"},
{id:"003",title:"消息3"}
]
}
},
methods:{
pushShow(m){
this.$router.push({
name:"detailroute",
query:{
id:m.id,
title:m.title
}
})
},
replaceShow(m){
this.$router.replace({
name:"detailroute",
query:{
id:m.id,
title:m.title
}
})
}
}
}
</script>
<style>
</style>
pages/news
<template>
<div>
<ul>
<h1 :style="{opacity:opacity}">学习Vue是一个长期的事情</h1>
<li v-for="news in newslist" :key="news.id">
<router-link class="list-group-item" :to="{
name:'detail2route',//params只能用name
params:{
id:news.id,
title:news.title
}
}">
<!-- <router-link class="list-group-item" :to="`/home/news/detail2/${news.id}/${news.title}`"> -->
{{ news.title }}
</router-link>
<input type="text" >
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:"news",
data(){
return {
newslist:[
{id:"001",title:"新闻1"},
{id:"002",title:"新闻2"},
{id:"003",title:"新闻3"}
],
opacity:1
}
},
/*mounted(){
this.timer=setInterval(() => {
this.opacity-=0.01
if(this.opacity<=0){
this.opacity=1
}
}, 40);
},
beforeDestroy(){
clearInterval(this.timer)
}*/
activated(){//路由组件独有的,被激活的生命周期
this.timer=setInterval(() => {
this.opacity-=0.01
if(this.opacity<=0){
this.opacity=1
}
console.log("run")
}, 40);
},
deactivated(){//路由组件独有的,被切换走的生命周期
clearInterval(this.timer)
}
}
</script>
<style>
</style>
pages/Detail
<template>
<ul>
<li>消息编号:{{ id }}</li>
<li>消息标题:{{ $route.query.title }}</li>
</ul>
</template>
<script>
export default {
name:"Detail",
props:["id","title"],
mounted(){
console.log(this)
}
}
</script>
<style>
</style>
pages/detail2
<template>
<div>
<ul>
<li>消息编号:{{ id }}</li>
<li>消息标题:{{ $route.params.title }}</li>
</ul>
</div>
</template>
<script>
export default {
name:"detaila",
mounted(){
console.log(this.$route)
},
props:["id","title"]
}
</script>
<style>
</style>
pages/t1.vue
<template>
<div>
<h1> 测试成功!</h1>
</div>
</template>
<script>
export default {
name:"t1"
}
</script>
<style>
</style>
运行截图
开发者工具