Vue笔记06-VueRouter

路由简介

Vue Router是Vue官方的路由管理器,专门实现SPA应用,即单页面应用。
单页面应用的含义是,整个应用只有一个完整的页面,点击页面中的链接不会刷新整个页面,只会局部刷新,数据通过ajax请求获取,在页面局部刷新的时候,地址栏的url会跟着变化。
一个路由就是一个key-value的映射关系,key为路径,value为function或component。
路由分为前端路由和后端路由。
前端路由:value为component,用于展示页面内容,浏览器路径改变的时候,对应的组件就会显示。
后端路由:value为function,用于处理客户端提交的请求,服务器接到请求后,根据请求路径找到匹配的函数处理请求,返回响应数据。

路由的基本使用

页面上有两个tab,分别是About和Home,每个tab对应一个路由,也就是需要创建两个组件:About.vue和Home.vue,创建components文件夹,将两个组件放进去。
既然要使用VueRouter,就要安装vue-router,使用npm i vue-router安装。还需要一个router文件夹,里面放路由的规则index.js。
每个组件都由直接的$route属性,里面存储着自己的路由信息。
main.js

//引入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
});

router/index.js

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

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

Home.vue,About.vue类似,只是内容不同

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

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

App.vue

<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也会被翻译成a标签 -->
		  <!-- 原来的active的class改为动态添加了,active-class是router-link标签的属性 -->
		  <!-- to的值对应router/index.js里的key -->
		  <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>

多级路由

路由是可以嵌套的,也就是在router/index.js里配置即可。和上面不同的是这里使用一个pages文件夹,里面放了4个组件:About.vue、Home.vue、Message.vue、News.vue。普通组件放在components里,路由组件放在pages里。
router/index.js

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
// 创建并暴露一个路由器
export default new VueRouter({
	// 一级路由的path带'/',子级路由的path不带'/'
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home,
			// Home组件里还有子级组件,使用children引出
			children:[
				{
					path:'news',
					component:News,
				},
				{
					path:'message',
					component:Message,
				}
			]
		}
	]
});

About.vue没有变,Home.vue里要加上<router-link>标签,用于路由Message.vue和News.vue。Message.vue和News.vue没有什么新内容,就是普通的页面。
Home.vue

<template>
	<div>
		<h2>Home组件内容</h2>
		<div>
			<ul class="nav nav-tabs">
				<li>
					<!-- to要带上父组件的前缀 -->
					<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
				</li>
				<li>
					<!-- to要带上父组件的前缀 -->
					<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',
		// 通过函数可以知道,在切换路由的时候,组件是在不断的销毁和新建的
		/* beforeDestroy() {
			console.log('Home组件即将被销毁了');
		}, */
		/* mounted() {
			console.log('Home组件挂载完毕了',this);
			window.homeRoute = this.$route;
			window.homeRouter = this.$router;
		},  */
	}
</script>

路由的query参数

在路由跳转的时候,是支持携带参数的,这里介绍的是query参数,也就是跟在地址的?后面,用&分隔的参数。引入一个Detail.vue组件,修改Message.vue,Detail.vue在接收参数的时候使用{{$route.query.key}}来接收。
Message.vue

<template>
	<div>
		<ul>
			<li v-for="m in messageList" :key="m.id">
				<!-- 跳转路由并携带query参数,to的字符串写法 -->
				<!-- 使用模板字符串和${key}传参 -->
				<!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link>&nbsp;&nbsp; -->
				<!-- 跳转路由并携带query参数,to的对象写法 -->
				<router-link :to="{
					path:'/home/message/detail',
					query:{
						id:m.id,
						title:m.title
					}
				}">
					{{m.title}}
				</router-link>
			</li>
		</ul>
		<hr>
		<router-view></router-view>
	</div>
</template>
<script>
	export default {
		name:'Message',
		data() {
			return {
				messageList:[
					{id:'001',title:'消息001'},
					{id:'002',title:'消息002'},
					{id:'003',title:'消息003'}
				]
			}
		},
	}
</script>

Detail.vue

<template>
	<ul>
		<li>消息编号:{{$route.query.id}}</li>
		<li>消息标题:{{$route.query.title}}</li>
	</ul>
</template>
<script>
	export default {
		name:'Detail',
		mounted() {
			console.log(this.$route);
		},
	}
</script>

命名路由

命名路由就是给路由加一个name属性,当路由的path非常长的时候,用name代替长串路由是比较方便的,需要在定义路由的/router/index.js里给路由指定name。
注意,在<router-link>中使用命名路由的时候,必须将to属性写成对象的形式。
router/index.js

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
// 创建并暴露一个路由器
export default new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home,
			children:[
				{
					path:'news',
					component:News,
				},
				{
					// 给路由起名字
					name:'message',
					path:'message',
					component:Message,
					children:[
						{
							path:'detail',
							component:Detail,
						}
					]
				}
			]
		}
	]
});

Home.vue中路由的修改,将path改为name即可。

<router-link :to="{
	name:'message',
	query:{
		id:m.id,
		title:m.title
	}
}">
</router-link>

路由的params参数

