项目仓库地址:https://github.com/konglingwen94/vue-elm-selljavascript
项目线上地址: http://123.56.124.33:5000css
自从学习了Vue
后,能用Vue
解决的场景用例最终我都尽量的用Vue
去实现。单纯的用例需求并无完整的项目开发流程,从中能学到的东西也是有限的。在这以前除了使用Vue
作过vue-music的移动端音乐播放器项目和vue-bytedanceJob(重构某独角兽互联网公司官方招聘网站)以外,本身并无用Vue
涉猎web
端更复杂的业务场景。html
为了找一个项目练习,我去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">vue
前端java
vue
开发项目核心框架axios
HTTP请求模块lib-flexible
移动端屏幕适配方案better-scroll
仿IOS
效果的移动端滚动库normalize.css
第三方css
样式初始化模块es 6/7
下一代javascript
语法后端ios
express
搭建服务端应用核心框架开发git
vue-cli
项目初始化脚手架vue-devtools
项目开发环境调试工具vscode
chrome
git
macbookpro
部署github
[x] 商品页web
[x] 评论页
是否有内容的评论
[x] 商家页
bounce
效果的滑动显示[x] 应用头部
[x] 购物车
bounce效果
是指在应用中页面位置滚动到一个端点继续滑动时出现反弹的效果,常见场景是IOS
系统应用滑动效果
完整的组件代码点 https://github.com/konglingwen94/vue-elm-sell/blob/master/src/views/goods/index.vue。
因为商品导航和内容是两个独立的滚动容器,当滚动到一个目标内容块时怎么才能激活它所关联的导航项呢?咱们知道导航项列表和内容列表在排列顺序上是一致的,若是能计算出内容滚动位置处在对应区间块的索引,也就获得了导航列表应该激活的目标索引,而后就能够用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-scroll
的scroll
事件API
点 这里。
完整代码 https://github.com/konglingwen94/vue-elm-sell/blob/master/src/components/food-picker/index.vue
添加商品到购物车是一个多场景的功能,因为这里的购物车功能是一个多页面联动的效果,购物车商品数量的实时更改也须要同步到商品内容页和商品详情页。从功能映射到javascript
语言数据结构层面的话,不难想到对象引用传递的特色能够做为实现此功能的底层架构思路,那就让咱们去实现它吧。
为了统计商品的数量。首先须要给每个商品信息对象添加一个默认值为0
的count
属性,添加后的对象长这样
{ "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
,这样在增长/减小商品数量的时候直接操做foodInfo
的count
属性来实现同步数据的效果。
<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
的数据驱动视图改变的思想,熟练的掌握了组件化开发项目的流程,同时也感觉到所带来的便利,为本身接下来预备作的中大型项目建筑好了桥梁。
若是本项目对您学习有帮助,请您动手点个star
https://github.com/konglingwen94/vue-elm-sell。也但愿您继续关注个人动态https://github.com/konglingwen94,有了您的支持我会有动力开源更多有趣的项目。
欢迎点赞和留言,谢谢!