我们在开发单页面应用时,经常需要进入某个路由后使用传递过来的参数从服务器获取数据,那么我们是怎样实现路由组件间通信的呢,这里我给大家总结出了四种路由组件间常用的通信方式。
一、基本知识
想要掌握路由传参,首先我们得知道跳转路由的2种基本方式,即声明式路由导航和编程式路由导航。
声明式路由导航
直接写在模板(template)中,结构简单使用方便,但是只能放在<router-link>标签中使用,<router-link>标签会将路由转成<a>标签,通过点击跳转路由,因此局限性也非常大。
基本使用:
要想能够跳转,需要将所有的a标签换成<router-link>。
<router-link :to="xxx" ></router-link>
要想让组件在页面中的相应位置显示,要使用<router-view>。
<router-view></router-view>
编程式路由导航:
需要写在js中,结构也不算复杂,优势在于非常灵活,不受固定标签限制,可以在任意情景下转跳路由。
路由对象$router是VueRouter的一个对象,通过Vue.use(VueRouter)和Vue构造函数得到一个router的全局实例对象,他包含了所有的路由,包含了许多关键的对象和属性。
在Vue实例内部,我们可以通过$router访问路由实例。从而就可以调用push方法,等同于点击<router-link>跳转。
基本使用:
this.$router.push()
// 字符串
this.$router.push('home')
// 对象
this.$router.push({ path: 'home' })
// 命名的路由
this.$router.push({ name: 'user', params: { userId: '123' }}
replace跟push很像,唯一的不同就是,它不会向history添加新记录,而是直接替换掉当前的history记录。
基本使用:
this.$router.replace()
注意点:
如果使用path跳转的话,必须使用query方式传参,若使用params传参的话params参数会丢失。
声明式路由导航和编程式路由导航的区别
- 声明式导航是直接渲染到页面的,比如a链接;
- 编程式导航则是用在js处理逻辑后需要页面跳转,比如点击button按钮跳转。
二、query
query传参类似于网络请求中的 get 请求,query传过去的参数会拼接在地址栏中(?name=xx)。query较为灵活既可以配合path使用,也能配合name使用。
声明式
<router-link :to="{ path: '/home', query: { username: username } }">
// 取值
this.$route.query.username
编程式
data:{
username: ''
},
login() {
...
this.$router.push({
path: '/home',
query: { username: this.username },
})
}
三、params
params传参类似于网络请求中的post请求,当使用编程式路由导航使用path跳转时,如果没有在对应的位置添加params参数,params传过去的参数就不会显示在地址栏中(并且不能刷新)。
声明式
<router-link :to="{ name: 'home', params: { username: username } }">
// 取值
this.$route.params.username
编程式
data:{
username: ''
},
login() {
...
this.$router.push({
name: 'home', //注意使用 params 时一定不能使用 path
params: { username: this.username },
})
}
注意事项
当使用编程式路由导航时通过params传参后,刷新页面会失去拿到的参数。所以路由参数要修改为 '/login/:username'(官方称为动态路由)。
const routes = [
{
path: '/login',
component: Login
},
{
path: '/home/:username',
name: '/home',
component: Home
}
但是这样就不会类似于 post 请求,他会把接收到的参数替换作为地址。
假如传入参数为:params: { username: ‘admin’},那么最终访问的地址为:http://localhost:8080/home/admin。
query传参和params传参的区别
query可以通过name属性或者path属性来引入路由,而params只能通过name属性来引入路由;在使用params传递时如果指定了path属性而没有name属性,那么界面能成功跳转但是不能接受到传递过来的参数;
query传递参数时会地址栏会发生改变,传递参数会携带在路径中,而params则不会;
编程式导航使用path跳转时,在刷新界面后,query传递的参数不会丢失,而params会丢失。
四、props
我们可以通过配置路由时的props属性,将params/query携带的参数,在组件中用props属性来接收,这样用时可以直接使用,就不需要$route.params.xxx/$route.query.xxx的形式了。
路由规则配置中,props参数的使用有如下三种方式:
- props值是一个对象。
- props值是一个布尔值。
- props值是一个函数。
配置方法
{
name: "detail",
path: "/detail",
component: Detail,
/**
方式一,值为对象,对象中的key-value会以props的形式传递给Detail组件,
但是传递的值都是一样的,不推荐
props: {
id: '123',
title: '消息001',
},
**/
/**
方式二,值为布尔值,若布尔值为真,就会把该组件收到的所有params参数,以props的形式传式传递给Detail组件, 但之这种方式只适用于params参数
props: true,
**/
/**
方式三,值为函数,内置传参$route,可以使用结构赋值形式
**/
props({ query }) {
return { id: query.id, title: query.title };
},
}
五、meta
我们经常会在进入一个页面时判断是否已经登陆,经常会用到路由导航守卫router.beforeEach(to, from, next), 一个两个页面还好,但是多的话,就会麻烦,并且路由还会嵌套。这时可以使用meta。
在配置路由时,我们可以给每个路由添加一个自定义的meta对象,在meta对象中可以设置一些状态,来进行一些操作。项目中经常用它来做登录校验。
const router = new VueRouter({
routes: [
{
path: "/detail",
component: Detail,
meta: { requiresAuth: true }, //meta参数可以是一个对象,也可以是一个数组,配置每个路由的一些信息,类似于每个路由的标识符
},
],
});
// 取值
this.$route.meta
六、实战bug解析
面试场景:编程式路由跳转到当前路由, 参数不变, 会报出错误?
说明情况:
之前的项目这种操作没有这个问题,后面的一个项目(2019.8之后)开始有这个问题, 而且是声明式跳转没有, 只有编程式跳转有。当编程式跳转到当前路由且参数数据不变, 就会出警告错误:错误: Avoided redundant navigation to current location(重复跳转当前路由)。
原因: vue-router在3.1.0版本(2019.8)引入了push()的promise的语法, 如果没有通过参数指定回调 函数就返回一个promise来指定成功/失败的回调, 且内部会判断如果要跳转的路径和参数 都没有变化, 会抛出一个失败的promise。
说明文档: https://github.com/vuejs/vue-router/releases?after=v3.3.1
解决:
办法1: 在每次push时指定回调函数或catch错误。
push('/xxx', () => {}) // 声明式路由跳转本质就是这样执行的。
push('/xxx').catch(() => {})
办法2: 重写VueRouter原型上的push方法 (比较好)。
如果没有指定回调函数, 需要调用原本的push()后catch()来处理错误的 promise。
如果传入了回调函数, 本身就没问题, 直接调用原本的push()就可以。
const originPush = VueRouter.prototype.push;
VueRouter.prototype.push = function (
location,
onComplete,
onAbort
) {
console.log("push()", onComplete, onAbort);
// 判断如果没有指定回调函数, 通过call调用源函数并使用catch来处理错误
if (onComplete === undefined && onAbort === undefined) {
// 使用的新语法
return originPush.call(this, location).catch(() => {});
} else {
// 如果有指定任意回调函数, 通过call调用源push函数处理
return originPush.call(this, location, onComplete, onAbort);
}
};
说明:声明式路由跳转之所有没有问题, 是因为默认传入了成功的空回调函数。
query和params分别都有三种形式可以传参:
- router-link形式。
- 通过path匹配路由的编程式导航形式。
- 通过name匹配路由的编程式导航形式。
而这其中只有params通过name匹配路由的编程式导航形式不会在路径中带有参数(且刷新页面参数不会丢失),其他都会带有参数值。