Vue路由

2/18/2020

# 单Web应用(SPA)

SPA(Single Page Web Application)是指整个web应用只有一个页面驱动。主要特点就是点击页面中的导航链接不会刷新页面,只会做页面的局部更新,数据一般通过ajax请求从后端获取。

# 路由

路由是一个比较广义和抽象的概念,其本质是对应关系的映射。key是路径,value可以是组件也可以是函数。

在开发中,路由主要分为两类:

  • 后端路由

    后端路由是根据不同的用户URL请求,返回不同的内容,其本质是URL请求地址和服务器资源之间的对应关系。

    后端路由

  • 前端路由

    前端路由是根据不同的用户事件,显示不同的页面内容,其本质是用户事件与处理函数之间的对应关系。

    前端路由

Vue Router (opens new window)是Vue.js官方的路由管理器。它和Vue.js的核心深度集成,可以非常方便的用于SPA应用程序开发。

# 基本使用

# 安装Vue Router

npm i vue-router

#main.js内引入路由

Vue.use(VueRouter)

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router'

//关闭Vue的生产提示
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	router:router
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 编写router配置项

创建router文件夹和index.js,在内部编写

//引入VueRouter
import VueRouter from 'vue-router'
//引入路由组件
import About from '../components/About'
import Home from '../components/Home'

//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home
		}
	]
})

//暴露router
export default router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<router-link active-class="active" to="/about">About</router-link>
1

router-link最终会被浏览器解释为a标签。active-class可配置高亮样式。

<router-link>还有一个replace属性,它用于控制路由跳转时操作浏览器历史记录的模式,它的取值分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push

# 使用内置标签<router-view>来指定展示位置。

<router-view></router-view>
1

# Example

<template>
  <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
        <div class="page-header"><h2>Vue Router Demo</h2></div>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
					<!-- 原始html中我们使用a标签实现页面的跳转 -->
          <!-- <a class="list-group-item active" href="./about.html">About</a> -->
          <!-- <a class="list-group-item" href="./home.html">Home</a> -->

					<!-- Vue中借助router-link标签实现路由的切换 -->
					<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>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
	export default {
		name:'App',
	}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 几个注意四点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

# 重定向路由

重定向路由是指用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面,比如访问首页/跳转到特定的/user页面。

通过路由规则的redirect属性;指定一个新的路由地址,可以很方便地设置路由的重定向。

//实例化路由对象
const router = new VueRouter({
	//routes 是路由规则数组
	routes:[
		//path 表示当前路由规则匹配的hash地址
		//redirect 表示将要重定向的新地址
		{path:'/',redirect:'/foo'},
		{path:'/foo',component:Foo},
		{path:'/bar',component:Bar}
	]
})
1
2
3
4
5
6
7
8
9
10
11

# 嵌套路由

嵌套路由即当点击父路由链接时显示模板内容,在模板内容中又有子路由链接,点击子路由链接显示子级模板内容。

# 父路由组件模板

定义父路由链接"/user"和"/register",然后定义父组件填充位。

<div id="app">
	<p>
		<router-link to="/user">User</router-link>
		<router-link to="/register">register</router-link>
		<div>
			<router-view></router-view>
		</div>
	</p>
</div>
<script type="text/javascript">
	const User = {
		template:`
			<div>
				<h1>User组件</h1>
			</div>
		`
	}
	
	const Register = {
		template:`
			<div>
				<h1>Register组件</h1>
			</div>
		`
	}
	
	const router = new VueRouter({
		routes:[
			{path:'/user',component:User},
			{path:'/register',component:Register}
		]
	})
	
	const vm = new Vue({
		el:"#app",
		router
	})
	
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# 子组件模板

在父路由组件中添加子路由链接和子路由填充位。

const Register = {
	template:`
		<div>
			<h1>Register组件</h1>
			<!--子组件路由链接 (要写完整路径)-->
			<router-link to="/register/tab1">tab1</router-link>
			<router-link to="/register/tab2">tab2</router-link>
			!--子组件填充位-->
			<router-view></router-view>
		</div>
	`
}

//子组件1
const Tab1 = {
	template:`
		<div>
			<h2>Tab1组件</h2>
		</div>
	`
}

