Vue shopCart 组件开发详解

一、shopCart组件

(1) goods 父组件和 子组件 shopCart 传参

deliveryPrice:{ // 单价 从json seller 对象数据中获取
 type:Number,
 default:0
},
minPrice:{ // 最低起送价 从json seller 对象数据中获取
 type:Number,
 default:20
}

其中 deliveryPrice 和 minPrice 的数据都是从 data.json数据 中 seller 对象下 获得。所以在goods 组件中还要 获取到 seller对象 的数据,否则会报错:

[Vue warn]: Error in render: "TypeError: Cannot read property 'deliveryPrice' of undefined"

解决方法:根组件 App.vue 中 router-view 组件获取seller 数据,传到 goods 组件中

1-1.app.vue (根组件 也是 goods 的父组件)

<keep-alive>
 <router-view :sell="sellerObj"></router-view>
</keep-alive>

注意:sellerObj 是data 定义 的 对象里用来接收 data.json 数据,相当于 实参

1-2.goods.vue (相对于跟组件的子组件 且 shopCart 的父组件)

通过props 属性 进行组件之间的通信

props: {
  sell: Object // 相当于 形参
 },

1-3.shopCart.vue ( goods 的子组件)

<shopCart :delivery-price="sell.deliveryPrice" :min-price="sell.minPrice"></shopCart>

(2) 选中商品 的 计算功能

1-1. 传入用户选中商品的集合

说明:从父组件会 传入一个用户选中商品的 数组,数组里会存放着 n 个对象,每个对象里存放着该 商品的 价格 和 数量。