params参数是在请求路径上的参数,这里有一点需要注意,路由携带params参数并且<router-link>to写成对象的时候,不能使用path配置项,必须使用name配置,因为使用path的时候,params是带不过来的,使用name可以将path上的参数带过来,在接收params参数的时候使用{{$route.params.key}}接收。
先来看router/index.js中的路由配置。

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'
// 创建并暴露一个路由器
export default new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home,
			children:[
				{
					path:'news',
					component:News,
				},
				{
					path:'message',
					component:Message,
					children:[
						{
							name:'detail',
							path:'detail/:id/:title',
							component:Detail,
						}
					]
				}
			]
		}
	]
});

Message.vue中router-link标签。

<router-link :to="{
	name:'detail',
	params:{
		id:m.id,
		title:m.title
	}
}">
	{{m.title}}
</router-link>

Detail.vue

<template>
	<ul>
		<li>消息编号:{{$route.params.id}}</li>
		<li>消息标题:{{$route.params.title}}</li>
	</ul>
</template>
<script>
	export default {
		name:'Detail'
	}
</script>

路由的props配置

路由还有一个props配置项,用于传值,props有三种写法,写在哪个路由里的props,在哪个路由组件中使用,组件中通过props来接收。
router/index.js部分内容

routes:[
	{
		path:'detail',
		component:Detail,
		// props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件
		// props:{a:1,b:'hello'}
		// props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件
		// props:true
		// props的第三种写法,值为函数
		props($route){
			return {
				id:$route.query.id,
				title:$route.query.title,
				a:1,
				b:'hello'
			}
		}
	}
]

Detail.vue

<template>
	<ul>
		<li>消息编号:{{id}}</li>
		<li>消息标题:{{title}}</li>
	</ul>
</template>
<script>
	export default {
		name:'Detail',
		props:['id','title'],
		computed: {
			// id(){
			// 	return this.$route.query.id
			// },
			// title(){
			// 	return this.$route.query.title
			// },
		}
	}
</script>

router-link的replace属性

<router-link>replace属性用于控制路由跳转浏览器历史记录。浏览器历史记录有两种方式:分别是pushreplacepush是追加记录,replace是替换记录,默认采用的是push,如果需要使用replace属性,直接加在<router-link>上即可。

<router-link replace to="/home/news">News</router-link>

编程式路由导航

编程式路由导航就是将导航的前进后退通过自定义方法,调用$router上的方法来实现。
定义几个按钮:push、replace、back、forward、go。

<template>
	<div>
		<ul>
			<li v-for="m in messageList" :key="m.id">
				<router-link :to="{
					name:'detail',
					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>
		<button @click="back">后退</button>
		<button @click="forward">前进</button>
		<button @click="test(-1)">测试一下go</button>
		<hr>
		<router-view></router-view>
	</div>
</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
					}
				});
			},
			replaceShow(m){
				this.$router.replace({
					name:'xiangqing',
					query:{
						id:m.id,
						title:m.title
					}
				});
			},
			back(){
				this.$router.back();
			},
			forward(){
				this.$router.forward();
			},
			go(n){
				this.$router.go(n);
			}
		}
	}
</script>

缓存路由组件

考虑这么一个场景,有两个tab标签,第一个tab标签里有几个input输入框,输入完成后,点击第二个tab,再点第一个tab,会发现刚刚输入的内容丢失了。这是因为Vue在路由导航切换的时候,默认会销毁历史组件,创建新的组件,这一点可以通过组件的beforeDestroy()来验证,为了在路由切换的时候,不让组件被销毁,需要一个新的标签<keep-alived>
如果News.vue组件需要被缓存,那么,找到News.vue的父组件,也就是Home.vue,在插入News.vue的地方<router-view></router-view>,包上一层标签<keep-alived><keep-alived>还有一个属性include,当<router-view></router-view>处有可能放多个路由组件的时候,需要用到include属性,显示指定哪些路由组件需要被缓存,如果不加include那就是<router-view></router-view>处的组件都缓存。
Home.vue部分

<!-- 缓存多个路由组件,这里的值是组件名 -->
<!-- <keep-alive :include="['News','Message']"> -->
<!-- 缓存一个路由组件,这里的值是组件名 -->
<keep-alive include="News">
	<router-view></router-view>
</keep-alive>

两个新的生命周期钩子

这里引出两个路由组件独有的生命周期钩子:activated和deactivated。
在前面提到的路由缓存组件中,添加一个元素和一个定时器,通过定时器修改元素的opacity。这个组件缓存,导致定时器不能被销毁,是非常消耗性能的,于是引出了activated和deactivated,在组件激活的时候,调用activaded()方法,在组件失活的时候,调用deactivaded()方法。
News.vue

<template>
	<ul>
		<li :style="{opacity}">欢迎学习Vue</li>
		<li>news001 <input type="text"></li>
		<li>news002 <input type="text"></li>
		<li>news003 <input type="text"></li>
	</ul>
