Vue2--Vue路由

377 阅读9分钟

路由的介绍

路由简介:

1、定义:

(1)路由就是一组key-value的对应关系

(2)路由要经过路由器的管理

image.png

2、路由工作原理简介:

(1)目的:为了实现导航区和展示区内容的切换

image.png

(2)Vue中的router代表路由,可以实现页面不全部刷新进行切换,根据不同的route规则展示不同的组件。

image.png

vue-router

1、vue-router的理解:vue的一个插件库,专门用来实现SPA应用;

2、对SPA应用的理解

(1)单页Web应用(single page web application , SPA)

(2)整个应用只有一个完整的页面。

(3)点击页面中的导航链接不会刷新页面,只会做页面的局部更新;

(4)数据需要通过ajax请求获取。

3、什么是路由?

(1)一个路由就是一组映射关系(key-value)

(2)key为路径,value可能是function或component

4、路由分类:

(1)后端路由:

  • 理解:value是function,用于处理客户端提交的请求;

  • 工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据

(2)前端路由:

  • 理解:value是component,用于展示页面内容;

  • 工作过程:当浏览器的路径发生改变时,对应的组件就会显示;

路由的基本使用

1、安装vue-router

注意:在vue2中要安装vue-router3,在vue3中要安装vue-router4

安装vue-router3的命令:npm i vue-router@3

2、应用插件:Vue.use(VueRouter)

3、编写router配置项:

先创建router文件,然后建立index.js文件,并且在index.js文件中编写路由规则,在其他文件中编写路由跳转

  • index.js
//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../components/About.vue'
import Home from '../components/Home.vue'

//创建并暴露一个路由器
export default new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home
		}
	]
})

  • main.js的修改
//引入路由器
import router from './router'

Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)

4、路由标签

(1)实现路由切换:<router-link class="list-group-item" active-class="active" to="/about">About</router-link>

(2)指定展示位置:<router-view></router-view>

5、区分路由组件与一般组件

(1)路由组件:通过或放入组件内容,不用导入;

(2)一般组件:通过components导入放入组件的内容。

注意:

(1)一般开发时最好将路由组件和一般组件区分开来,所以在src中新建一个pages文件存放路由组件,router文件存放路由组件的配置

image.png

(2)组件切换时,原来的组件被销毁了,而不是隐藏了

(3)每个组件都有自己的$route属性,里面存储着自己的路由信息

(4)整个应用只有一个router,可以通过组件的$router属性获取到

嵌套路由(多级路由)

1、配置路由规则,使用children配置项:

routes:[
    {
        path:'/about',
        component:About,
    },
    {
        path:'./home',
        component:Home,
        children:[ //通过children配置子级路由
            {
                path:'news', //此处一定不要写
                component:News
            },
            {
                path:'message', //此处一定不要写
                component:Message
            }
        ]
    }
]

2、跳转: <router-link to = "/home/news">News</router-link>

案例

1、需求:

(1)展示About和Home两个路由组件并且它们之间可以相互切换;

(2)在Home组件中嵌套News和Message两个路由组件,并且它们之间可以相互切换;

2、实现:

(1)main.js

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

Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)

new Vue({
  render: h => h(App),
  router:router
}).$mount('#app')

(2)App.vue

<template>
	<div>
		<div class="row">
			<Banner></Banner>
		</div>
		<div class="row">
			<div class="col-xs-2 col-xs-offset-2">
				<div class="list-group">
					<!-- Vue中借助router-link标签实现路由的切换 -->
					<!-- class-active 表示被激活时样式, -->
					<!-- to表示要到达的路径 -->
					<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>
	import Banner from './components/Banner'
	export default {
		name:'App',
		components:{
			Banner
		}
	}
</script>

(3)router文件:(路由规则)

  • index.js
//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../page/About.vue'
import Home from '../page/Home.vue'
import Message from '../page/Message.vue'
import News from '../page/News.vue'

//创建并暴露一个路由器
export default new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home,
			children:[
				{
					path:'news', //子路由不加/
					component:News
				},
				{
					path:'message', //子路由不加/
					component:Message
				},
			]
		}
	]
})

(4)page文件:(路由组件)

  • About.vue
<template>
	<h2>我是About的内容</h2>
</template>

<script>
	export default {
		name:'About'
	}
</script>
  • Home.vue
<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="/home/message">Message</router-link>
				</li>
			</ul>
			<router-view></router-view>
		</div>
	</div>
</template>

<script>
	export default {
		name:'Home'
	}
</script>

  • Message.vue