props:{       // 通过父组件传过来的 ( 相当于形参 )
 selefoodsArr:{   // 用户选中的商品存放在一个数组里  接收的是 data.json数据的 goods(数组)
 type:Array, // 当父组件传过来的 类型是对象或者 是数组时, default 就是一个函数
 default (){
 return []  // 返回数组 存放着选中 商品 对应的 goods下的 foods 数组(由 父组件 的 实参 决定的返回值)
 }
}

1-2. 利用计算属性 选中商品数量的变化,商品总价,动态改变描述等功能

computed:{
 totalPrice (){     //计算总价,超过起送额度后提示可付款
 let total=0   // 定义一个返回值
 this.selefoodsArr.forEach((rfoods) =>{ // 遍历 这个 goods 数组 取到 价格 和 数量 (当然在这里数据库没有count 这个属性,稍后 我们会利用 vue.set() 新建一个count 属性)
  total += rfoods.price * rfoods.count // 形参 rfoods 实参 是 foods
 });
 return total;
 },
 totalCount (){   // //计算选中的food数量,在购物车图标处显示,采用绝对定位,top:0;right:0;显示在购物车图标右上角
 let count=0
 this.selefoodsArr.forEach((rfoods) =>{ // 形参 rfoods 实参 是 foods
  count += rfoods.count
 });
 return count;
 },
 payDesc (){    //控制底部右边内容随food的变化而变化,payDesc()控制显示内容,enough 添加类调整显示样式
 let diff = this.minPrice - this.totalPrice
    if (!this.totalPrice) {
     return `¥${this.minPrice}起送`
    } else if (diff > 0) {
     return `还差¥${diff}元`
    } else {
     return '去结算'
    }
 }
}

这样就渲染到 template 里了

<div class="shopCart">
 <div class="content">
  <div class="content-left">
 <div class="logo-wrapper">
 <!--徽章 展示选中商品的个数-->
 <div class="badge" v-show="totalCount">
 {{totalCount}}
 </div>
 <!--购物车 图标 选择商品和未选择商品 时 动态改变 样式 条件:只要选择了商品即总价不为0 ,样式变-->
  <div class="logo" :class="{'active':totalCount}">
   <i class="icon-shopping_cart"></i>
  </div>
 </div>
 <!--同理: 总价 不为0 字体高亮-->
 <div class="price" :class="{'active':totalPrice}">
  ¥{{totalPrice}}
 </div>
 <!--配送费 data.json 提供-->
 <div class="desc">
  另需要配送费¥{{deliveryPrice}}元
 </div>
  </div>
  <!--根据条件  动态 改变样式-->
  <div class="content-right" :class="{'enough':totalPrice>=minPrice}">
 {{payDesc}}
 </div>
 </div>
</div>

相关样式

&.active
  color white

&.enough
  background #00b43c
  color white

总结:通过以上学习我们能发现,selectFoods()的变化起着关键作用,它的变化会引起DOM的变化,并最终体现到界面上,而我们不用关注DOM内部的具体实现,这就是vue的一大好处。如果采用jQuery完成这些功能会略显繁杂。

二、cartControl 组件

说明:这个组件是控制购物车小球的。其中涉及到小球的动画

(1) 新增属性 count

说明:

在goods 下的 foods 添加一个属性 count,用来存储用户选中的商品个数,计算商品总价 以及 关联徽章(显示用户选择商品的个数)的变化

方法:通过import Vue from 'vue';使用set接口,通过vue.set()添加属性,当它变化时就能被检测到,从而父组件能获取到count值(遍历选中的商品时使用)

methods:{
 addCart(event){ // 点击count 加,
  //console.log(event.target);
 if (!event._constructed) { // 去掉自带click事件的点击
    return;
   }
 if(!this.foodsele.count){
 Vue.set(this.foodsele, 'count', 1)
 }else{
 this.foodsele.count++
 }
 },
 decreaseCart (event){ // 点击减少
 if (!event._constructed) { // 去掉自带click事件的点击
    return;
    }
 if(this.foodsele.count){
 this.foodsele.count --
  }
  }
}

(2)添加按钮 实现transtion 过渡

我们要实现的效果是:当点击添加按钮时,减少按钮出现 并伴随着 旋转、平移以及透明度变化的 一些 动画效果

<transition name='move'> <!--平移动画-->
 <div class="cart-decrease" v-show="foodsele.count" @click='decreaseCart($event)'>
  <span class="icon-remove_circle_outline inner"></span><!--旋转、透明度动画-->
  </div>
</transition>
 .cart-decrease
  display inline-block
  padding 6px
  transition: all .4s linear  /*过渡效果的 CSS 属性的名称、过渡效果需要多少时间、速度效果的速度曲线*/
  .inner
   line-height 24px
   font-size 24px
   color rgb(0,160,220)
   transition all 0.4s linear
  &.move-enter-active, &.move-leave-active
   transform translate3d(0,0,0) /* 这样可以开启硬件加速,动画更流畅,3D旋转,X轴位移24px */
   .inner
    display inline-block  /* 设置成inline-block才有高度,才能有动画 */
    transform rotate(0)
  &.move-enter, &.move-leave-active
   opacity: 0
   transform translate3d(24px,0,0)
   .inner
    transform rotate(180deg)

三、抛物线小球动画

通过两个层来控制小球,外层控制一个方向的变化,内层控制另外一个方向的变化(写两层才会有抛物线的效果),采用fixed布局(是相对于视口的动画)

事件发射和接收

组件之间传值-1

组件之间传值-2

扩展

Vue1.0组件间传递

  1. 使用$on()监听事件;
  2. 使用$emit()在它上面触发事件;
  3. 使用$dispatch()派发事件,事件沿着父链冒泡;
  4. 使用$broadcast()广播事件,事件向下传导给所有的后代

(1) Vue2.0 组件之间传递数据

1-1. 当点击 添加数量时 在 cartControl 组件里的 addCount 方法里 通过 $emit 属性 派发一个事件 , 传入点击的对象

addCart(event){ // 点击count 加,
//  console.log(event.target);
 if (!event._constructed) { // 去掉自带click事件的点击
    return;
   }
 if(!this.foodsele.count){
 Vue.set(this.foodsele, 'count', 1)
 }else{
 this.foodsele.count++
 }
// 当点击 添加数量时 通过 $emit 属性 提交一个名为 add 给父组件
// 子组件通过 $emit触发 add事件 ,将参数传递给父组件
 this.$emit('add', event.target);
}

1-2. 操作 goods 组件

购物车组件如果提交了addCart事件就调用add函数

 <cart-control :foodsele='food' @add="addFood"></cart-control>

父组件使用 @add="addFood"监听由子组件vm.$emit触发的事件,通过addFood()接受从子组件传递过来的数据,通知父组件数据改变了。

addFood(target) {
  this._drop(target);
}

1-3. 父组件访问子组件 vue 提供了接口 ref

代码如下:

<shopCart ref="shopCart" :delivery-price="sell.deliveryPrice" :min-price="sell.minPrice" :selefoods-arr='selectfoods'  ></shopCart>

_drop(target) {
  // 体验优化,异步执行下落动画
  this.$nextTick(() => {
   this.$refs.shopCart.balldrop(target);// 将target传入shopCart子组件中的balldrop方法,所以drop方法能获得用户点击按钮的元素,即能获取点击按钮的位置
  });
}  

区别 访问DOM 变量

1-3. 操作 shopCart 组件

data (){ // 定义一个数组 来 控制小球的状态  定义多个对象,表示页面中做多同时运动的小球
 return{ // 定义 5 个 小球
 balls:[{show:false},{show:false},{show:false},{show:false},{show:false}],
 dropBalls:[] // 接收下落小球
  }
}
methods:{
 balldrop(ele) {
// console.log(el) 取到点击 对象
   for(var i=0;i<this.balls.length;i++){
    let ball=this.balls[i]
    if(!ball.show){
     ball.show=true
     ball.ele=ele
     this.dropBalls.push(ball)
     return;
    }
   }
 }
}

动画过程开始,利用vue 提供的钩子函数

beforeEnter (el){ //找到所以设为true的小球
 let count=this.balls.length
 while(count--){
 let ball = this.balls[count];
 if(ball.show){
  let pos=ball.el.getBoundingClientRect() //返回元素相对于视口偏移的位置
  let x=pos.left-32  // 点击的按钮与小球(fixed)之间x方向的差值
  let y=-(window.innerHeight-pos.top-22)
  el.style.display = '';  //设置初始位置前,手动置空,覆盖之前的display:none,使其显示
       el.style.webkitTransform = `translate3d(0,${y}px,0)`; //外层元素做纵向的动画,y是变量
       el.style.transform = `translate3d(0,${y}px,0)`;
       let inner = el.getElementsByClassName('inner_hook')[0];//内层元素做横向动画,inner-hook(用于js选择的样式名加上-hook,表明只是用                                   //于js选择的,没有真实的样式含义)
       inner.style.webkitTransform = `translate3d(${x}px,0,0)`;
       inner.style.transform = `translate3d(${x}px,0,0)`;
 }
 }
 },
   enter(el) {
   /* eslint-disable no-unused-vars */
   let rf = el.offsetHeight;
   this.$nextTick(() => {//异步执行
   el.style.webkitTransform = 'translate3d(0,0,0)';  //重置回来
   el.style.transform = 'translate3d(0,0,0)';
   let inner = el.getElementsByClassName('inner_hook')[0];
   inner.style.webkitTransform = 'translate3d(0,0,0)';
   inner.style.transform = 'translate3d(0,0,0)';
  });
 },
 afterEnter(el) {
  let ball = this.dropBalls.shift(); //取到做完动画的球,再置为false,即重置,它还可以接着被利用
  if (ball) {
   ball.show = false;
   el.style.display = 'none';
  }
 }
<div class="ball-container">
  <div v-for="ball in balls">
   <transition name="drop" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
    <div class="ball" v-show="ball.show">
     <div class="inner inner_hook"></div>
    </div>
   </transition>
  </div>
</div>
&.drop-enter,&.drop-enter-active
    transition all 0.4s cubic-bezier(0.49,-0.29,0.75,0.41)
    .inner
     width 16px
     height 16px
     border-radius 50%
     background rgb(0,160,220)
     transition all 0.4s linear

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Vue shopCart 组件开发详解

    一.shopCart组件 (1) goods 父组件和 子组件 shopCart 传参 deliveryPrice:{ // 单价 从json seller 对象数据中获取 type:Number, default:0 }, minPrice:{ // 最低起送价 从json seller 对象数据中获取 type:Number, default:20 } 其中 deliveryPrice 和 minPrice 的数据都是从 data.json数据 中 seller 对象下 获得.所以在good

  • Vue header组件开发详解

    一. header 组件开发 之数据的传递 1. App.vue 引入组件 import header from './components/header/header' 2. App.vue 中注册组件 export default { components:{ v-header:header } } 3. 使用组件 <v-header :sell="sellerObj"></v-header> 解释::sell="sellerObj",这

  • Vue 短信验证码组件开发详解

    Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的库.Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件. Vue.js 自身不是一个全能框架--它只聚焦于视图层.因此它非常容易学习,非常容易与其它库或已有项目整合.另一方面,在与相关工具和支持库一起使用时,Vue.js 也能完美地驱动复杂的单页应用. 摘要: 1.该组件基于Vue 2.1.X版本: 1. Vue 组件代码如下: Vue.component('timerBtn

  • vue中组件通信详解(父子组件, 爷孙组件, 兄弟组件)

    vue中我们常常用到组件. 那么组件总体可以分为如下的几种关系. 父子组件, 爷孙组件, 兄弟组件. 这几种组件之间是如何通信的呢? 父子组件通信 根据vue中的文档可知, 组件的props属性用于接收父组件传递的信息. 而子组件想要向父组件传递信息, 可以使用$emit事件. 我们定义两个组件, 一个为父组件名为father, 另外一个为子组件child. 子组件通过props属性接收父组件传递的值, 这个值为fname, 是父组件的名字. 点击子组件的按钮, 触发toFather事件, 向父

  • Kotlin Service服务组件开发详解

    目录 服务简介 服务的创建 服务的启动方式 Service的生命周期 Activity和Service进行通信 实现前台Service 服务简介 服务是Android中的四大组件之一,它能够长期在后台运行且不提供用户界面.即使用户切到另一应用程序,服务仍可以在后台运行. 服务的创建 (1)创建Service子类 class MyService : Service() { override fun onBind(intent: Intent): IBinder { TODO("Return the

  • vue悬浮表单复合组件开发详解

    本文实例为大家分享了vue悬浮表单复合组件开发的具体代码,供大家参考,具体内容如下 组件样式 组件功能 卡片形式展示筛选条件点击添加筛选后展示悬浮表单表单内完成条件选择后点击保存,新增一个卡片 开发 <div class="form-label">筛选条件:</div> <template v-for="(item, index) in fitter">   <div :key="index" class

  • vue输入框组件开发过程详解

    本文实例为大家分享了vue输入框组件开发过程的具体代码,供大家参考,具体内容如下 input-number.js function isValueNumber(value) {     return(/(^-?[0-9]+\.{1}\d+$)|(^-?[1-9]*$)|(^-?0{1}$)/).test(value + ''); } Vue.component('input-number',{     template: '\     <div class=input-number>\   

  • Vue框架之goods组件开发详解

    一. 布局 Flex Flex 布局,可以简便.完整.响应式地实现各种页面布局,Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性.任何一个容器都可以指定为 Flex 布局. // 指定为 Flex 布局 display: flex: // 主要属性   flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]   flex属性是f

  • Vue异步组件使用详解

    Vue的异步组件,供大家参考,具体内容如下 1.前置要求 建议使用webpack: Browserify在默认情况下不支持: 2.用法解释 首先上官网说明:异步组件 虽然说明是没问题的,但是示例中的写法怪怪的,不符合一般新手学习者在实际使用中的习惯. 嗯,换句话说,这段代码告诉你,通过这种方式引入异步组件,然后他漏掉了一些内容,比如说赋值,如何使用之类. [1]官方示例代码: Vue.component('async-webpack-example', function (resolve) {

  • 微信小程序自定义tabBar组件开发详解

    本文实例为大家分享了微信小程序自定义tabBar组件的具体代码,供大家参考,具体内容如下 以下代码保存在github地址 先看一看目录 template文件夹里存放tabbar模板. template/template.wxml <template name="tabBar"> <view class="tabBar"> <block wx:for="{{tabBar}}" wx:for-item="ite

随机推荐