一、前言

  在上一章的学习中,通过举例说明,我们了解了 Vue Router 中命名路由、命名视图的使用方法,以及如何通过 query 查询参数传参,或者是采用 param 传参的方式实现路由间的参数传递。通过学习我们可以发现,在实现路由间的参数传递时,我们将 Vue Router 与我们的组件强耦合在一起,这无疑是不合适的,那么本章我们就来学习,如何实现组件和 Vue Router 之间的解耦。

  学习系列目录地址:https://www.cnblogs.com/danvic712/p/9549100.html

  仓储地址:https://github.com/Lanesra712/VueTrial/blob/master/chapter02-bronze/router/decoupling.html

二、干货合集

  1、编程式导航

  在使用 Vue Router 的时候,我们通常会通过 router-link 标签去生成跳转到指定路由的链接,但是在实际的前端开发中,我们更多的是通过 js 的方式进行跳转。就像我们很常见的一个交互需求,用户提交表单,提交成功后跳转到上一页面,提交失败则留在当前页。这时候如果我们还是通过 router-link 标签进行跳转就不合适了,我们需要通过 js 根据表单返回的状态进行动态的判断。

  在使用 Vue Router 时,我们已经将 Vue Router 的实例挂载到了 Vue 实例上,因此我们就可以借助 $router 的实例方法,通过编写 js 代码的方式实现路由间的跳转,而这种方式就是一种编程式的路由导航。

  在 Vue Router 中具有三种导航方法,分别为 push、replace 和 go。我们最常见的通过在页面上设置 router-link 标签进行路由地址间的跳转,就等同于执行了一次 push 方法。

  在这一小节的示例中,我将使用编程式导航实现通过点击不同的按钮实现路由间的跳转,最终实现的示意图如下所示。

  在之前学习 Vue Router 的基础使用方法时,我们了解到,前端路由的实现方式,实际上就是对于浏览器的 history api 的操作。浏览器的 history 对象提供了对浏览器的会话历史的访问,它暴露了很多有用的方法和属性,允许我们在用户浏览历史中向前和向后跳转,同时从 HTML5 开始提供了对 history 栈中内容的操作。

  而 Vue Router 所提供的 push、replace 和 go 方法则完全可以对应到浏览器 history api 中所提供的 history.pushState、history.replaceState 和 history.go 方法。

  1.1、push

  当我们需要跳转新页面时,我们就可以通过 push 方法将一条新的路由记录添加到浏览器的 history 栈中,通过 history 的自身特性,从而驱使浏览器进行页面的跳转。同时,因为在 history 会话历史中会一直保留着这个路由信息,所以当我们后退时还是可以退回到当前的页面。

  在 push 方法中,参数可以是一个字符串路径,或者是一个描述地址的对象,这里其实就等同于我们调用了 history.pushState 方法。

// 字符串 => /first
this.$router.push('first') // 对象 => /first
this.$router.push({ path: 'first' }) // 带查询参数 => /first?abc=123
this.$router.push({ path: 'first', query: { abc: '123' }})

  这里需要注意,当我们传递的参数为一个对象并且当 path 与 params 共同使用时,对象中的 params 属性不会起任何的作用,我们需要采用命名路由的方式进行跳转,或者是直接使用带有参数的全路径。

const userId = '123'

// 使用命名路由 => /user/123
this.$router.push({ name: 'user', params: { userId }}) // 使用带有参数的全路径 => /user/123
this.$router.push({ path: `/user/${userId}` }) // 这里的 params 不生效 => /user
this.$router.push({ path: '/user', params: { userId }})

  1.2、go

  当我们使用 go 方法时,我们就可以在 history 记录中向前或者后退多少步,也就是说通过 go 方法你可以在已经存储的 history 路由历史中来回跳。

// 在浏览器记录中前进一步,等同于 history.forward()
this.$router.go(1) // 后退一步记录,等同于 history.back()
this.$router.go(-1) // 前进 3 步记录
this.$router.go(3) // 如果 history 记录不够用,那就默默地失败呗
this.$router.go(-100)
this.$router.go(100)

  1.3、replace

  replace 方法同样可以达到实现路由跳转的目的,不过,从名字中你也可以看出,与使用 push 方法跳转不同是,当我们使用 replace 方法时,并不会往 history 栈中新增一条新的记录,而是会替换掉当前的记录,因此,你无法通过后退按钮再回到被替换前的页面。

this.$router.replace({
path: '/special'
})

  通过编程式路由实现路由间切换的示例代码如下所示,你可以自己尝试一下,去熟悉如何通过 js 来实现路由地址间的切换。

