一、对路由的理解
1.什么是路由
路由其实就是一组键值对or映射关系,在一个路由中应包含最基本的路径和组件信息。
示例如下:
上面展示的是一个路由组,即用" [ ] "将一组路由写在一起,其中一个大括号中包裹的就是一个路由的信息,此处展示的是一个命名路由,因此多了一个name属性。
2.为什么需要路由
以往的写法是写多个html页面使用 <a> 标签进行跳转,但是有时一个页面只有部分内容需要替换or改变,此时再使用多页面跳转会有窗口抖动的感觉。
如上,展示的是淘宝特价版网页端,其左边选择不同品牌,右边数据会发生改变,这样的就可以使用路由。当网址路径发生改变时,相应组件就会展现。
二、路由的基本使用步骤
1.准备工作
- 安装路由插件库(注意版本对应问题,Vue2对应的插件库是3版本,但是目前默认安装最新版本)
- 在main.js中引入并应用路由插件库,
- 配置router路由器文件夹
(1)文件夹结构如下:
创建了一个router文件夹,并配置一个index.js文件。
(2)index.js中具体内容如下:
此处采用的是默认暴露,routes表示配置一组路由对象
- 在main.js中引入路由器并绑定到Vue
下面以一个案例来具体讲解后续工作
2.替换工作
(1)创建路由组件
先说什么是路由组件,路由组件其实就是原本应该写成多个html中的内容,将它们写成组件形式,然后写到路由表中。
为了和普通组件区别,一般会另外用一个名为pages的文件夹存放路由组件,如下:
其中,在components中存储的是一般组件,而在pages中存储的是路由组件。
(2)使用路由组件
那么在哪里使用路由组件呢?
这取决于你需要在哪里使用它,在哪个组件中需要展示该组件的区域效果,就在哪个组件中使用。
下面是一般组件和路由组件在App.vue中的使用区别
- 一般组件直接写成组件名标签形式
- 一般组件需要在使用它的组件中先①引入②注册,但路由组件则不用
- 路由组件则要先用router-link将路由组件名包裹起来(此处只是a标签用router-link,若其他标签不同,则后续补充)
- 且路由组件后面要使用 to="路径"的形式来指示具体要跳转的路由位置,这里的路径与router文件夹下配置的路由表中各路由的path属性值 保持一致
注意!上面标签只是相当于<a>标签,具体呈现组件内容,还需要使用标签
(这里的router-view的位置才是真正呈现的地方)
细节补充
- 当路由组件被切换时,其实该路由对象是被销毁了,而每次点击其标签时,才会再去挂载
- 不同组件对象上的router属性却是同一个。即整个应用只有一个router属性获取
三、路由进阶
1.嵌套路由
(1)什么是嵌套路由
嵌套路由 又称 多级路由,是在一级路由(即第一个被嵌套的路由)的基础上嵌套一层路由。为了嵌套的页面而出现的一种写法。
(2)怎么写嵌套路由
基本写法和一级路由差不多,另外注意以下几点即可
- 嵌套路由在创建路由时,是在其嵌套的一级路由下,用children:[…]包裹着的。如下:
- 在标签中,嵌套路由的路径要带上父路由路径。如下:
2.路由参数(父组件向子路由传递参数)
为什么需要传递路由参数
因为根据不同的点击,要显示不同的页面,而随着点击的链接不同,有时会有向子路由传递不同参数的需求(即路由界面需要接收参数,而不是静态页面)。
传递参数的方式
- 利用路由的query参数
- 利用路由的params参数
- 路由的props配置
下面逐一介绍三种方式:
(1)query参数
query参数是路由对象的一个参数,可以使用console.log(this.$route)检验,如下:
下面以Message组件向Detail组件传参为例,说明如何传递query参数,以及一些知识点。
在Message组件(参数传递方)中
先准备好要传递的数据,也可以从数据库中获取。
然后传递时,有以下两种写法:
- to的字符串写法
①to前面要加上冒号,表示后面双引号中要当成JS解析
②双引号内的内容要用反单引号括起来,这是ES6的一种技术,表示模板字符串
③参数要在路径后加 ? 然后指明参数属性,再用 ${ 参数 } 携带参数值,多个参数之间用 & 连接
- to的对象写法
①to前面要加上冒号,表示后面双引号中要当成JS解析
②双引号内的内容写成一个对象,用大括号包住
③将要传递的query参数也写成一个对象,以键值对的形式(更清晰
在Detail组件(参数接收方)中
想要应用参数 只需要在模板中获取$route中的query参数携带的数据即可。
(2)params参数
params参数同样也是路由对象的一个参数。因此其参数接收方和query参数相同,但与query不同的是其参数发送方不同。
下面主要介绍其参数发送方(依然用Message组件举例)
传递时,同样有以下两种写法(但与query参数传递略有不同):
- to的字符串写法
①to前面要加上冒号,表示后面双引号中要当成JS解析
②双引号内的内容要用反单引号括起来,这是ES6的一种技术,表示模板字符串
③【不同之处】参数之间放在路径后,加 / 即可。无需指明参数属性名,只需用 ${ 参数 } 携带参数值,多个参数之间还是用 / 连接
④【不同之处】由于第③步没有指明参数属性名,所以在router/index.js文件中要修改路径path值,指出相应位置的参数名来起到占位的作用
- to的对象写法
①to前面要加上冒号,表示后面双引号中要当成JS解析
②双引号内的内容写成一个对象,用大括号包住
③将要传递的parmas参数也写成一个对象,以键值对的形式(更清晰
④【不同之处】将参数接收处(注意不是应用处)的path去掉(注意!不要把index.js文件中的路由路径去掉了啊),添加name属性(因为params参数只能使用name
并且要在router/index.js文件中给传递参数方添加name属性 【这叫做命名路由,此后可以直接用名字代替路径】
在Detail组件(参数接收方)中
想要应用参数 只需要在模板中获取$route中的params参数携带的数据即可。
3.路由的props配置
为什么需要路由的props配置
- 因为在接收参数方组件中,直接在模板中使用参数太过复杂,于是试图将其简化,从而更简洁地使用。如下:
- 使用计算属性可以达到简化模板中参数使用的效果,但是计算属性写起来又变繁杂了。如下:
于是,就可以使用路由的props配置来一次传递多个参数,从简化模板中参数的书写。而路由的props配置主要有三种方法,下面将逐一介绍(均是从Message组件向Detail组件传递参数)
(1)值为对象
将props以对象形式写在参数发送方的路由中。缺点是:参数是静态写死的
在参数接收方的组件中,添加props属性,并添加参数的key
(2)值为布尔值
在参数发送方的路由中,添加一个props属性,值为true。缺点是:只能传递params参数,不能传递query参数
在参数接收方的组件中,添加props属性,并添加参数的key
这种方法只能传递params参数,所以在参数传递的方法上,要配置params参数的传递环境,如命名路由、路由路径设置、应用参数等等
(3)值为函数
在参数发送方的路由中,添加一个props函数,其形参默认就是$route,可以按照以下方式配置参数。
(补充:也可以利用ES6中的解构赋值来简化return语句书写,详细见链接 Js连续解构赋值)
在参数接收方的组件中,添加props属性,并添加参数的key
最后,注意一个问题,我刚刚上面丢了一个东西,见下图:
在标签内,除了参数,别忘了文本内容,否则没有可点击跳转信息了
4.小结——参数传递时的位置关系
在父子组件中,参数传递主要涉及以上三个部分中的四个位置,下面会逐一解释各个位置的作用。
①:参数的传递方(也就是这里涉及to的两种写法
②:参数的展示位置
③:参数的应用方or参数的接收方
④:进行props配置,以及与params参数相关的地方小补充:
5.编程式路由导航
为什么需要编程式路由导航
其实这是为了不被给局限住,前面演示的<router-link> 只是为了替代点击标签<a>,而编程式路由导航使得任意元素都可以实现路由跳转,可以是按钮,也可以是图片等。
如何使用编程式路由导航
很简单,它可以在任何位置,且不需要用特殊标签包裹,只要绑定一个点击事件即可
然后添加一个methods属性,配置相应的方法,如下图所示:
但是这里涉及两个API,分别是push({ })和replace({ }),其内容均和的to的对象写法相同。
这里说明一下:
push() 和 replace()其实是两个与历史记录不同存储方式有关的API,其中push()方法是路由跳转时默认的属性,它会将历史记录进行压栈操作,所以可以自由前进后退。而replace()则会用当前页面记录覆盖前一条记录。
补充知识点——三个与跳转有关的API
6.缓存路由
为什么需要缓存路由
因为路由组件在切换时会自动销毁,而有时会需要用户输入一些输入,此时如果切换组件,那么已经输入的数据就会丢失,所以需要对路由进行缓存,这样当组件被切换时就不会走销毁流程,避免不好的体验。
如何使用缓存路由
如上图所示,只需要用keep-alive标签将router-view包裹起来即可,这样在此处展示的组件都会被缓存(此处是News组件和Message组件,都被缓存)
那么如果只想缓存某个组件怎么办?
答:添加include属性,其值为要缓存的组件名。如下:
如果要缓存多个,但不是全部怎么办?
答:在include前添加冒号,并将值写成数组形式
7.两个路由组件独有的生命周期钩子
beforeDestroy() {
console.log('News组件即将被销毁了')
clearInterval(this.timer)
},
mounted() {
//设置一个定时器
this.timer = setInterval(()=>{
this.opacity -=0.01
if(this.opacity <= 0) this.opacity = 1
},16)
},
//下面两个是路由组件特有的生命周期钩子,切换到当前组件时actived被触发
//激活时触发
activated() {
//设置一个定时器
this.timer = setInterval(()=>{
this.opacity -=0.01
if(this.opacity <= 0) this.opacity = 1
},16)
},
//失活时触发
deactivated() {
console.log('News组件即将被销毁了')
clearInterval(this.timer)
},