• vue实战总结


     

    vue实战总结

    仿饿了么外卖APP,部分总结以及部分实现如下:

     

    App.vue

            1.App.vue在HTML中使用router-link标签来导航,默认被渲染成一个a标签,通过传入to属性指定链接;
    <div class="tab-item">
        <router-link to="/goods">商品</router-link>
    </div>
    <div class="tab-item">
        <router-link to="/ratings">评论</router-link>
    </div>
    <div class="tab-item">
        <router-link to="/seller">商家</router-link>
    </div>
             渲染后:
          2.在main.js中配置路由
    • 导入Vue和VueRouter,并调用Vue.use(VueRouter)
    • 定义组件:import子组件
    import Vue from 'vue';
    import VueRouter from 'vue-router';
    import goods from 'components/goods/goods.vue';
    import ratings from 'components/ratings/ratings.vue';
    import seller from 'components/seller/seller.vue';
    import App from './App.vue';
    • 定义路由:每个路由映射一个组件
    const routes = [
        {
            path: '/goods',
            component: goods
        },
        {
            path: '/ratings',
            component: ratings
        },
        {
            path: '/seller',
            component: seller
        }
    ];
    • 创建router实例:传入routes配置
    const router = new VueRouter({
        routes: routes
    });
    • 创建和挂载根实例:通过router配置参数注入路由,从而让整个应用都有路由功能
    const app = new Vue({
        el: '#app',
        template: '<App/>',
        components: {App},
        router
    });
            3.在App.vue中添加router-view(最顶层的出口,渲染最高级路由匹配到的组件),使用keep-alive,把切换出去的组件保存在内存中,保留它的状态或避免重新渲染
    <keep-alive>
      <router-view :seller="seller"></router-view>
    </keep-alive>
            4.引入data.json中的数据
    created() {
      this.$http.get('/api/seller').then((response) => {
          response = response.body;
          if (response.errno === ERR_OK) {//const ERR_OK = 0;
              this.seller = response.data;
          }
      });
    }        
           5.引入header组件,并将seller数据传给header组件
           在JS中import header组件,并在components中注册,在template模板中使用header
    <v-header :seller="seller"></v-header>

    header.vue

           1.接收父组件传来的数据
    props: {
        seller: {
            type: Object
        }
    }
            2.写模板以及方法
           在img中引入图片来源:
    <img  width="64" height="64" :src="seller.avatar">
           如果有优惠活动,显示第一个优惠活动:data.json中定义了优惠活动的type序号,对应classMap中的活动名称
    <div v-if="seller.supports" class="support">
        <span class="icon" :class="classMap[seller.supports[0].type]"></span>
        <span class="text">{{seller.supports[0].description}}</span>
    </div>
    created() {
      this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee'];
    },
            点击展开活动详情及商家公告:弹出层绝对定位,@click控制弹出层的v-show的true或false值,并设置默认值为false
    <div class="support-count" v-if="seller.supports" @click="showDetail">
      <span class="count">{{seller.supports.length}}</span>
      <i class="icon-keyboard_arrow_right"></i>
    </div>
    methods: {
      showDetail() {
        this.detailShow = true;
      }
    }
    <div class="detail" v-show="detailShow">
    data() {
      return {
        detailShow:false
      };
    }
            遍历优惠活动的小图标及文字:在公共样式表中封装bg-image()样式,在不同的分辨率下显示不同大小的图标,通过优惠活动的type选择不同的样式
    <ul v-if="seller.supports" class="supports">
      <li class="support-item" v-for="(item,index) in seller.supports">
        <span class="icon" :class="classMap[seller.supports[index].type]"></span>
        <span class="text">{{seller.supports[index].description}}</span>
      </li>
    </ul>
    bg-image($url)
      background-image:url($url+"@2x.png")
      @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3)
        background-image:url($url+"@3x.png")
    &.decrease
      bg-image('decrease_2')
    &.discount
      bg-image('discount_2')
    &.guarantee
      bg-image('guarantee_2')
    &.invoice
      bg-image('invoice_2')
    &.special
      bg-image('special_2')
            3.引入star组件,传入star图标的size和score
    <star :size="48" :score="seller.score"></star>

    star.vue

    <div class="star" :class="starType">
      <span v-for="itemClass in itemClasses" :class="itemClass" class="star-item" track-by="$index"></span>
    </div>
            1.在计算属性中,starType根据传入的size来定义样式
    starType() {
      return 'star-' + this.size;
    }
            2.itemClasses返回评星状态,根据传入的score来计算满星、半星和无星的个数,result数组装入星星的状态
    const CLS_ON = 'on';
    const CLS_OFF = 'off';
    const CLS_HALF = 'half';
    itemClasses() {
      let result = [];
      let score = Math.floor(this.score * 2) / 2;
      let hasDecimal = score % 1 !== 0;
      let integer = Math.floor(score);
      for (let i = 0; i < integer; i++) {
        result.push(CLS_ON);
      }
      if (hasDecimal) {
        result.push(CLS_HALF);
      }
      while (result.length < LENGTH) {
        result.push(CLS_OFF);
      }
      return result;
    }
            3.:class="itemClass"表示遍历出的星星的状态,状态不同,显示星星的状态图标即不同,class为on即显示满星,以此类推
    &.star-48
      .star-item
         20px
    height: 20px
    margin-right: 22px
    background-size:20px 20px
    &:last-child
          margin-right:0
        &.on
          bg-image("star48_on")
        &.half
          bg-image("star48_half")
        &.off
          bg-image("star48_off")

    goods.vue

            1.通过props导入数据并在li中循环,若是优惠活动,则加上小图标,并获得数组索引,在计算属性中获取内容栏滚动位置的索引,改变菜单栏的背景颜色
    <ul>
      <li v-for="(item,index) in goods" class="menu-item" :class="{'current':currentIndex===index}" @click="selectMenu(index,$event)">
        <span class="text border-1px">
          <span v-show="item.type>0" class="icon" :class="classMap[item.type]"></span>{{item.name}}
        </span>
      </li>
    </ul>
    currentIndex() {
      for (let i = 0; i < this.listHeight.length; i++) {
        let height1 = this.listHeight[i];
        let height2 = this.listHeight[i + 1];
        if (!height2 || (this.scrollY >= height1 && this.scrollY < height2)) {
          return i;
        }
      }
      return 0;
    }
            2.绑定菜单栏和内容栏的点击及滚动:菜单栏绑定点击事件,获取点击的index的元素,内容栏滚动到该元素
    <li v-for="item in goods" class="food-list food-list-hook">
    selectMenu(index, event) {
      if (!event._constructed) {
        return;
      }
      let foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook');
      let el = foodList[index];
      this.foodsScroll.scrollToElement(el, 300);
    }
            3.引入购物车组件,并传入选中的food以及配送费、最低配送价格,food.count为cartcontrol组件中的变量,为商品个数
    <shopcart :selectFoods="selectFoods" :deliveryPrice="seller.deliveryPrice" :minPrice="seller.minPrice"></shopcart>
    selectFoods() {
      let foods = [];
      this.goods.forEach((good) => {
        good.foods.forEach((food) => {
          if (food.count) {
            foods.push(food);
          }
        });
      });
      return foods;
    }
            4.导入food组件,展开菜品详情页,并传入已选中的菜品,ref 被用来给元素或子组件注册引用信息
    <food :food="selectedFood" ref="food"></food>

    shopcart.vue

            判断是否达到结算要求
    <div class="pay" :class="payClass">
      {{payDesc}}
    </div>
    payDesc() {
      if (this.totalPrice === 0) {
        return `¥${this.minPrice}元起送`;
      } else if (this.totalPrice < this.minPrice) {
        let diff = this.minPrice - this.totalPrice;
        return `还差¥${diff}元起送`;
      } else {
        return '去结算';
      }
    },
    payClass() {
      if (this.totalPrice < this.minPrice) {
        return 'not-enough';
      } else {
        return 'enough';
      }
    }

    food.vue

            点击加入购物车,商品数量显示为1,购物车加入数量及价格
    <div class="cartcontrol-wrapper">
      <cartcontrol :food="food"></cartcontrol>
    </div>
    <div @click="addFirst" class="buy" v-show="!food.count || food.count===0">加入购物车</div>
            引入ratingSelect组件,显示商品评价的类别及是否只看有内容的评价,v-on接收来自子组件的参数
    <ratingselect v-on:ratingTypeSelect="chooseType" v-on:contentToggle="chooseOnly" :selct-type="selectType" :only-content="onlyContent" :desc="desc" :ratings="food.ratings"></ratingselect>
            使用过滤器,引入公共js方法,将时间戳转换为yyyy:MM:dd hh:mm的时间格式
    <div class="time">{{rating.rateTime | formatDate}}</div>
    import {formatDate} from '../../common/js/date';
    //date.js
    export function formatDate(date, fmt) {
      if (/(y+)/.test(fmt)) {
        fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
      }
      let o = {
        'M+' :date.getMonth() + 1,
        'd+' :date.getDate(),
        'h+' :date.getHours(),
        'm+' :date.getMinutes(),
        's+' :date.getSeconds()
      };
      for (let k in o) {
        if (new RegExp(`(${k})`).test(fmt)) {
          let str = o[k] + '';
          fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
        }
      }
      return fmt;
    };
    
    function padLeftZero(str) {
      return ('00' + str).substr(str.length);
    }
    
    filters: {
      formatDate(time) {
        let date = new Date(time);
        return formatDate(date, 'yyyy-MM-dd hh:mm');
      }
    }
            实现父组件与子组件通信,选择评论类别,评论改变
    chooseType(type) {
      this.selectType = type;
      this.$nextTick(() => {
        this.scroll.refresh();
      });
    },
    chooseOnly(onlyContent) {
      this.onlyContent = onlyContent;
      this.$nextTick(() => {
        this.scroll.refresh();
      });
    }

    ratingSelect.vue

           评论类别选择按钮
    <span @click="select(2,$event)" class="block positive" :class="{'active':selectType===2}">{{desc.all}}<span class="count">{{ratings.length}}</span></span>
            将评论类别及是否只显示内容传入父组件
    select(type, event) {
      if (!event._constructed) {
        return;
      }
      this.selectType = type;
      this.$emit('ratingTypeSelect', type);
    },
    toggleContent(event) {
      if (!event._constructed) {
        return;
      }
      this.onlyContent = !this.onlyContent;
      this.$emit('contentToggle', this.onlyContent);
    }

    seller.vue

            商家实景,遍历实景图片,并横向滑动
     
    <div class="pic-wrapper" ref="picWrapper">
      <ul class="pic-list" ref="picList">
        <li class="pic-item" v-for="pic in seller.pics">
          <img :src="pic" width="120" height="90">
        </li>
      </ul>
    </div>
    _initPics() {
      if (this.seller.pics) {
        let picWidth = 120;
        let margin = 6;
        let width = (picWidth + margin) * this.seller.pics.length - margin;
        this.$refs.picList.style.width = width + 'px';
        this.$nextTick(() => {
          this.picScroll = new BScroll(this.$refs.picWrapper, {
            scrollX: true,
            eventPassScroll: 'vertical'
          });
        });
      }
    }
    转自:http://blog.csdn.net/qq_14863671/article/details/54412254
  • 相关阅读:
    Codeforces 787B. Not Afraid
    Codeforces 670D. Magic Powder
    POJ 1979 Red and Black
    T1215:迷宫
    POJ 1163 The Triangle
    洛谷P1219 八皇后
    T1212:LETTERS
    T1317:【例5.2】组合的输出
    洛谷P1706 全排列问题
    codevs 5971 打击犯罪
  • 原文地址:https://www.cnblogs.com/wl521/p/7601582.html
Copyright © 2020-2023  润新知