<template>
		<ul>
			<li>
				<a href="./message1">message0001</a>&nbsp;&nbsp;
			</li>
			<li>
				<a href="./message2">message0002</a>&nbsp;&nbsp;
			</li>
			<li>
				<a href="./message3">message0003</a>&nbsp;&nbsp;
			</li>
		</ul>
</template>

<script>
	export default {
		name:'Message'
	}
</script>

  • News.vue
<template>
	<ul>
		<li>news001</li>
		<li>news002</li>
		<li>news003</li>
	</ul>
</template>

<script>
	export default {
		name:'News'
	}
</script>

3、效果:

嵌套路由.gif

路由的query参数

1、传递参数

<!-- 跳转路由并携带query参数,to的字符串写法 -->
<router-link active-class="active" :to="`/home/message/detail?id=${p.id}&title=${p.title}`">{{p.title}}</router-link>
				
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
	path:'/home/message/detail',
	query:{
            id:p.id,
            title:p.title
	}
}">
{{p.title}}
</router-link>

2、接收参数

$route.query.id
$route.query.title

案例

1、需求:在上一个案例的基础上

(1)点击Message时,呈现‘消息00x’;

(2)点击‘消息00x’时,呈现对应的信息;

2、实现:

  • /router/index.js (修改路由规则)
//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../page/About.vue'
import Home from '../page/Home.vue'
import Message from '../page/Message.vue'
import News from '../page/News.vue'
import Detail from '../page/Detail.vue'
//创建并暴露一个路由器
export default new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home,
			children:[
				{
					path:'news', //子路由不加/
					component:News
				},
				{
					path:'message', //子路由不加/
					component:Message,
					children:[
						{
							path:'detail',
							component:Detail,
						}
					]
				},
			]
		}
	]
})
  • Message.vue
<template>
		<ul>
			<li v-for="p in messageList" :key="p.id" >
				<!-- 跳转路由并携带query参数,to的字符串写法 -->
				<!-- <router-link active-class="active" :to="`/home/message/detail?id=${p.id}&title=${p.title}`">{{p.title}}</router-link> -->
				
				<!-- 跳转路由并携带query参数,to的对象写法 -->
				<router-link :to="{
					path:'/home/message/detail',
					query:{
						id:p.id,
						title:p.title
					}
				}">
				{{p.title}}
				</router-link>
			</li>
			<hr />
			<router-view></router-view>
		</ul>
</template>

<script>
	export default {
		name:'Message',
		data() {
			return {
				messageList:[
					{id:'001',title:'消息001'},
					{id:'002',title:'消息002'},
					{id:'003',title:'消息003'}
				]
			}
		}
	}
</script>

  • Detail.vue (新建一个Detail路由组件用于接收Message中的信息,并将信息呈现出来)
<template>
	<ul>
		<li>消息编号:{{this.$route.query.id}}</li>
		<li>消息标题:{{this.$route.query.title}}</li>
	</ul>
</template>

<script>
	export default {
		name:'Detail',
	}
</script>

3、效果: image.png

命名路由

1、作用:可以简化路由的跳转

2、如何使用

(1)给路由命名:

{
    path:'/demo',
    component:Demo,
    children:[
        {
            path:'test',
            component:Test,
            children:[
                {
                    name:'hello',//给路由命名
                    path:'welcome',
                    component:Hello
                }
            ]
        }
    ]
}

