Vue 跨组件传参 (1.事件总线eventBus,2.vuex)实现购物车

该博客介绍了如何使用Vue.js的事件总线(eventBus)实现跨组件通信,具体应用是在商品列表和购物车组件之间传递数据。当用户点击购买按钮时,商品会添加到购物车,并自动更新购物车数量。购物车数据存储在本地,页面刷新后数据不丢失。此外,还展示了计算属性的使用,用于计算购物车总价。
摘要由CSDN通过智能技术生成

案例实现购物车, 跨组件传参。这里用的是事件总线eventBus .。
动态组件。
点击购买按钮后,购物车数量统计自动++
,相同产品自加1,没有的商品添加到购物车。
点击购物车时,能看到加入的商品数据。
刷新页面数据不会丢失。
(计算属性要有return 返回值得。)
在这里插入图片描述
在这里插入图片描述

代码如下

<style>
  html,
  body,
  ul,
  li {
    margin: 0;
    padding: 0;
  }
  ul li {
    list-style: none;
  }
  .product {
    display: flex;
    justify-content: center;
    align-items: center;
    border-bottom: solid 0.01rem fuchsia;
    padding: 0.5rem 1.5rem;
    flex-direction: column;
  }
  .product img {
    width: 75%;
  }
  .product h5 {
    color: deeppink;
  }
  html,
  body,
  #app {
    height: 100%;
  }
  #app {
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  .list {
    flex: 1;
    height: 100%;
    overflow: auto;
  }
  .nav {
    height: 60px;
  }
  .nav ul {
    display: flex;
    justify-content: space-around;
  }
  .nav .active {
    color: deeppink;
  }
  .nav ul li {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    position: relative;
  }
  .nav ul li img {
    width: 26px;
    margin: 0.5rem;
  }
  .nav ul li span {
    font-size: 0.8rem;
  }
  .dot {
    position: absolute;
    z-index: 10;
    width: 20px;
    height: 20px;
    padding: 0.2rem;
    border-radius: 50%;
    background-color: rgba(200, 0, 0, 0.7);
    color: white;
    text-align: center;
    top: -6px;
    left: 20px;
  }
  .btn {
    border: none;
    background-color: orangered;
    text-align: center;
    color: white;
    width: 5rem;
    height: 1.5rem;
  }
  .title {
    padding: 0;
    margin: 0.5rem 1rem;
  }
  .sum-price {
    position: fixed;
    background-color: #4bd67c;
    bottom: 70px;
    right: 5px;
    padding: 0.5rem 1rem;
    color: white;
  }
</style>


<div id="app">
  <!-- 动态组件,通过设置is属性绑定当前显示的组件内容 -->
  <component style="height: 100%;" :is="currentComponent"></component>

  <div class="nav">
    <ul>
      <li @click="setPage('list', 0)" :class="{active: selectIndex == 0}">
        <img
          :src="selectIndex==0? './assets/images/list-sel.png': './assets/images/list.png'"
        />
        <span>商品列表</span>
      </li>
      <li @click="setPage('cart', 1)" :class="{active: selectIndex == 1}">
        <img
          :src="selectIndex==1? './assets/images/cart-sel.png': './assets/images/cart.png'"
        />
        <span>购物车</span>
        <span class="dot">{{cartCount}}</span>
      </li>
    </ul>
  </div>
