Vue(六)——vue-router(路由组件传参)(局部组件实现悬浮窗查看商品详情)

89 篇文章 7 订阅

路由组件传参

 我们通常把路由直接映射(绑定)的组件称为 路由组件,也只有路由组件才能直接调用路由有关对象:$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) })
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值