使用 vue 仿写的一个购物商城
在学习了 vue 之后,决定做一个小练习,仿写了一个有关购物商城的小项目。下面就对项目做一个简单的介绍。
项目源码: github
项目的目录结构
-assets 与项目有关的静态资源,包括 css,以及一些 images
-common 公共的工具类方法
-components 公共组件
-common 与项目耦合度较低的组件
-content 与项目耦合度较高的组件
-network 网络请求相关
-router 路由相关
-store vuex 相关
-views 主要展示的页面
-home 首页
-childcomps
-detail 详情页
-childcomps
-cart 购物车页面
-childcomps
-profile 个人信息页面
-childcomps
划分好目录结构后就可以对每个模块进行独立开发
主要实现的功能
首页
- 顶部轮播图的展示
- 中间选项卡点击可进行商品的切换以及吸顶效果
- 点击商品进入商品详情页
- 上拉展示更多商品
- 点击按钮回到顶部
详情页
- 根据首页中用户的点击进行每个商品的独立展示
- 点击商品信息对应的标题跳转到对应的内容区域
- 滑动页面过程中与顶部商品信息的标题联动效果
- 点击按钮回到顶部
- 加入购物车跳出弹窗
购物车页面
- 展示各种商品的数量
- 底部工具栏展示选中商品的总价格
- 全选与取消全选
- 结算时的两种弹窗
我的页面
- 个人信息的展示
- 点击我的购物车进入到购物车页面
主要思路介绍
底部 tabbar 的封装
移动端中比较常见的一种导航栏,需要根据用户的点击进行跳转到相应的页面,所以直接封装一个公共的组件。布局一般是图片加文字,图片一般有两种,需要根据当前是否处于活跃状态的路由和用户的点击来判断展示哪张。内部预留插槽,用户根据实际需求选择底部导航的个数。每个导航为其配置路由。
顶部导航 navbar 的封装
顶部的导航基本会出现在每个页面中,所以也直接封装为一个公共组件,布局通常是左中右三栏布局,所以组件内部预留三个插槽,采用 flex 布局。
数据请求
对 axios 再封装为一个 request.js 文件,让所有的页面都基于这个文件发送请求,这样做可以避免重复操作,提高了项目后期的可维护性。
在这个项目中将每个页面需要发送请求的部分又进行了一层封装,即每个页面都有独立的与之相对应的发送请求的文件,首页发送请求只需要面向 home.js,详情页发送请求面向 detail.js。
滚动组件 Scroll
- 引入 better-scroll 来替换掉页面的原生滚动,Scroll.vue 是对 better-scroll 的封装,为了提高组件的可复用性,可以让 probeType, pullUpLoad,等值从外部传入(项目中 click 的值为 true)。
- Scroll.vue 内代码的封装:
创建一个 Better-Scroll 对象,传入 DOM 和 参数( probeType,click,pullUpLoad等),
监听 scroll 事件,该事件会返回一个 position,
监听 pullUpLoad 事件,表示该事件触发的时候是否上拉加载更多,
封装一个滚动的方法 this.scroll.scrollTo(x,y,time),
封装一个刷新的方法 this.scroll.refresh(),会重新计算可滚动区域的高度,
封装完成上拉加载更多的方法 this.scroll.finishPullUp
全局插件 Toast
- 项目中有两个地方用到了弹窗组件,一个是点击加入购物车时,一个是点击结算时,如果想要使用弹窗组件那么每次都需要导入还要加一些代码,这样做有点麻烦,如果有多个地方都需要使用这个弹窗组件那么此时需要做的重复工作量太大,所以采用 Vue.use() 方法全局注册了一个插件。
- 需要在 main.js 中 import, 然后 Vue.use() 进行注册,Vue.use() 需要传入至少一个参数,该参数只能是 Object 或 Function ,如果是 Object 那么这个对象需要定义一个 install 方法,如果是 Funcion 那么这个函数就被当做 install 方法,在 Vue.use() 执行时 install 方法会默认执行,install 方法调用时会将 Vue 作为参数传入。Vue.use() 除了传入第一个参数外,也可继续传入一个选项对象。这里还要注意的是 Vue.use() 必须要在 new Vue() 之前被调用,这是因为在安装组件时,组件给 Vue 添加全局功能,所以必须写在 new Vue() 之前,否则创建的 Vue 实例就无法获取插件添加的全局功能。
- 所以我们在项目中使用了 Vue.use() 注册了全局插件,在任何地方想使用只需要 this.$名称 即可。
首页
- 数据处理
banners 数组保存轮播图的数据,recommends 数组保存推荐的数据。
还有其它的即需要展示的每一条商品的数据通过 goods 来保存。
goods: {
'pop': {page: 0, list: [ ] },
'new': {page: 0, list: [ ] },
'sell': {page: 0, list: [ ] }
} - 最上面的轮播图,可以根据图片数量自动进行轮播,也可手动滑动图片,是一个独立的子组件。
轮播图下面的推荐部分是一个独立的子组件。 - 对 goods 中的数据进行展示,封装 GoodsList 组件,而每一个商品又是一个独立的小组件 GoodsListItem.vue。
- 点击选项卡进行数据的切换,监听选项卡的点击事件,通过 $emit() 发出事件(携带 index),在父组件中监听,根据 index 对应的数据类型(swicth(index)),父组件再通过 props 将 currentType 传给 GoodsList 进行展示。
- 滑动过程中选项卡的吸顶效果,原理是复制一个选项卡(tabControl)在顶部,默认隐藏,通过 v-show 来决定何时出现,用一个变量保存原选项卡 (contentControl) 的 offsetTop,在页面滚动的过程中不断获取滚动的距离,当滚动的距离大于原选项卡 (contentControl) 的 offsetTop时,就显示 tabControl。
- 上拉加载更多,Scroll 组件中的 pullingUp 一触发就会发送一个事件,父组件中进行监听然后当事件触发的时候去请求数据即可。
- 回到顶部组件 BackTop,默认不显示,当页面滚动到设定的距离时显示。点击回到顶部需要监听组件的点击(@click.native),然后触发函数 scrollTo(x,y,time) 返回顶部。
详情页
- 当用户点击商品时进入到详情页,首先要监听每个 GoodsListItem 的点击,点击时进行路由的跳转,并且携带商品的 id,根据 id 请求数据,再将数据进行展示。
- 底部功能栏的封装也是一个独立的子组件,当点击加入购物车时,将点击事件发送给父组件 detail.vue,父组件中根据商品 id 获取购物车(cart.vue,与 detail.vue 同级)中需要保存的商品数据,提交到 vuex 进行全局状态管理,购物车组件中通过 $store.state 拿到数据进行渲染,加入购物车成功后弹出弹窗提示。
- 将商品添加到购物车其实有两种情况,一种是 vuex 中还没有这个商品的添加,另一种是 vuex 中已经有了这个商品,所以为了使 mutation 里定义的函数职能单一,这里将点击添加购物车这个操作提交到 action 中管理,再由 action 提交到 mutation 来使 vuex 管理的状态进行更新。
- 点击联动效果,商品详情页面中共有四个部分组成:商品,参数,评论,推荐,每个部分都是一个独立的子组件,当数据请求完成时去获得每个组件的 offsetTop ($refs.组件绑定的 ref 值.$el.offsetTop) 保存在一个数组中,监听商品详情栏中的点击,根据 index 触发 scrollTo() 进行跳转
- 滑动联动效果,实时监听滚动位置,通过和上面数组中的值进行比较,在 0 - 参数之间的偏移这个高度时,currentIndex 为 0,在参数的偏移高度 - 评论的偏移高度时 currentIndex 为1,以此类推,这样就可以实现在滑动的过程中与顶部标题信息的联动。
- 回到顶部,与首页中的一样。
购物车页
- 渲染在详情页添加到购物车的数据,从 vuex 中拿到数据,封装 CartList 组件,而每一条商品又是一个独立的子组件 CartListItem。
- 底部工具栏的封装,主要包括全选按钮,选中商品的总价格,去结算时的弹窗。
- 全选按钮的实现,某商品第一次添加到购物车时,需要给这件商品的数据里定义一个是否选中 (checked)的属性,默认为 true。为了管理选中的状态,只能由 mucation 对状态进行修改,商品最终是否展示也由 mucation 最终修改的状态为准,监听全选按钮的点击,根据商品数组中每个商品的 checked 属性进行逻辑判断即可。
- 点击去结算组件时,先判断当前购物车的商品列表中是否有数据,如果没有弹出提示信息,让用户选中商品。
我的页面
- 展示用户的一些个人信息,点击“我的购物车”跳转到购物车页面,this.$router.push('/cart')
项目中遇到的一些问题
由于引入 better-scroll 带来的问题
- 引入 better-scroll 之后,带来了一些问题,比如页面会划不动、scrollTo 跳不准、等等,导致这些问题的原因,多数情况下是因为请求的数据没回来,或者拿到数据了但是页面还未加载完,而 better-scroll 在这之前就需要计算可滚动区域的高度,但由于图片还没加载完所以这个时候计算的高度并不是最终的高度,所以导致滚动出现了问题,怎么解决呢?
- 那么这个时候可以在某些资源加载完成是时来个 scroll.refresh(),让 better-scroll 重新计算可滚动区域的高度,比如项目中我们可以监听图片加载事件,当有图片加载完成时就触发 scroll.refresh()。但这样的话只要一有图片加载完成就会 scroll.refresh() ,使得 refresh() 的调用太频繁了,在此我们可以进行一个防抖操作来减少 scroll.refresh() 的调用。
让首页的内容保持原来的位置
- 项目中在浏览到首页某个位置时,用户点击了商品然后进入了商品详情页或者进入了购物车等其它页面,当再次回到首页时发现不是之前浏览的位置。
- 可以在首页的 deactivated() 中记录下离开时的位置信息,activated() 时再将位置设置为原来保存的位置。
选项卡 tabControl 的吸顶效果
- 其实实现这个效果的基本思路就是首先获取 tabControl 的 offsetTop ,怎么获取?this.$refs.tabControl.$el.offsetTop ,然后实时监测选项卡的滚动位置,当滚动位置达到 offsetTop 时将选项卡改为固定定位即可,这里需要注意的是在 mounted() 中获取的值不一定是正确的,因为可能顶部的轮播图或者推荐部分的图片没有加载完,那么如何获取正确的值?监听轮播图的图片加载情况,加载完毕发出事件,然后在首页中再去获取 offsetTop。
- 按照这样的思路实现以后,发现并没有达到预期的效果,而是出现了下面的商品部分会突然上移,并没有实现停留的效果,所以换了另一种方案,就是上文提到的先在顶部复制一份占位选项卡,默认不显示,然后根据滚动位置和 offsetTop 来决定什么时候显示,这样就实现了吸顶的效果。
使用 vue 仿写的一个购物商城的更多相关文章
- 用vue.js写的一个瀑布流的组件
用vue.js写的一个瀑布流的组件:https://segmentfault.com/a/1190000010741319 https://www.jianshu.com/p/db3cadc03402
- First Project -用函数写的ATM+购物商城程序
作业需求:模拟实现一个ATM + 购物商城程序 额度15000或自定义 实现购物商城,买东西加入 购物车,调用信用卡接口结账 可以提现,手续费5% 每月22号出账单,每月10号为还款日,过期未还,按欠 ...
- Vue项目——Supermall移动端购物商城
一.项目描述 基于Vue全家桶构建的移动端购物商城APP.页面一共分为:首页.详情页.分类页.购物车页面.登录页面和个人信息页面. 二.使用技术 使用Vue CLI3快速搭建Vue开发环境以及对应的w ...
- vue自己写了一个div菜单,点击按钮展开,点击其他地方关闭这个div菜单
需求是通过点击body页面,在其他地方就关闭这个<div>菜单,给这个div一个id:problemList,但是点击我打开的按钮,不关闭. created () { document.o ...
- vue 自己写的一个日历
样式: //quanbu全部代码 <template> <div class="priceListContent clearfix"> <!-- 顶部 ...
- vue elementui 写的一个后台管理页面模板
https://github.com/PanJiaChen/vue-element-admin
- vue 仿写微信公众号自定义菜单
先看效果图 代码参考 <template> <div> <!-- 公众号设置 --> <el-col :span="24" style=& ...
- vue仿写taobao
1.less () cnpm install less less-loader --save ()在webpack.base.conf.js里 { test:/\.less$/, loader:'st ...
- vue 仿今日头条
vue 仿今日头条 为了增加移动端项目的经验,近一周通过 vue 仿写今日头条,以下就项目实现过程中遇到的问题以及解决方法给出总结,有什么不正确的地方,恳请大家批评指正^ _ ^!,代码仓库地址为 g ...
随机推荐
- 如何使用iMindMap制作更专业的时间计划
时间计划无论是在日常生活中,还是在工作中,都显得极为重要.小到每周的购物时间规划,大到大型项目的时间管理,时间计划都会如影随形.虽然时间计划很重要,但很多人都会忽视这种重要性,可能只会在台本日历上作一 ...
- Jmeter(一)发送http请求
Jmeter中发请求的步骤 1.添加线程组 2.添加http消息头管理器 3.添加http请求 一.线程组: 1.添加路径: 2.字段解释 ①线程数(Number of Threads): : 设置发 ...
- 适合 Java 新手的开源项目集合——在 GitHub 学编程
作者:HelloGitHub--老荀 当今互联网份额最大的编程语言是哪一个?是 Java!这两年一直有听说 Java 要不行了.在走下坡路了.没错,Java 的确在走下坡路,未来的事情的确不好说,但是 ...
- Linux三剑客grep、awk、sed
何为Linux三剑客? 第一个剑客是 grep,grep 会根据正则表达式查找相关内容并打印对应的数据. 第二个剑客是 awk,awk 的名字来源于三个作者的名字简称,它可以根据定位到的数据行处理其中 ...
- AcWing 298. 围栏 (POJ1821)
标签(空格分隔): dp 单调队列优化 题目描述 有N块木板从左到右排成一行,有M个工匠对这些木板进行粉刷,每块木板至多被粉刷一次. 第 i 个木匠要么不粉刷,要么粉刷包含木板 \(S_i\) 的,长 ...
- CF1156D 0-1-Tree
路径考虑顺序. 显然合法的路径只有以下两种: 一段 \(0\) 加一段 \(1\) 或一段 \(1\) 加一段 \(0\). 全 \(0\) 或全 \(1\). 用并查集将边权为 \(0\) 和 \( ...
- 深度学习论文翻译解析(十五):Densely Connected Convolutional Networks
论文标题:Densely Connected Convolutional Networks 论文作者:Gao Huang Zhuang Liu Laurens van der Maaten Kili ...
- java17(面向对象)
1.面向过程:所有事情都是按顺序一件件做,未知主体 买菜,做饭,吃饭,洗碗 面向对象:将功能封装到对象之中,让对象去实现功能 去饭馆,告诉服务员要吃啥,然后等着端上来. 面向对象的目的: 复杂的东西简 ...
- 浅尝 Elastic Stack (四) Logstash + Beats 读取 Spring Boot 日志
一.Spring Boot 日志配置 采用 Spring Boot 默认的 Logback: <?xml version="1.0" encoding="UTF-8 ...
- PyQt(Python+Qt)学习随笔:Qt Designer中QAbstractButton派生按钮部件的text属性
text属性保存按钮上显示的文字,如果按钮未设置文字则为空字符串.如果文字中包含有与符号('&'),则该按钮会自动设置一个快捷键,快捷键就是'&'后第一个字符,显示时会在该字符下加下划 ...