<div id="app">
<div class="main">
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group mr-2" role="group" aria-label="First group">
<button type="button" class="btn btn-secondary" @click="goFirst">第一页</button>
<button type="button" class="btn btn-secondary" @click="goSecond">第二页</button>
<button type="button" class="btn btn-secondary" @click="goThird">第三页</button>
<button type="button" class="btn btn-secondary" @click="goFourth">第四页</button>
</div>
<div class="btn-group mr-2" role="group" aria-label="Second group">
<button type="button" class="btn btn-secondary" @click="pre">后退</button>
<button type="button" class="btn btn-secondary" @click="next">前进</button>
</div>
<div class="btn-group mr-2" role="group" aria-label="Third group">
<button type="button" class="btn btn-secondary" @click="replace">替换当前页为特殊页</button>
</div>
</div> <router-view></router-view>
</div>
</div> <script>
const first = {
template: '<h3>当前是第一页</h3>'
} const second = {
template: '<h3>当前是第二页</h3>'
} const third = {
template: '<h3>当前是第三页</h3>'
} const fourth = {
template: '<h3>当前是第四页</h3>'
} const special = {
template: '<h3>特殊页面</h3>'
} const router = new VueRouter({
routes: [{
path: '/first',
component: first
}, {
path: '/second',
component: second
}, {
path: '/third',
component: third
}, {
path: '/fourth',
component: fourth
}, {
path: '/special',
component: special
}]
}) const vm = new Vue({
el: '#app',
data: {},
methods: {
goFirst() {
this.$router.push({
path: '/first'
})
},
goSecond() {
this.$router.push({
path: '/second'
})
},
goThird() {
this.$router.push({
path: '/third'
})
},
goFourth() {
this.$router.push({
path: '/fourth'
})
},
next() {
this.$router.go(1)
},
pre() {
this.$router.go(-1)
},
replace() {
this.$router.replace({
path: '/special'
})
}
},
router: router
})
</script>

  2、解耦

  在文章开头我们有提到过,在使用路由传参的时候,我们将组件与 Vue Router 强绑定在了一块,这意味着在任何需要获取路由参数的地方,我们都需要加载 Vue Router。那么,如何解决这一强绑定呢?

  在之前学习组件相关的知识时,我们提到了可以通过组件的 props 选项来实现子组件接收父组件传递的值。而在 Vue Router 中,同样给我们提供了通过使用组件的 props 选项来进行解耦的功能。

  在下面的示例中,在定义路由模板时,我们通过指定需要传递的参数为 props 选项中的一个数据项,之后,我们通过在定义路由规则时,指定 props 属性为 true,即可实现对于组件以及 Vue Router 之间的解耦。

<script>
const second = {
props: ['id'],
template: '<h3>当前是第二页 --- {{id}} </h3>'
} const router = new VueRouter({
routes: [{
path: '/second/:id',
component: second,
props: true
}]
}) const vm = new Vue({
el: '#app',
data: {},
methods: {
goSecond() {
this.$router.push({
path: '/second'
})
}
},
router: router
})
</script>

  可以看到,这里采用 param 传参的方式进行参数传递,而在组件中我们并没有加载 Vue Router 实例,也完成了对于路由参数的获取。需要注意的是,采用此方法,只能实现基于 param 方式进行传参的解耦。

  针对定义路由规则时,指定 props 属性为 true 这一种情况,在 Vue Router 中,我们还可以给路由规则的 props 属性定义成一个对象或是函数。不过,如果定义成对象或是函数,此时并不能实现对于组件以及 Vue Router 间的解耦。

  在将路由规则的 props 定义成对象后,此时不管路由参数中传递是任何值,最终获取到的都是对象中的值。同时,需要注意的是,props 中的属性值必须是静态的,也就是说,你不能采用类似于子组件同步获取父组件传递的值作为 props 中的属性值。

<script>
const third = {
props: ['name'],
template: '<h3>当前是第三页 --- {{name}} </h3>'
} const router = new VueRouter({
routes: [{
path: '/third/:name',
component: third,
props: {
name: 'zhangsan'
}
}]
}) const vm = new Vue({
el: '#app',
data: {},
methods: {
goThird() {
this.$router.push({
path: '/third'
})
}
},
router: router
})
</script>

  在对象模式中,我们只能接收静态的 props 属性值,而当我们使用函数模式之后,就可以对静态值做数据的进一步加工或者是与路由传参的值进行结合。

<script>
const fourth = {
props: ['id', 'name'],
template: '<h3>当前是第四页 --- {{id}} --- {{name}} </h3>'
} const router = new VueRouter({
routes: [{
path: '/fourth',
component: fourth,
props: (route) => ({
id: route.query.id,
name: 'zhangsan'
})
}]
}) const vm = new Vue({
el: '#app',
data: {},
methods: {
goFourth() {
this.$router.push({
path: '/fourth'
})
}
},
router: router
})
</script>

三、总结

  这一章主要学习了如何通过使用 Vue Router 的实例方法,从而实现编程式导航,以及如何实现组件与 Vue Router 之间的解耦。至此,Vue Router 的一些基础使用方法也就大概介绍完了,其它的知识点将在后面的项目中具体使用到的时候再进行介绍,欢迎持续关注哈~~~