(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>

路由的params参数

下图中是$router中的props: image.png

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

{
    path:'/home',
    component:Home,
    children:[
        {
            path:'news',
            component:News,
        },
        {
            component:Message,
            children:[
                {
                    name:'xiangqing',
                    path:'detail/:id/:title', //使用占位符声明接收params参数
                    component:Detail,
                }
            ]
        }
    ]
}

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>

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

3、接收参数

$route.params.id
$route.params.title

路由props配置

1、作用:让路由组件更方便的收到参数

2、使用方法:

{
    name:'xiangqing',
    path:'detail/:id',
    component:Detail,
    
    // 第一种写法:props的值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
    // props:{a:900}
    
    // 第二种写法:props的值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
    // props:true
    
    // 第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
    props(route){
        return {
            id:route.query.id,
            title:route.query.title
        }
    }
}

router-link的replace属性

1、浏览器的两种模式: (1)模式一:push模式

每次点击都会产生新的页面。历史记录是一个栈结构,有一个指针(指针在栈顶),指向当前页面,如下图所示:

image.png

(2)模式二:replace模式

不保存历史记录,替换当前的栈顶记录

2、开启replace模式

在router-link标签中添加replace属性:replace="true"

<router-link :replace="true" class="list-group-item" active-class="active" :to="{name:'guanyu'}">About</router-link>

编程式路由导航

1、作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活。因为<router-link>最后会转化为<a>标签,但如果我们想要编写<button>标签或者实现开机过三秒跳转页面的效果就不能通过<router-link>来实现,但可以借助编程式路由导航来实现。

2、具体编码:

// router的两个API
this.$router.push({
    name:'xiangqing',
    params:{
        id:xxx,
        title:xxx,
    }
})

this.$router.replace({
    name:'xiangqing',
    params:{
        id:xxx,
        title:xxx
    }
})

案例:

1、需求:

(1)不借助<router-link>通过点击实现push查看内容(即将访问记录压入栈),通过点击实现replace查看内容(即将访问记录替换栈顶元素)。

(2)添加前进后退按键(点击前进键返回之前的页面,点击后退键回到下一个页面)

2、实现:

(1)在Banner.vue组件中实现前进后退键

<template>
	<div class="col-xs-offset-2 col-xs-8">
		<div class="page-header"><h2>Vue Router Demo</h2></div>
		<button @click="forward" >前进</button>&nbsp;
		<button @click="back" >后退</button>
	</div>
</template>

<script>
	export default {
		name:'Banner',
		methods:{
			forward(){
				this.$router.forward()
			},
			back(){
				this.$router.back()
			}
		}
	}
</script>

(2)在Message.vue的列表中添加自定义组件,通过在方法中调用$router事件来实现

<template>
		<ul>
			<li v-for="p in messageList" :key="p.id" >
				<!-- 跳转路由并携带query参数,to的字符串写法 -->
				<!-- <router-link active-class="active" :to="`/home/message/detail?id=${p.id}&title=${p.title}`">{{p.title}}</router-link> -->
				
				<!-- 跳转路由并携带query参数,to的对象写法 -->
				<router-link :to="{
					path:'/home/message/detail',
					query:{
						id:p.id,
						title:p.title
					}
				}">
				{{p.title}}
				</router-link>
				<button @click="pushShow(p)">push查看</button>
				<button @click="replacehShow(p)">replace查看</button>
			</li>
			<hr />
			<router-view></router-view>
		</ul>
</template>

<script>
	export default {
		name:'Message',
		data() {
			return {
				messageList:[
					{id:'001',title:'消息001'},
					{id:'002',title:'消息002'},
					{id:'003',title:'消息003'}
				]
			}
		},
		methods:{
			pushShow(m){
				this.$router.push({
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				})
			},
			replacehShow(m){
				this.$router.replace({
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				})
			}
		}
	}
</script>

3、效果:

编程式路由导航.gif

缓存路由组件

1、作用:让不展示的路由组件保持挂载状态,不被销毁(如果不使用缓存路由技术,不展示的路由就会被销毁,问题演示如下动画)

路由切换的问题.gif

2、使用方法:

	<!-- 注意,这里的include包含的使组件名!!! -->
	<keep-alive include="News">
		<router-view></router-view>
	</keep-alive>

3、效果:

缓存路由.gif

两个新的生命周期钩子

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

actived

激活时触发

deactived

失活时触发

案例

1、需求:

当切换到News界面时激活News组件,可以让文字透明度产生渐变;

2、实现:

<template>
	<ul>
		<li :style="{opacity}" >欢迎学习Vue</li>
		<li>news001</li><textarea/>
		<li>news002</li><textarea/>
		<li>news003</li><textarea/>
	</ul>
</template>

<script>
	export default {
		name:'News',
		data() {
			return {
				opacity:1
			}
		},
		//引入两个路由独有的生命周期钩子
		// 激活,显示该路由的时候被激活
		activated() {
			console.log('News组件激活了')
			this.timer = setInterval(() => {
				this.opacity -= 0.01
				if(this.opacity <= 0) this.opacity = 1
			})
		},
		// 失活,隐藏(销毁)该路由时失活
		deactivated() {
			console.log('News组件失活了')
			clearInterval(this.timer)
		}
	}
</script>

3、效果:

组件的生命周期钩子.gif

路由守卫

作用:保护路由的安全。

全局前置路由守卫

1、作用:每次初始化时被调用,路由切换之前被调用
(重点:路由切换前,路由切换前,路由切换前!!!)

2、使用方法:

(1)在被限制的路由组件中的meta中配置isAuth属性

{
	name:'xinwen',
	path:'news', //子路由不加/
	component:News,
	meta:{isAuth:true}, //在meta配置中设置isAuth属性来作为判断是否要拦截路由的依据,也是开启路由守卫的前提
},

(2)在index.js配置全局前置路由守卫:

