目录
[2.1 声明式导航](#2.1 声明式导航)
[2.2 声明式导航路由传参](#2.2 声明式导航路由传参)
[2.2.3 query 传参和 param 传参总结](#2.2.3 query 传参和 param 传参总结)
[2.3 命名路由](#2.3 命名路由)
[2.4 可选操作符](#2.4 可选操作符)
[2.5 props 参数](#2.5 props 参数)
[3.1 replace 和 push 跳转方法](#3.1 replace 和 push 跳转方法)
[3.2 back、forword、go 方法](#3.2 back、forword、go 方法)
[5.1 重定向](#5.1 重定向)
[5.2 404](#5.2 404)
[5.3 多级路由](#5.3 多级路由)
[5.4 路由模式配置](#5.4 路由模式配置)
[5.6 SPA 的理解](#5.6 SPA 的理解)
[6.1 语法](#6.1 语法)
[6.2 属性](#6.2 属性)
[6.3 两个生命周期钩子](#6.3 两个生命周期钩子)
[7.1 全局路由守卫](#7.1 全局路由守卫)
[7.2 独享路由守卫](#7.2 独享路由守卫)
[7.3 组件内路由守卫](#7.3 组件内路由守卫)
一、路由的使用
- 下载 VueRouter 到当前项目,如果是vue2项目需要指定路由版本,因为现在脚手架默认下载vue3的版本。
 
vue2 的 VueRouter 版本为 3.x , vuex 的版本为 3.x ;
vue3 的 VueRouter 版本为 4.x , vuex 的版本为 4.x ;
npm i vue-router@3.6.5
        - 
在 main.js 中引入 VueRouter
import VueRouter from 'vue-router'
 - 
注册
Vue.use(VueRouter)
 - 
创建路由对象
const router = new VueRouter()
 - 
在main.js 入口文件注入到 vue 实例中
new Vue({
render: h => h(App),
router:router
}).$mount('#app') - 
引入路由组件,并注册
 
单独一个 router.js 文件存放路由信息

在 router/index.js 引入 router.js

- 在 App.vue 中配置路由出口(配对成功路由显示的位置)
 

二、声明式导航
2.1 声明式导航
vue-router 提供了一个全局组件 router-link,类似 a 标签的作用
<router-link to="跳转路径">购物车</router-link>
        - 能跳转,配置 to 属性指定路径(必须) ,本质还是 a 标签 ,to 无需 #
 - 能高亮,默认就会提供高亮类名,可以直接设置高亮样式(router-link-exact-active 和 router-link-active)
 - router-link-exact-active: 精确匹配,只能匹配一层,如 to="/user"
 - router-link-exact-active: 模糊匹配,可以匹配多层,如 to="/user/a" to="/user/b"
 
我们可以在创建路由对象时自定义上面两个类名
const router = new VueRouter({
  routes: routes,
  linkActiveClass: "自定义名1",
  linkExactActiveClass: "自定义名2"
})
        2.2 声明式导航路由传参
2.2.1.字符串写法
1. 携带 query 参数
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
        获取参数:
$route.query.参数名
如: route.query.id route.query.title
注意: 在 template 中可以直接用上述方法接收参数,如果在 script 的 vue 实例对象中,还需要加 this
2. 携带 param 参数
链接位置,路径后面直接带传递的值, 如 666 你好
<router-link :to="路径/666/你好">跳转</router-link>
        路由配置时需要使用冒号(:)占位符,如 :id 和 :title
const router = new VueRouter({
    routes: [
        ...,
        path:'detail/:id/:title', //使用占位符声明接收params参数
    ]
 
})
        获取参数:
$route.params.占位参数名
如:route.params.id route.params.title
注意: 在 template 中可以直接用上述方法接收参数,如果在 script 的 vue 实例对象中,还需要加 this
2.2.2.对象写法
1. 携带 query 参数:
<router-link :to="{
	path:'/home/message/detail',
	query:{
		id:666,
        title:'你好'
	}
}">跳转</router-link>
        路由配置
new VueRouter({
    routes:[
        {
            path:'/home',
            component:Home,
            children:[
                {
                    path:'news',
                    component:News
                },
                {
                    path:'message',
                    component:Message,
                    children:[
                        {
                            path:'detail',
                            component:Detail
                        }
                    ]
                }
            ]
        }
    ]
})
        获取数据
$route.query.参数名
如: route.query.id route.query.title
2. 携带 param 参数:
携带 param 参数时, 对象写法跳转不能使用path,一定要用name配置项。但是 query 可以写 path
<router-link :to="{
    name:'xiangqing',
    params:{
          id: 666,
           title: '你好'
    }">
     {{m.title}}
</router-link>
        路由配置
const router = new VueRouter({
    routes: [
        ...,
        { 
            name: 'xiangqing'
            path:'detail/:id/:title'
        }, //使用占位符声明接收params参数
    ]
 
})
        获取参数
$route.params.参数名
如:route.params.id route.params.title
2.2.3 query 传参和 param 传参总结
query 传参
跳转:to="/path?参数名=值&参数名2=值"
获取:$route.query.参数名
param 传参
配置动态路由:path: "/path/:参数名"
跳转:to="/path/参数值"
获取:$route.params.参数名
注意:
动态路由也可以传多个参数,但一般只传一个。
使用param 对象写法传参,跳转路由要使用 name 属性,不能使用 path 属性。
3.需要在路由配置中设置占位参数
2.3 命名路由
给路由添加一个 name 属性,在路由跳转时便可根据 name 名称来路由跳转。但是在 <router-link>标签的 to 要写成对象的形式。这里我给子路由都添加 name 属性
const router = new VueRouter({
  routes:[
	{
		path:'/about',
		component:About,
	},
	{
		path:'/home',
		component:Home,
		children:[ //通过children配置子级路由
			{
                name: 'xingwen'
				path:'news', //此处一定不要写:/news
				component:News
			},
			{
                name: 'xiaoxi'
				path:'message', //此处一定不要写:/message
				component:Message
			}
		]
	}
  ]
})
        简化跳转
<!--简化前,需要写完整的路径 -->
<router-link to="/home/news">跳转</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'xingwen'}">跳转</router-link>
<!--简化写法配合传递参数 -->
<router-link 
	:to="{
		name:'xingwen',
		query:{
		    id:666,
            title:'你好'
		}
	}"
>跳转</router-link>
        2.4 可选操作符
/路径/:keywords表示 必须要传参数。如果不传参数也希望匹配,可以加个可选符 "?"
如:
const router = new VueRouter({
  routes: [
 	...
    { path: '/路径/:keywords?', component: Search }
  ]
})
        2.5 props 参数
new VueRouter({
    routes:[
        {
            path:'/home',
            component:Home,
            children:[
                {
                    path:'news',
                    component:News
                },
                {
                    path:'message',
                    component:Message,
                    children:[
                        {
                            name:'xiangqing',
                            path:'detail/:id/:title',
                            component:Detail,
                            //props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件。
							// props:{a:1,b:'hello'}
							//props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件。
							// props:true
							//props的第三种写法,值为函数
							props($route){
								return {
									id:$route.params.id,
									title:$route.params.title,
								}
							}
                        }
                    ]
                }
            ]
        }
    ]
})
        detail.vue 组件,接收 props 参数
<template>
    <ul>
        <li>消息编号:{{id}}</li>
        <li>消息标题:{{title}}</li>
    </ul>
</template>
<script>
    export default {
        name:'Detail',
        props:['id','title']
    }
</script>
        三、编程式导航
3.1 replace 和 push 跳转方法
在跳转标签 <router-link> 标签上添加 replace 属性
<template>
    <div>
        <h2>Home组件内容</h2>
		<div>
			<ul class="nav nav-tabs">
				<li>
					<router-link replace class="list-group-item" active-class="active" to="/home/news">News</router-link>
				</li>
				<li>
					<router-link replace class="list-group-item" active-class="active" to="/home/message">Message</router-link>
				</li>
			</ul>
			<router-view></router-view>
		</div>
    </div>
</template>
<script>
    export default {
        name:'Home'
    }
</script>
        
浏览器历史记录有两种方式,分别是 push 和 replace , 其中 push 是追加历史记录, replace 是替换当前记录。路由跳转默认是 push 方式。
开启 replace 模式,只需要在 <router-link> 标签添加 replace 属性
3.2 back、forword、go 方法
<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>
			<button @click="test">测试一下go</button>
            <button @click="test1">测试一下go</button>
            <button @click="test2">测试一下go</button>
		</div>
	</div>
</template>
<script>
	export default {
		name:'Banner',
		methods:{
			back(){
				this.$router.back()   // 后退
			},
			forward(){
				this.$router.forward()  // 前进
			},
			test(){
				this.$router.go()  // 可前进可后退
			},
            test1(){
				this.$router.go(2)  // 前进2次
			},
            test2(){
				this.$router.go(-1)  // 后退1次
			}
		},
	}
</script>
        四、声明式导航与编程式导航小结
声明式导航(通过router-link组件实现,包括to属性及传参方法)和编程式导航(利用router.push或router.replace函数)。声明式导航适用于静态或预定义的路由切换,而编程式导航在动态生成或复杂跳转场景下更为灵活。当数据量大时,为避免内存消耗和卡顿,推荐使用编程式导航。
五、扩展
5.1 重定向
默认 url 地址端口后面只带一个\ , 如果没有匹配成功,就会出现空白页
解决: 为 \ 设置一个路由,如果匹配的是 \ , 就重定向到首页
{ path: 匹配路径, redirect: 重定向到的路径 },
比如:
{ path:'/' ,redirect:'/index' }
        5.2 404
如果找不到该页面,就显示为 404页面,在使用路由基础模板时,需要把匹配 404 的路由放在最后面。
import NotFind from '@/views/NotFind'
 
const router = new VueRouter({
  routes: [
    ...
    { path: '*', component: NotFind } //最后一个
  ]
})
        5.3 多级路由
在一级路由里面添加 children 属性,它是一个数组。children 中的配置项 跟一级路由中的配置项一样
const router = new VueRouter({
  routes:[
	{
		path:'/about',
		component:About,
	},
	{
		path:'/home',
		component:Home,
		children:[ //通过children配置子级路由
			{
				path:'news', //此处一定不要写:/news
				component:News
			},
			{
				path:'message', //此处一定不要写:/message
				component:Message
			}
		]
	}
  ]
})
        跳转(要写完整路径),如 <router-link to="/home/message">信息</router-link>
5.4 路由模式配置
vue 导航栏模式有两种,分别是 hash 模式(vue 默认该模式)和 history 模式。
hash 模式带 # , 如 localhost:8080/#employee
history 模式不带 #, 如 localhost:8080/employee
切换 vue 路由模式
const router = new VueRouter({
    mode:'histroy', //默认是hash
    routes:[]
})
        区别:
- 
hash 模式不会把 # 后面的路径传递给服务器,而 history 模式会把 url 上的所有地址都传递给服务器
 - 
history 模式刷新后会出现 404 问题,需要依靠后端解决,hash 模式没有
 
5.6 SPA 的理解
单页 Web 应用(single page web application,SPA)
整个应用只有一个完整的页面
点击新页面中的导航链接不会刷页面,只会做页面的局部更新
数据需要通过ajax请求获取
六、缓存组件
从首页列表点到详情页,又点返回,数据重新加载了 → 希望回到原来的位置
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们
6.1 语法
在 router-view 路由出口标签上再包裹一个 keep-alvie 标签
<template>
  <div class="h5-wrapper">
    <keep-alive>
      <router-view></router-view>
    </keep-alive>
  </div>
</template>
        6.2 属性
include : 组件名数组,只有匹配的组件会被缓存
exclude : 组件名数组,任何匹配的组件都不会被缓存
max : 最多可以缓存多少组件实例
<template>
  <div class="h5-wrapper">
    <keep-alive :include="['LayoutPage']">  //include中写想要缓存的组件名,不写表示全部缓存
      <router-view></router-view>
    </keep-alive>
  </div>
</template>
//Layout.vue
 
//...
export default {
  name: "LayoutPage",    //如果没有配置name,则会找文件名作为组件名
}
        6.3 两个生命周期钩子
keep-alive的使用会触发两个生命周期函数。组件缓存后 就不会执行 组件的created, mounted, destroyed 等钩子了。所以其提供了actived 和deactived钩子,帮我们实现业务需求
activated 当组件被激活(使用)的时候触发 → 进入这个页面的时候触发
deactivated 当组件不被使用的时候触发 → 离开这个页面的时候触发
七、路由守卫
7.1 全局路由守卫
//该文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
//引入组件
import Home from '../pages/Home'
import About from '../pages/About'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
//创建一个路由器
const router = new VueRouter({
    routes:[
        {
            name:'guanyv',
            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:'详情'},
							props($route){
								return {
									id:$route.query.id,
									title:$route.query.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
        7.2 独享路由守卫
//该文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
//引入组件
import Home from '../pages/Home'
import About from '../pages/About'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
//创建一个路由器
const router = new VueRouter({
    routes:[
        {
            name:'guanyv',
            path:'/about',
            component:About,
            meta:{title:'关于'}
        },
        {
            name:'zhuye',
            path:'/home',
            component:Home,
            meta:{title:'主页'},
            children:[
                {
                    name:'xinwen',
                    path:'news',
                    component:News,
                    meta:{title:'新闻'},
                    //独享守卫,特定路由切换之后被调用
                    beforeEnter(to,from,next){
                        console.log('独享路由守卫',to,from)
                        if(localStorage.getItem('school') === 'atguigu'){
                            next()
                        }else{
                            alert('暂无权限查看')
                        }
                    }
                },
                {
                    name:'xiaoxi',
                    path:'message',
                    component:Message,
                    meta:{title:'消息'},
                    children:[
                        {
                            name:'xiangqing',
                            path:'detail',
                            component:Detail,
                            meta:{title:'详情'},
							props($route){
								return {
									id:$route.query.id,
									title:$route.query.title,
								}
							}
                        }
                    ]
                }
            ]
        }
    ]
})
//全局后置路由守卫------------初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
	console.log('后置路由守卫',to,from)
	document.title = to.meta.title || '硅谷系统'
})
//导出路由器
export default router
        7.3 组件内路由守卫
<template>
    <h2>我是About组件的内容</h2>
</template>
<script>
    export default {
        name:'About',
        //通过路由规则,离开该组件时被调用
        beforeRouteEnter (to, from, next) {
            console.log('About--beforeRouteEnter',to,from)
            if(localStorage.getItem('school')==='atguigu'){
                next()
            }else{
                alert('学校名不对,无权限查看!')
            }
        },
        //通过路由规则,离开该组件时被调用
        beforeRouteLeave (to, from, next) {
            console.log('About--beforeRouteLeave',to,from)
            next()
        }
    }
</script>