四、参考

  1、History API与浏览器历史堆栈管理

  2、可能比文档还详细--VueRouter完全指北

  3、十全大补vue-router

[Vue 牛刀小试]:第十四章 - 编程式导航与实现组件与 Vue Router 之间的解耦的更多相关文章

  1. VueRouter爬坑第四篇-命名路由、编程式导航

    VueRouter系列的文章示例编写时,项目是使用vue-cli脚手架搭建. 项目搭建的步骤和项目目录专门写了一篇文章:点击这里进行传送 后续VueRouter系列的文章的示例编写均基于该项目环境. ...

  2. 《Linux命令行与shell脚本编程大全》 第十四章 学习笔记

    第十四章:呈现数据 理解输入与输出 标准文件描述符 文件描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误 1.STDIN 代表标准输入.对于终端界面 ...

  3. perl 第十四章 Perl5的包和模块

    第十四章 Perl5的包和模块 by flamephoenix 一.require函数  1.require函数和子程序库  2.用require指定Perl版本二.包  1.包的定义  2.在包间切 ...

  4. Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  5. C和指针 (pointers on C)——第十四章:预处理器

    第十四章 预处理器 我跳过了先进的指针主题的章节. 太多的技巧,太学科不适合今天的我.但我真的读,读懂.假设谁读了私下能够交流一下.有的小技巧还是非常有意思. 预处理器这一章的内容.大家肯定都用过.什 ...

  6. CSS3秘笈复习:十三章&十四章&十五章&十六章&十七章

    第十三章 1.在使用浮动时,源代码的顺序非常重要.浮动元素的HTML必须处在要包围它的元素的HTML之前. 2.清楚浮动: (1).在外围div的底部添加一个清除元素:clear属性可以防止元素包围浮 ...

  7. JavaScript高级程序设计:第十四章

    第十四章 一.表单的基础知识 在HTML中,表单是由<form>元素来表示的,而在javascript中,表单对应的则是HTMLFormElement类型.HTMLFormElement继 ...

  8. C#语言和SQL Server第十三 十四章笔记

    十三章  使用ADO.NET访问数据库 十四章使用ADO.NET查询和操作数据库 十三章:                                                       ...

  9. Gradle 1.12用户指南翻译——第二十四章. Groovy 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

随机推荐

  1. Scala & IntelliJ IDEA环境搭建升级版:在JAVA中调用Scala的helloworld

    --------------------- 前言 --------------------- 项目关系,希望用Spark GraphX做数据分析及图像展示,但前提是得会spark:spark是基于sc ...

  2. 【转】eth0 no such device(reload)

    [转自:http://blog.chinaunix.net/uid-25554408-id-292638.html 北国的春的ChinaUnix博客] 今天我在vmware里安装了虚拟机,安装虚拟机就 ...

  3. WPF中StringFormat的用法--显示特定位数的数字

    原文:WPF中StringFormat的用法--显示特定位数的数字 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/huangli321456/art ...

  4. springboot 上传图片

    1. 创建多层目录 创建多层目录要使用File的mkdirs()方法,其他可以使用mkdir()方法. 2. 文件大小限制 配置文件中,在spring1.4以后要使用 ` spring.http.mu ...

  5. WPF中实现PropertyGrid(用于展示对象的详细信息)的三种方式

    原文:WPF中实现PropertyGrid(用于展示对象的详细信息)的三种方式 由于WPF中没有提供PropertyGrid控件,有些业务需要此类的控件.这篇文章介绍在WPF中实现PropertyGr ...

  6. NET实现RSA AES DES 字符串 加密解密以及SHA1 MD5加密

    本文列举了    数据加密算法(Data Encryption Algorithm,DEA) 密码学中的高级加密标准(Advanced EncryptionStandard,AES)RSA公钥加密算法 ...

  7. UWP应用载入SVG图片的兼容性方案

    原文 UWP应用载入SVG图片的兼容性方案 新版本<纸书科学计算器>的更新点之一,就是优化了表达式的显示方式.在旧版本中,表达式里的符号是用png图片显示的,当用户放大看的时候会发现一些锯 ...

  8. Asp +Js 无刷新分页

    Default.aspx代码 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind=" ...

  9. 关于SetLocaleInfo()

    原文:关于SetLocaleInfo() 此函数用于设置系统的一些本地信息, 非常有用. 比如日期格式为'yyyy/mm/dd'时, 稍微不注意,有些程序语句会报错. 以下资料网络收集: 1. Set ...

  10. 通过SSIS的“查找”组件进行不同数据源之间数据的合并操作

    原文:通过SSIS的"查找"组件进行不同数据源之间数据的合并操作 为了协助开发还原生产环境中的某些bug,需要将将生产环境的某些特定表数据导入到测试环境做测试,之前一直都是暴力地t ...