// 全局前置路由守卫 --- 初始化的时候被调用,每次路由切换之前被调用
router.beforeEach((to,from,next) => {
	// to 表示要去哪里
	// from 表示从哪里来
	// next 表示放行
	if(to.meta.isAuth){
		if(localStorage.name == 'bug_killer'){
			next()
		}
		else{
			alert('名称不对,无权限查看!')
		}
	}
	else{
		next()
	}
})

案例

1、需求:配置全局路由守卫,当localStore中的name是‘bug_killer’才能访问News和Message。

2、实现:

/router/index.js

//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../page/About.vue'
import Home from '../page/Home.vue'
import Message from '../page/Message.vue'
import News from '../page/News.vue'
import Detail from '../page/Detail.vue'
//创建并暴露一个路由器
const router = new VueRouter({
	routes:[
		{
			name:'guanyu',
			path:'/about',
			component:About
		},
		{
			name:'zhuye',
			path:'/home',
			component:Home,
			children:[
				{
					name:'xinwen',
					path:'news', //子路由不加/
					component:News,
					meta:{isAuth:true}, //在meta配置中设置isAuth属性来作为判断是否要拦截路由的依据,也是开启路由守卫的前提
				},
				{
					name:'xiaoxi',
					path:'message', //子路由不加/
					component:Message,
					meta:{isAuth:true}, //在meta配置中设置isAuth属性来作为判断是否要拦截路由的依据,也是开启路由守卫的前提
					children:[
						{
							name:'xiangqing',
							path:'detail',
							component:Detail,
						}
					]
				},
			]
		}
	]
})

// 全局前置路由守卫 --- 初始化的时候被调用,每次路由切换之前被调用
router.beforeEach((to,from,next) => {
	// to 表示要去哪里
	// from 表示从哪里来
	// next 表示放行
	if(to.meta.isAuth){
		if(localStorage.name == 'bug_killer'){
			next()
		}
		else{
			alert('名称不对,无权限查看!')
		}
	}
	else{
		next()
	}
})

export default router

3、效果:

  • 没有配置localStore时:

image.png

  • 在localStore中添加name属性是‘bug_killer’时:

image.png

可以访问

image.png

后置路由守卫

1、作用:每次初始化时被调用,路由切换后被调用;

2、用法:(应用:切换页面时更改页面标题)

(1)给每个路由组件配置一个标题

//在meta中配置title属性
meta:{title:'关于'}

(2)配置全局后置路由

//全局后置路由守卫--每次初始化时被调用,路由切换之后被调用
router.afterEach((to,from)=>{
	console.log('后置路由守卫',to,from)
	document.title = to.meta.title || '硅谷系统'
})

独享路由守卫

1、作用:访问某个路由组件前被调用,初始化某个路由组件时被调用。(注意:独享路由守卫只有前置路由守卫,没有后置)

2、使用+案例:(对新闻组件进行限制,当localStore中存在(school,'atguigu')键值对时才能访问)

Note:除了将名字换为beforeEnter:其他内容与全局前置路由守卫都一样

{
	name:'xinwen',
	path:'news', //子路由不加/
	component:News,
	meta:{isAuth:true,title:'新闻'},
	beforeEnter:(to,from,next)=>{
		console.log('独享前置路由守卫')
		if(to.meta.isAuth){
			if(localStorage.getItem('school') === 'atguigu'){
				next()
			}
			else{
				alert('学校名称不对,无权查看')
			}
		}
		else{
			next()
		}
	}
},

组件内路由守卫

1、作用:分为进入守卫、离开守卫两个;

进入守卫:通过路由规则时,进入该组件时被调用; 离开守卫:通过路由规则时,离开该组件时被调用;

2、使用方法:

//通过路由规则,进入该组件时被调用
//这里说的路由规则要注意,因为我也可以将它作为一个普通的组件进行导入,此时不遵守路由规则
beforeRouteEnter(to,from,next) {
	console.log('About--beforeRouteEnter',to,from)
	if(to.meta.isAuth){
		if(localStorage.getItem('name') == 'bushizhiwu'){
			next()
		}else{
			alert('学校名称不对,无权限查看')
		}
	}else{
		next()
	}
},

//通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next) {
	console.log('About--beforeRouteLeave',to,from)
	next()
}

history模式与hash模式

1、对于一个url来说,什么是hash值?

#及其后面的内容就是hash值,如下图所示:

image.png

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

3、两种模式:

(1)hash模式:

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

(2)history模式:

  • 地址干净、美观
  • 兼容性和hash模式相比略差
  • 应用部署上线时,解决刷新页面服务器404的问题

4、项目上线:用vue写好项目后,需要生成纯js,html,css构成的文件,用于部署在其他服务器上完成上线。所用命令:npm run build,执行后会生成一个dist文件可以放在我们部署的服务器上。