Vue全新技术栈重构黄老师饿了么商家应用总结

项目仓库地址:https://github.com/konglingwen94/vue-elm-sell

项目线上地址: http://123.56.124.33:5000

前提

自从学习了Vue后,能用Vue解决的场景用例最终我都尽可能的用Vue去实现。单纯的用例需求并没有完整的项目开发流程,从中能学到的东西也是有限的。在这之前除了使用Vue做过vue-music的移动端音乐播放器项目和vue-bytedanceJob(重构某独角兽互联网公司官方招聘网站)之外,自己并没有用Vue涉猎web端更复杂的业务场景。

为了找一个项目练习,我去github上开始了搜索,当看到https://github.com/ustbhuangyi/vue-sell这个项目时,感觉这个移动端应用的一些业务场景是自己没有接触过的,于是我就照着这个应用的UI功能用自己的知识体系技术栈进行了重构,大概不到半个月的时间,我完成了第一个commit提交到项目上线运行,本篇文章就从应用功能技术实现一些方面剖析此项目的开发过程以及采到的坑。

项目截图

<img src="https://user-gold-cdn.xitu.io/2020/7/1/17308b5c66a54ae5?w=286&h=500&f=gif&s=1854225" width="200">
<img src="https://user-gold-cdn.xitu.io/2020/7/1/17308b647f0cba7f?w=286&h=500&f=gif&s=838667" width="200">
<img src="https://user-gold-cdn.xitu.io/2020/7/1/17308b6220974f7e?w=286&h=500&f=gif&s=1786516" width="200">
<img src="https://user-gold-cdn.xitu.io/2020/7/1/17308b316156fd95?w=286&h=500&f=gif&s=954705" width="200">
<img src="https://user-gold-cdn.xitu.io/2020/7/1/17308b33d9cb205e?w=286&h=500&f=gif&s=1187009" width="200">

项目技术栈

  1. 前端

    • vue开发项目核心框架
    • axios HTTP请求模块
    • lib-flexible 移动端屏幕适配方案
    • better-scroll 仿IOS效果的移动端滚动库
    • normalize.css 第三方css样式初始化模块
    • es 6/7 下一代javascript语法
  2. 后端

    • express 搭建服务端应用核心框架
  3. 开发

    • vue-cli 项目初始化脚手架
    • vue-devtools 项目开发环境调试工具
    • vscode chrome git macbookpro
  4. 部署

应用功能

  • 商品页

    • 商品分类导航和商品列表的联动效果
    • 点击商品分类菜单展示对应商品列表信息
    • 添加/删除商品到购物车
    • 点击商品进入到详情页面
    • 商品添加到购物车动画效果
    • 页面滚动到对应商品类别时的标题吸顶效果
  • 评论页

    • 综合评论信息渲染
    • 切换评论筛选项按钮展示对应的信息
    • 选择展示是否有内容的评论
  • 商家页

    • 商家店铺信息展示
    • 收藏店铺
    • 商家实景图片具有bounce效果的滑动显示
  • 应用头部

    • 点击展示详情
    • 公告信息动态滚动显示
  • 购物车

    • 根据商品个数显示不同的状态
    • 购物车商品列表
    • 支付弹窗
    • 清空购物车
    • 增加/删除商品
  • 应用局部优化

bounce效果是指在应用中页面位置滚动到一个端点继续滑动时出现反弹的效果,常见场景是IOS系统应用滑动效果

功能难点

商品导航和内容的左右联动效果

效果演示

完整的组件代码点https://github.com/konglingwen94/vue-elm-sell/blob/master/src/views/goods/index.vue

image

思路

由于商品导航和内容是两个独立的滚动容器,当滚动到一个目标内容块时怎么才能激活它所关联的导航项呢?我们知道导航项列表和内容列表在排列顺序上是一致的,如果能计算出内容滚动位置处在对应区间块的索引,也就得到了导航列表应该激活的目标索引,然后就可以用Vue数据驱动视图的思想去实现这一切。

容器的左右联动效果是指容器滚动到目标内容时激活其关联的导航菜单项并滚动到可视区域。

逻辑实现

找到要激活的目标导航项索引的第一步需要把商品内容的各个类别块在容器内的纵坐标位置存储起来(给之后找到激活的目标索引提供比较对象),由于列表内容时动态渲染的,所以这里需要等所有数据已经渲染完成后才能操作,下面直接看代码演示吧!

template部分

<template>
    /* 这里只显示部分代码*/
     <ul class="foods-list">
          <li ref="foodsGroup" class="foods-group" v-for="(item,index) in data" :key="index">
            <dl class="foods-group-wrapper">
              <dt :class="{fixed:currentIndex===index}" class="foods-group-name">{{item.name}}</dt>
              <dd
                class="foods-group-item"
                v-for="(food ,key) in item.foods"
                :key="key"
              >
                 {{food.name}}
              </dd>
            </dl>
          </li>
        </ul>
</template>

script部分

 
export default {
    data(){
      return {
          currentIndex: 0,//导航项激活的索引
          currentFood: {},
          data:[],
          sectionHeight: [0],//第一个高度块坐标`y`值为`0`
          // 渲染完成后的值为 `[0,1281,1459,1612,2000,2270,2565,2952,3574,4436]`
      }  
    },
    created() {
        request
          .get("/goods")
          .then(response => {
            this.data = response;
          })
          .then(() => {
            setTimeout(() => {
              const sections = this.$refs.foodsGroup;
    
              sections.reduce((prevTotal, current) => {
                const sectionHeight = prevTotal + current.clientHeight;
                this.sectionHeight.push(sectionHeight);
    
                return sectionHeight;
              }, 0);
            });
        });
  }
}