//子组件2
const Tab2 = {
	template:`
		<div>
			<h2>Tab2组件</h2>
		</div>
	`
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

在父级路由配置中通过children属性配置子路由规则。

const router = new VueRouter({
	routes:[
		{path:'/user',component:User},
		{
			path:'/register',
			component:Register,
			//通过children属性,为/register添加子路由规则
			children:[
				{path:'/register/tab1',component:Tab1},
				{path:'/register/tab2',component:Tab2}
			]
			
		}
	]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 路由参数

我们跳转路由的时候如果需要携带参数,那么就需要使用query或者param参数来实现路由参数。

# query参数

  1. 传递参数

    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
    				
    <!-- 跳转并携带query参数,to的对象写法 -->
    <router-link 
    	:to="{
    		path:'/home/message/detail',
    		query:{
    		   id:666,
                title:'你好'
    		}
    	}"
    >跳转</router-link>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  2. 接收参数:

    $route.query.id
    $route.query.title
    
    1
    2

# param参数

  1. 配置路由,声明接收params参数

    {
    	path:'/home',
    	component:Home,
    	children:[
    		{
    			path:'news',
    			component:News
    		},
    		{
    			component:Message,
    			children:[
    				{
    					name:'xiangqing',
    					path:'detail/:id/:title', //使用占位符声明接收params参数
    					component:Detail
    				}
    			]
    		}
    	]
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  2. 传递参数

    <!-- 跳转并携带params参数,to的字符串写法 -->
    <router-link :to="/home/message/detail/666/你好">跳转</router-link>
    				
    <!-- 跳转并携带params参数,to的对象写法 -->
    <router-link 
    	:to="{
    		name:'xiangqing',
    		params:{
    		   id:666,
                title:'你好'
    		}
    	}"
    >跳转</router-link>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

# 路由的props配置

props配置可以让路由组件更方便的接收参数,之前的$route与对应路由形成高度耦合,不够灵活,所以可以使用props将组件和路由解耦。

<div id="app">
	<p>
		<router-link to="/user/1">User1</router-link>
		<router-link to="/user/2">User2</router-link>
		<router-link to="/user/3">User3</router-link>
		<div>
			<router-view></router-view>
		</div>
	</p>
</div>
<script type="text/javascript">
	const User = {
		//路由组件中通过$route.params获取路由参数
		template:`
			<div>User {{$route.params.id}}</div>
		`
	}
	
	const router = new VueRouter({
		routes:[
			//动态路径参数,以冒号开头
			{path:'/user/:id',component:User}
		]
	})
	
	const vm = new Vue({
		el:"#app",
		router
	})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# props值为布尔类型

props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给组件。

<div id="app">
	<p>
		<router-link to="/user/1">User1</router-link>
		<router-link to="/user/2">User2</router-link>
		<router-link to="/user/3">User3</router-link>
		<div>
			<router-view></router-view>
		</div>
	</p>
</div>
<script type="text/javascript">
	const User = {
		props:['id'], //使用props接收路由参数
		template:`
			<div>User {{id}}</div>
		`
	}
	
	const router = new VueRouter({
		routes:[
			//如果props被设置为true,route.params将会被设置为组件属性
			{path:'/user/:id',component:User,props:true}
		]
	})
	
	const vm = new Vue({
		el:"#app",
		router
	})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# props值为对象类型

props值为对象,该对象中所有的key-value的组合最终都会通过props传给组件

<div id="app">
	<p>
		<router-link to="/user/1">User1</router-link>
		<router-link to="/user/2">User2</router-link>
		<router-link to="/user/3">User3</router-link>
		<div>
			<router-view></router-view>
		</div>
	</p>
</div>
<script type="text/javascript">
	const User = {
		props:['id','name','age'], //使用props接收路由参数
		template:`
			<div>
				User {{id}} - {{name}} - {{age}}
			</div>
		`
	}
	
	const router = new VueRouter({
		routes:[
			//如果props是一个对象,它会被按原样设置为组件属性
			{path:'/user/:id',component:User,props:{id:'1',name:'snake8859',age:22}}
		]
	})
	
	const vm = new Vue({
		el:"#app",
		router
	})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# props的值为函数类型

props值为函数,该函数返回的对象中每一组key-value都会通过props传给组件

<div id="app">
	<p>
		<router-link to="/user/1">User1</router-link>
		<router-link to="/user/2">User2</router-link>
		<router-link to="/user/3">User3</router-link>
		<div>
			<router-view></router-view>
		</div>
	</p>
</div>
<script type="text/javascript">
	const User = {
		props:['id','name','age'], //使用props接收路由参数
		template:`
			<div>
				User {{id}} - {{name}} - {{age}}
			</div>
		`
	}
	
	const router = new VueRouter({
		routes:[
			//如果props是一个对象,它会被按原样设置为组件属性
			{
				path:'/user/:id',
				component:User,
				//如果props是一个函数,则这个函数接收router对象为自己的形参
				props:route=>({id:route.params.id,name:'snake8859',age:22})}
		]
	})
	
	const vm = new Vue({
		el:"#app",
		router
	})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 命名路由

为了更加方便的表示路由的路径,可以给路由规则起一个别名,即为“命名路由”。

  1. 给路由命名:

    {
    	path:'/demo',
    	component:Demo,
    	children:[
    		{
    			path:'test',
    			component:Test,
    			children:[
    				{
                          name:'hello' //给路由命名
    					path:'welcome',
    					component:Hello,
    				}
    			]
    		}
    	]
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  2. 简化跳转:

    <!--简化前,需要写完整的路径 -->
    <router-link to="/demo/test/welcome">跳转</router-link>
    
    <!--简化后,直接通过名字跳转 -->
    <router-link :to="{name:'hello'}">跳转</router-link>
    
    <!--简化写法配合传递参数 -->
    <router-link 
    	:to="{
    		name:'hello',
    		query:{
    		   id:666,
                title:'你好'
    		}
    	}"
    >跳转</router-link>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

# 编程式导航

在页面导航里,有两种方式,声明式导航和编程式导航。

  • 声明式导航

    通过点击链接实现导航的方式,例如普通网页中的 <a></a>链接 或 vue 中的<router-link></router-link>

  • 编程式导航

    通过调用JavaScript形式的API实现导航的方式,例如普通网页中的 location.href或者vue中的this.$router.push('hash地址')this.$router.go(n)

# this.$router.push()

用于控制路由的跳转,可传入字符串(路由名称),对象,命名的路由(传递参数)和带查询参数。

<!-- 被 vm 实例所控制的区域 -->
<div id="app">
  <router-link to="/user/1">User1</router-link>
  <router-link to="/user/2">User2</router-link>
  <router-link :to="{ name: 'user', params: {id: 3} }">User3</router-link>
  <router-link to="/register">Register</router-link>

  <!-- 路由占位符 -->
  <router-view></router-view>
</div>
<script type="text/javascript">
	const User = {
		props:['id','name','age'], //使用props接收路由参数
		methods:{
			goRegister:function(id){
				//字符串
				//this.$router.push('/register')
				//对象
				//this.$router.push({path:'/register'})
				//命名路由
				//this.$router.push({name:'register',params:{id:id}})
				//带参数查询,变成/register?id='1'
				//this.$router.push({path:'/register',query:{id:id}})
			}
		},
		template:`
			<div>
				User {{id}} - {{name}} - {{age}}<br/>
				<button @click='goRegister(id)' >跳转注册</button>
			</div>
		`
	}
	
	const Register = {
		props:['id'],
		template:`
			<div>
				<h1>User {{id}} 开始注册组件</h1>
			</div>
		`
	}
	
	const router = new VueRouter({
		routes:[
			{
				path:'/user/:id',
				//name属性,用于为路由规则起名
				name:'user',
				component:User,
				props:route=>({id:route.params.id,name:'snake8859',age:22})
				
			},
			{
				path:'/register',
				name:'register',
				component:Register,
				props:true
			}
		]
	})
	
	const vm = new Vue({
		el:"#app",
		router
	})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

# this.$router.go(n)

用于路由前进或者后台,参数是一个整数,表示再历史记录中向前或者向后退多少步。

<!-- 被 vm 实例所控制的区域 -->
<div id="app">
  <router-link to="/user/1">User1</router-link>
  <router-link to="/user/2">User2</router-link>
  <router-link :to="{ name: 'user', params: {id: 3} }">User3</router-link>
  <router-link to="/register">Register</router-link>

  <!-- 路由占位符 -->
  <router-view></router-view>
</div>
<script type="text/javascript">
	const User = {
		props:['id','name','age'], //使用props接收路由参数
		methods:{
			goRegister:function(id){
				//字符串
				//this.$router.push('/register')
				//对象
				//this.$router.push({path:'/register'})
				//命名路由
				this.$router.push({name:'register',params:{id:id}})
				//带参数查询,变成/register?id='1'
				//this.$router.push({path:'/register',query:{id:id}})
			}
		},
		template:`
			<div>
				User {{id}} - {{name}} - {{age}}<br/>
				<button @click='goRegister(id)' >跳转注册</button>
			</div>
		`
	}
	
	const Register = {
		props:['id'],
		methods:{
			goBack:function(){
				this.$router.go(-1)
			}
		},
		template:`
			<div>
				<h1>User {{id}} 开始注册组件</h1>
				<button @click="goBack">后退</button>
			</div>
		`
	}
	
	const router = new VueRouter({
		routes:[
			{
				path:'/user/:id',
				//name属性,用于为路由规则起名
				name:'user',
				component:User,
				props:route=>({id:route.params.id,name:'snake8859',age:22})
				
			},
			{
				path:'/register',
				name:'register',
				component:Register,
				props:true
			}
		]
	})
	
	const vm = new Vue({
		el:"#app",
		router
	})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

# 缓存路由

使用内置的<keep-alive>标签,能够不展示的路由保持挂载,不被销毁。

<!-- 缓存多个路由组件 -->
<keep-alive :include="['News','Message']">

<!-- 缓存一个路由组件 -->
<keep-alive include="News">
    <router-view></router-view>
</keep-alive>
1
2
3
4
5
6
7

# 与路由相关的生命周期函数

路由组件所独有的两个钩子,用于捕获路由组件的激活状态。

# activated

路由组件被激活时触发

activated() {
    console.log('News组件被激活了')
}
1
2
3

# deactivated

路由组件失活时触发

deactivated() {
    console.log('News组件失活了')
}
1
2
3

# 路由守卫

路由守卫的作用就是对路由进行权限控制,按照守卫范围可分为全局守卫、独享守卫和组件内守卫。

# 全局守卫

全局守卫是应用在整个路由内,主要分为:

  • 全局前置守卫:初始化执行,每次路由切换前执行
  • 全局后置守卫:初始化执行,每次路由切换后执行
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
	console.log('beforeEach',to,from)
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
		if(localStorage.getItem('school') === 'snake8859'){ //权限控制的具体规则
			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'
	}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 独享守卫

独享守卫是仅针对某个路由的权限控制,通常写在路由配置文件内,且仅有进入路由时的守卫。

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:'新闻'},
					beforeEnter: (to, from, next) => {
						console.log('独享路由守卫',to,from)
						if(to.meta.isAuth){ //判断是否需要鉴权
							if(localStorage.getItem('school')==='snake8859'){
								next()
							}else{
								alert('学校名不对,无权限查看!')
							}
						}else{
							next()
						}
					}
				},
			]
		}
	]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 组件内守卫