</template>
<script>
	export default {
		name:'News',
		data() {
			return {
				opacity:1
			}
		},
		/* beforeDestroy() {
			console.log('News组件即将被销毁了');
			clearInterval(this.timer);
		}, */
		/* mounted(){
			this.timer = setInterval(() => {
				console.log('@');
				this.opacity -= 0.01;
				if(this.opacity <= 0) this.opacity = 1;
			},16);
		}, */
		activated() {
			console.log('News组件被激活了');
			this.timer = setInterval(() => {
				console.log('@');
				this.opacity -= 0.01;
				if(this.opacity <= 0) this.opacity = 1;
			},16);
		},
		deactivated() {
			console.log('News组件失活了');
			clearInterval(this.timer);
		}
	}
</script>

路由守卫

在进入和离开路由的时候,可以定义一些方法, 就可以在这些时机进行一些操作,对路由进行一些权限控制。
全局前置守卫、全局后置守卫、独享路由守卫写在router/index.js里,组件内路由守卫,写在组件里。在路由里,有一个meta参数,可以让路由以对象的形式接收一些元数据,通过元数据对路由做权限控制。
路由里,会看到3个参数:from、to、next。
from表示从哪里进的路由组件,to表示将要去的路由组件,next是一个方法名,调用next()表示路由放行,否则路由会停住。

全局前置守卫

router/index.js部分

// 全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
	console.log('前置路由守卫',to,from);
	if(to.meta.isAuth){/ 判断是否需要鉴权
		if(localStorage.getItem('school')==='atguigu'){
			next();
		}else{
			alert('学校名不对,无权限查看!');
		}
	}else{
		next();
	}
});

全局后置守卫

router/index.js部分

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

独享路由守卫

独享路由守卫只有一个方法:beforeEnter(),进入路由时候调用,写在一个路由配置对象的里面。
router/index.js部分

{
	name:'news',
	path:'news',
	component:News,
	meta:{isAuth:true,title:'新闻'},
	beforeEnter: (to, from, next) => {
		console.log('独享路由守卫',to,from);
		if(to.meta.isAuth){// 判断是否需要鉴权
			if(localStorage.getItem('school')==='atguigu'){
				next();
			}else{
				alert('学校名不对,无权限查看!');
			}
		}else{
			next();
		}
	}
}

组件内路由守卫

这里有两个方法:beforeRouteEnter()beforeRouteLeave()
通过路由规则,进入组件和离开组件的时候会被调用。在页面上写一个组件标签,这种不算“通过路由规则”,必须是路由跳转的,地址栏跟着变化的才算。
About.vue

<template>
	<h2>我是About的内容</h2>
</template>
<script>
	export default {
		name:'About',
		// 通过路由规则,进入该组件时被调用
		beforeRouteEnter (to, from, next) {
			console.log('About--beforeRouteEnter',to,from);
			if(to.meta.isAuth){// 判断是否需要鉴权
				if(localStorage.getItem('school')==='atguigu'){
					next();
				}else{
					alert('学校名不对,无权限查看!');
				}
			}else{
				next();
			}
		},
		// 通过路由规则,离开该组件时被调用
		beforeRouteLeave (to, from, next) {
			console.log('About--beforeRouteLeave',to,from);
			next();
		}
	}
</script>

从a.vue,通过路由规则进入About.vue,会触发beforeRouteEnter(),从About.vue,通过路由规则进入b.vue,会触发beforeRouteLeave()

路由的两种工作模式

在new VueRouter的时候,可以传入一个mode属性,它有两个值,history和hash,默认是hash,标志就是我们在url里看到的#号。
使用npm run build对项目进行构建,会生成一个dist文件夹,这个就是由Vue编译后的静态文件,静态文件需要进行部署,才能看到效果,这里使用node express进行部署。
新建一个demo文件夹,使用npm init初始化,然后一路回车,再输入npm i express命令安装express。将刚才的文件放到demo下的static里,需要新建一个static文件夹。新建一个server.js文件,使用node server启动服务。浏览器通过http://localhost:5005/person可以拿到结果,访问http://localhost:5005可以看到vue编译后的首页结果。

const express = requier('express');
const app = express();
app.use(express.static(__dirname + '/static'));
app.get('/person', (request, response)=>{
    response.send({
        name:'tom',
        age:'20'
    });
});
app.listen(5005,(error)=>{
    if (!error) {
        console.log{'服务器启动成功'};
    }
});

history模式的路径上不存在#,而hash模式的路径上存在#,相比来看,history模式更美观,但是history的兼容性差一点。history和hash的本质区别是发往服务器的请求不一样,hash模式下,#后面的内容不会被发往服务器,history会把整个路径发往服务器,所以在单页面应用中,会出现这样一种情况:在history模式下,通过路由跳转到一个内页面,再刷新页面会报404。
npm有一个专门的类库解决这个问题。通过npm i connect-history-api-fallback命令安装这个类库,修改server.js,再启动服务器。

const express = require('express');
const histroy = require('connect-history-api-fallback');
const app = express();
app.use(histroy());// 写在static资源前面
app.use(express.static(__dirname + '/static'));
app.get('/person', (request, response)=>{
    response.send({
        name:'tom',
        age:'20'
    });
});
app.listen(5005,(error)=>{
    if (!error) {
        console.log('服务器启动成功');
    }
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值