路由组件传参
我们通常把路由直接映射(绑定)的组件称为 路由组件,也只有路由组件才能直接调用路由有关对象:$router
、$route
当一个组件既想作为路由组件使用,又想作为功能组件(某个页面中的一部分)去使用时,就可通过路由组件传参的方式来实现。
一、案例(局部组件实现悬浮穿查看商品详情)
我们对 item.vue 组件进行改造,当我们在 home.vue 的查看详情上移入移出,出现商品信息提示层
重点注意:
1.在Home.vue中增加局部组件用于显示商品信息悬浮窗,在查看详情链接上加上mouseover、mouseout事件,并通过.native限制修饰符将此事件所在组件标签当作原生html标签使用;
//Home.vue
<!-- 在组件标签上加上.native修饰符,就相当于直接拿组件标签当做原生的html标签在使用 -->
<router-link
:to="{name: 'item', params:{id: item.id}}"
@mouseover.native="mouseover(item.id,$event)"
@mouseout.native="mouseout(item.id,$event)"
>查看详情</router-link>
</span>
</li>
</ul>
<div class="tip" :style="tip.boxSty" v-show="tip.isShow">
<Item :id="tip.id"></Item>
</div>
2.局部组件使用时,需要在父组件中引入
//Home.vue
import Item from "./Item.vue";
export default {
name: "Home",
// 注意如果要使用局部组件,必须先引入再挂载到父组件的components上才行
components: {
Item,
},
......
}
3.通过v-show组件标签显示/隐藏悬浮窗,并通过data中tip数据控制悬浮窗大小和是否隐藏isShow,以及商品信息id
//Home.vue
...
<div class="tip" :style="tip.boxSty" v-show="tip.isShow">
<Item :id="tip.id"></Item>
</div>
....
data() {
return {
items: [],
sort: "desc",
// 显示隐藏提示
tip:{
boxSty:{
left: '300px', top: '200px', width:'300px', height:'250px',
},
isShow:false,
id: 0,
}
};
},
....
methods: {
...
mouseover(itemId, e) {
// 鼠标经过时显示商品详情提示层
this.tip.id = itemId;
this.tip.isShow = true;
},
mouseout(itemId, e) {
// 鼠标易初时隐藏商品详情提示层
console.log(itemId, e);
this.tip.isShow = false;
//
}
},
/* Home.vue 商品提示层 */
.tip {
position: fixed;
left: 0;
top: 0;
border: 1px solid #000;
background: #fff;
padding: 10px;
}
4. 因为原来的Item.vue是通过this.$route.params.id来接收id获取商品信息的,但此时作为功能组件id就需要通过props传递。通过props:['id']接收传值,并通过watch监听id变化(局部组件在Home页面刷新时会调用一次,而鼠标悬浮时又会重新调用,此时id就会变化)
//Item.vue
...
props:['id'],
...
// 在鼠标悬浮时,Item发生变化,也需要获取一次Item商品信息
watch:{
id(){
this.getItem();
}
},
// 点击和鼠标悬浮时都需要获取商品信息
methods:{
getItem(){
if(this.id>0){
axios({
// 使用路由组件props传参后,必须使用this.id才能获取到
url: `/api/item/${this.id}`
}).then(res => {
this.item = res.data;
});
}
}
5.通过Item.vue中props:['id']可以通过props将id从Home页面传递到item页面;但是这样就不能 处理来自路由的 params 参数了。为了能让 Item.vue 组件既能接收 props 传递的参数,也能接收 route.params 传递的参数,需要对路由也进行一些改造,即设置props:true
{
// 详情页:页面以动态路由形式赋值时:to='{name: "item", params:{id: item.id}}',需要给路由设置name才能赋值
name: 'item',
// :id配置在route配置时
path: '/item/:id',
component: Item,
// 当props: true时,route.params中的数据会自动被设置为组件属性并与组件原有props进行合并
props: true,
// 利用路由元信息进行鉴权
meta: {
// requiresAuth: true
}
},
完整Home.vue代码:
//Home.vue
<template>
<div class="home">
<h2>商品列表——首页</h2>
<!-- 使用v-model 绑定排序,并使用 watch进行监听-->
<select v-model="sort">
<option value="desc">从高到低</option>
<option value="asc">从低到高</option>
</select>
<ul class="item-list">
<li class="head">
<span>名称</span>
<span>品牌</span>
<span>价格</span>
<span>操作</span>
</li>
<!-- 当悬浮在上面时显示商品详细信息 -->
<li v-for="item of items" :key="item.id">
<span>{{item.name}}</span>
<span>{{item.vendor}}</span>
<span>{{item.price|RMB}}</span>
<span>
<!-- 注意:用v-bind方式,则属性值里必须是表达式-->
<!-- <router-link :to="`/item/${item.id}`">查看详情</router-link> -->
<!-- 以动态路由形式赋值,注意一定要以v-bind形式进行绑定,后台路由需要设置name:'item' -->
<!-- 在组件标签上加上.native修饰符,就相当于直接拿组件标签当做原生的html标签在使用 -->
<router-link
:to="{name: 'item', params:{id: item.id}}"
@mouseover.native="mouseover(item.id,$event)"
@mouseout.native="mouseout(item.id,$event)"
>查看详情</router-link>
</span>
</li>
</ul>
<div class="tip" :style="tip.boxSty" v-show="tip.isShow">
<Item :id="tip.id"></Item>
</div>
</div>
</template>
<script>
//通过axios发送异步请求
import axios from "axios";
import RMB from "@/filter/RMB";
import Item from "./Item.vue"
export default {
name: "Home",
// 注意如果要使用局部组件,必须先引入再挂载到父组件的components上才行
components: {
Item,
},
// 注册过滤器
filters: {
RMB
},
data() {
return {
items: [],
sort: "desc",
// 显示隐藏提示
tip:{
boxSty:{
left: '300px', top: '200px', width:'300px', height:'250px',
},
isShow:false,
id: 0,
}
};
},
async created() {
// 注意this.sort获取位置,是在组件渲染完后立即赋值,否则有可能没有获取到,从而产生异步问题
this.sort = this.$route.query.sort || this.sort;
// 注意请求需要使用async await
await this.getItems();
},
// 使用watch监听组件变化,注意watch是对象
watch: {
sort() {
this.$router.push({
// 此处name对应路由中Home组件的路由name
name: "home",
// 此处为地址栏上地址加上queryString
query: {
sort: this.sort
}
});
}
},
methods: {
async getItems() {
await axios({
// 通过axios发起异步代理请求
url: "/api/items",
// 此处为发送给后台请求地址加上queryString
params: {
sort: this.sort
}
}).then(res => {
// 将获取到的值设置到data中
this.items = res.data;
});
},
mouseover(itemId, e) {
// 鼠标经过时显示商品详情提示层
this.tip.id = itemId;
this.tip.isShow = true;
},
mouseout(itemId, e) {
// 鼠标易初时隐藏商品详情提示层
console.log(itemId, e);
this.tip.isShow = false;
//
}
},
// 组件被复用时调用(更新):如表单填写后离开页面时询问是否要提交页面等需求
beforeRouteUpdate(to, from, next) {
this.getItems();
next();
},
};
</script>
<style>
ul {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
.item-list li {
padding: 10px;
display: flex;
justify-content: space-between;
height: 30px;
line-height: 30px;
border-bottom: 1px dotted #333;
}
.item-list li.head {
font-weight: bold;
}
.item-list li span {
min-width: 200px;
}
/* 商品提示层 */
.tip {
position: fixed;
left: 0;
top: 0;
border: 1px solid #000;
background: #fff;
padding: 10px;
}
</style>
完整Item.vue代码:
<template>
<div>
<template v-if="item">
<h2>商品详情 - {{item.name}}</h2>
<dt>ID</dt>
<dd>{{item.id}}</dd>
<dt>名称</dt>
<dd>{{item.name}}</dd>
<dt>价格</dt>
<dd>{{item.price|RMB}}</dd>
</template>
<template v-else>
<h2>没有该商品信息</h2>
</template>
</div>
</template>
<script>
import axios from "axios";
import RMB from "@/filter/RMB";
export default {
name: "Item",
// 注册过滤器
filters: {
RMB
},
// 因为原来的Item.vue组件是通过 `this.$route.params.id` 来接收 `id` 的,但是作为功能组件 `id` 需要通过prop来传入,此时需要对Item.vue组件进行改造
props:['id'],
data() {
return {
item:null
};
},
async created() {
// 通过动态路由和拼接字符串形式都需要通过 路由对象 获取id的值
// const {id} = this.$route.params;
await this.getItem();
},
// 在鼠标悬浮时,Item发生变化,也需要获取一次Item商品信息
watch:{
id(){
this.getItem();
}
},
// 点击和鼠标悬浮时都需要获取商品信息
methods:{
getItem(){
if(this.id>0){
axios({
// 详情页:id占位符
// url: "/api/item/"+id
// 使用params传参
// url: `/api/item/${id}`
// 使用路由组件props传参后,必须使用this.id才能获取到
url: `/api/item/${this.id}`
}).then(res => {
this.item = res.data;
});
}
}
}
};
</script>
<style>
</style>
二、默认处理
当 props
设置 为 true
,那么 route.params
中的数据自动就会被设置为组件属性与组件原有 props 进行合并
三、对象模式
我们也可以有选择的返回 props
{
path: '/item/:itemId',
name: 'item',
component: Item,
props: {a: 1, b: 2}
}
四、回调函数模式
也可以使用回调函数模式
{
path: '/item/:itemId',
name: 'item',
component: Item,
props: r => ({ itemId: Number(r.params.itemId) })
}