组件内守卫是写在组件内部的守卫,主要有以下两种方式:

<template>
	<h2>我是About的内容</h2>
</template>

<script>
	export default {
		name:'About',
		mounted() {
			// console.log('%%%',this.$route)
		},
		//通过路由规则,进入该组件时被调用
		beforeRouteEnter (to, from, next) {
			console.log('About--beforeRouteEnter',to,from)
			if(to.meta.isAuth){ //判断是否需要鉴权
				if(localStorage.getItem('school')==='snake8859'){
					next()
				}else{
					alert('学校名不对,无权限查看!')
				}
			}else{
				next()
			}
		},
		//通过路由规则,离开该组件时被调用
		beforeRouteLeave (to, from, next) {
			console.log('About--beforeRouteLeave',to,from)
			next()
		}
	}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# hash模式和history模式

# hash模式

在浏览器经常看到https://ip:port/#/路径,其中#及其后面的内容就是hash值。

hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。

优缺点:

  1. 地址中永远带着#号,不美观 。
  2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
  3. 兼容性较好。

# history模式

正常路径模式https://ip:port/xxx/yyy/zzz

history模式虽然地址干净,但是存在一些兼容性和部署上线问题,特别是部署上,当页面刷新时可能会导致404,因为刷新后浏览器会以为发起网络请求,想后端请求数据。通常需要后端人员支持,解决刷新页面服务端404的问题。

Last Updated: 8/23/2023, 2:39:40 PM