有了各个商品块的y坐标,下一步就需要注册容器元素的滚动事件了,在回调函数里通过找到实时滚动位置disanceY处在sectionHeight数组中两个相邻元素之间的位置从而就得到了待激活导航索引currentIndex的值,具体代码实现如下

<template>
    <div>
        <!--导航菜单-->
         <scroll class="menu">
            <ul class="menu-list">
              <li
                @tap="selectMenu(index)"
                class="menu-item"
                :class="{selected:currentIndex===index}"
                v-for="(item,index) in data"
                :key="index"
              >
               <span>{{ item.name}}</span>
              </li>
            </ul>
          </scroll>
          
        <!--商品内容-->
        <scroll ref="foodsScroll" @scroll="onFoodScroll" class="foods">
        <!--这里省略商品内容模板的代码-->
        </scroll>
    
    </div>
</template>

export default {

    // 这里省略其他代码
    
    methods:{
        onFoodScroll({ x, y }) {
          const distanceY = Math.abs(Math.round(y));
          for (let index = 0; index < this.sectionHeight.length; index++) {
            if (
              distanceY >= this.sectionHeight[index] &&
              distanceY < this.sectionHeight[index + 1]
            ) {
              this.currentIndex = index;
            }
          }
        }
    }
}

完整的组件代码点https://github.com/konglingwen94/vue-elm-sell/blob/master/src/views/goods/index.vue。由于左右两侧的布局容器都是基于better-scroll实现的页面滚动,所以这里需要侦听better-scroll提供的scroll事件而不是浏览器原生的滚动事件。查看better-scrollscroll事件API这里

添加/删除 商品到购物车

效果截图

image

完整代码https://github.com/konglingwen94/vue-elm-sell/blob/master/src/components/food-picker/index.vue

思路

添加商品到购物车是一个多场景的功能,由于这里的购物车功能是一个多页面联动的效果,购物车商品数量的实时更改也需要同步到商品内容页和商品详情页。从功能映射到javascript语言数据结构层面的话,不难想到对象引用传递的特点可以作为实现此功能的底层架构思路,那就让我们去实现它吧。

实现

为了统计商品的数量。首先需要给每一个商品信息对象添加一个默认值为0count属性,添加后的对象长这样

{
  "count": 0, // 此变量用来存储添加到购物车的数量
  "name": "皮蛋瘦肉粥",
  "price": 10,
  "oldPrice": "",
  "description": "咸粥",
  "sellCount": 229,
  "rating": 100,
  "info": "一碗皮蛋瘦肉粥,总是我到粥店时的不二之选。香浓软滑,饱腹暖心,皮蛋的Q弹与瘦肉的滑嫩伴着粥香溢于满口,让人喝这样的一碗粥也觉得心满意足",
  "ratings": [
    {
      "username": "3******b",
      "rateTime": 1469261964000,
      "rateType": 1,
      "text": "",
      "avatar": "http://static.galileo.xiaojukeji.com/static/tms/default_header.png"
    }
  ],
  "icon": "http://fuss10.elemecdn.com/c/cd/c12745ed8a5171e13b427dbc39401jpeg.jpeg?imageView2/1/w/114/h/114",
  "image": "http://fuss10.elemecdn.com/c/cd/c12745ed8a5171e13b427dbc39401jpeg.jpeg?imageView2/1/w/750/h/750"
}

由于每一个商品项都有一个添加到购物车的数量选择器功能,这样我们直接给商品数量选择器组件设计一个名为foodInfo的对象类型props,这样在增加/减少商品数量的时候直接操作foodInfocount属性来实现同步数据的效果。

/components/food-picker.vue 组件代码

<template>
  <div class="food-picker" @click.stop>
    <div class="reduce-wrapper" @click="reduce">
      <i class="iconfont reduce"></i>
    </div>
    <div class="counter">{{foodInfo.count}}</div>
    <div class="add-wrapper" @click="add">
      <i class="iconfont add"></i>
    </div>
  </div>
</template>
<script>
export default {
  name: "food-picker",
  props: {
    foodInfo: {
      type: Object,
      default: () => ({})
    }
  },
  methods: {
    reduce() {
      if (parseInt(this.foodInfo.count) > 0) {
        this.foodInfo.count--;
      }
    },
    add() {
      this.foodInfo.count++;
    }
  }
};
</script>
<style lang="less" scoped>
.food-picker {
  min-width: 180px;
  max-width: 200px;

  display: flex;
  align-items: center;
  width: 100%;
  justify-content: space-between;
  .iconfont {
    color: #00a0dc;
    font-size: 38px;
  }
  .counter {
    // margin: 0 20px;
  }
}
</style>

下一个目标

基于目前已经实现的功能,整个应用的数据都是以json文件的格式存储在服务器,服务端并没有可以用来增删改查API接口可供使用。下一步我计划做出管理后台和服务端API用来管理前端页面的数据,使所有模块的数据都是可配置的,这样前端所渲染出来的数据也都是动态的,能够整合三端到一个项目也满足了当下Web全栈开发的场景需要。

总结

通过真实的开发这样一个复杂交互的应用,自己对Vue在实际业务场景中的使用和理解有深入了一步。深入理解了Vue数据驱动视图改变的思想,熟练的掌握了组件化开发项目的流程,同时也感受到所带来的便利,为自己接下来预备做的中大型项目建筑好了桥梁。

支持

如果本项目对您学习有帮助,请您动手点个starhttps://github.com/konglingwen94/vue-elm-sell。也希望您继续关注我的动态https://github.com/konglingwen94,有了您的支持我会有动力开源更多有趣的项目。

欢迎点赞和留言,谢谢!

本篇文章属于作者原创,转载参考请注明出处,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272