</div>
<!-- 就会找一个离我们最近的服务器进行访问,加快访问速度 -->
<!-- cdn服务器部署和开发人员没有关系,是运维的环节 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@2.1.1/dist/jquery.min.js"></script>
<script>
  // 非相关组件之间传参 使用事件总线
  //  所谓事件总线是一种开发思想,所有的事件派发和监听都是同一个空白实例中进行
  var eventBus = new Vue();
  // 我们在vue对象的prototype属性上定义一个变量
  //  此属性在vue实例中能被访问到
  Vue.prototype.$eventBus = eventBus;
  // 首页
  var list = {
    template: `<ul class="list">
        <li class="product" v-for="item in products" :key="item.skuId">
          <img :src="item.image" :alt="item.name" />
          <h5>{{item.name}}</h5>
          <button class="btn" @click="buy(item)">购买</button>
        </li>
      </ul>`,
    data() {
      return { products: [] }; //
    },
    created() {
      $.getJSON(
        "https://o2api.jd.com/data?body=%7B%22query%22%3A%22query%20getCommodities(%24ids%3A%20String)%7Bcommodities(ids%3A%20%24ids)%7BgroupId%2C%20groupName%2C%20productList%7BcanSell%20skuId%20name%20image%20commentCount%20goodRate%20jdPrice%20pcpPrice%20plusPrice%20tag%20copyWriting%20copyWritingDown%20backUpWords%7D%7D%7D%22%2C%22operationName%22%3A%22getCommodities%22%2C%22variables%22%3A%7B%22ids%22%3A%22%5B03504985%2C03505081%5D%22%7D%2C%22config%22%3A%7B%22cache%22%3Afalse%2C%22trim%22%3Atrue%2C%22map%22%3A%7B%22keyBy%22%3A%22groupId%22%2C%22valueField%22%3A%22productList%22%7D%7D%7D&_=1568689310891"
      ).then((res) => {
        this.products = res.data["03504985"];
      });
    },
    methods: {
      buy(product) {
        // 事件派发, 参数一叫事件名字,参数二是传递的参数
        this.$eventBus.$emit("buyClick", product);
      },
    },
  };

  // 购物车页面
  var cart = {
    template: `
    <div class="list">
      <h1 class="title">购物车</h1>
      <ul>
        <li class="product" v-for="item in products" :key="item.skuId">
          <img :src="item.image" :alt="item.name" />
          <h5>{{item.name}}</h5>
          <p>
            <button @click="subj(item)">-</button>
            <input v-model="item.isChecked" type="checkbox"/>
              {{item.jdPrice}}X{{item.amount}}
            <button @click="item.amount+=1">+</button>
          </p>
        </li>
      </ul>
      <div class="sum-price">
        <p>总价:¥{{sumPrice}}元</p>
      </div>
    </div>`,
    data() {
      return {
        products: [],
      };
    },
    
    created() {
      // 在购物车页面,当已进入这个页面就从本地取数据
      var strShopCarts = localStorage.getItem("shop-carts");
      if (strShopCarts) {
        this.products = JSON.parse(strShopCarts);
      }
    },
    computed: {
      // 计算属性设置总价
      sumPrice() {
        return this.products
          .filter((item) => item.isChecked) // 过滤
          .reduce((pre, cur) => pre + cur.jdPrice * cur.amount, 0); // 求和
      },
    },
    methods:{
		subj(item){
			console.log(item, ‘是接受减法时的参数对象’)
			item.amount--
            if(item.amount<1){ // 对象的数量小于1时,数量为0.
              item.amount = 0
            }
		}
	}
  };

  var app = new Vue({
    el: "#app",
    data: {
      selectIndex: 0,
      currentComponent: "list",
      // cartCount: 0,
      shopCarts: [], // 购物车数据
    },
    // 注册组件
    components: {
      list,
      cart,
    },
    methods: {
      // 动态接受页面组件
      setPage(page, index) { 
        // 设置当前选中的组件
        this.currentComponent = page;
        // 设置当前选中的底部索引
        this.selectIndex = index;
      },
    },
    created() {
      // 做事件监听 v代表传递过来的商品信息
      this.$eventBus.$on("buyClick", (v) => {
        // console.log(v);
        // 查找符合条件的数据的下标 findIndex()
        var index = this.shopCarts.findIndex(
          (item) => item.skuId === v.skuId
        );
        if (index > -1) {
          // this.shopCarts 
          // 如果存在这个商品数量就加1 
          this.shopCarts[index].amount += 1;
        } else {  // 如果没有就新加入购物车一个商品数据
          // 使用了对象的扩展运算符
          this.shopCarts.push({ ...v, amount: 1, isChecked: false });
        }

        // 存储到本地 为了点击购物车的时候可以看到加入购物车的数据
        localStorage.setItem("shop-carts", JSON.stringify(this.shopCarts));
        // if(this.shopCarts.findIndex())
        // this.cartCount++;
      });
      var strShopCarts = localStorage.getItem("shop-carts");
      if (strShopCarts) {
        this.shopCarts = JSON.parse(strShopCarts);
      }
    },
    computed: {
      cartCount() {
        // 根据当前的shopCarts数据计算总数量
        return this.shopCarts.reduce((pre, cur) => {
          return pre + cur.amount;
        }, 0);
      },
    },
  });
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值