在学习了 vue 之后,决定做一个小练习,仿写了一个有关购物商城的小项目。下面就对项目做一个简单的介绍。

项目源码: github

项目的目录结构

-assets                            与项目有关的静态资源,包括 css,以及一些 images

-common                            公共的工具类方法

-components						   公共组件
-common 与项目耦合度较低的组件
-content 与项目耦合度较高的组件 -network 网络请求相关 -router 路由相关 -store vuex 相关 -views 主要展示的页面
-home 首页
-childcomps
-detail 详情页
-childcomps
-cart 购物车页面
-childcomps
-profile 个人信息页面
-childcomps

划分好目录结构后就可以对每个模块进行独立开发

主要实现的功能

首页
  1. 顶部轮播图的展示
  2. 中间选项卡点击可进行商品的切换以及吸顶效果
  3. 点击商品进入商品详情页
  4. 上拉展示更多商品
  5. 点击按钮回到顶部
详情页
  1. 根据首页中用户的点击进行每个商品的独立展示
  2. 点击商品信息对应的标题跳转到对应的内容区域
  3. 滑动页面过程中与顶部商品信息的标题联动效果
  4. 点击按钮回到顶部
  5. 加入购物车跳出弹窗
购物车页面
  1. 展示各种商品的数量
  2. 底部工具栏展示选中商品的总价格
  3. 全选与取消全选
  4. 结算时的两种弹窗
我的页面
  1. 个人信息的展示
  2. 点击我的购物车进入到购物车页面

主要思路介绍

底部 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 仿写的一个购物商城的更多相关文章

  1. 用vue.js写的一个瀑布流的组件

    用vue.js写的一个瀑布流的组件:https://segmentfault.com/a/1190000010741319 https://www.jianshu.com/p/db3cadc03402

  2. First Project -用函数写的ATM+购物商城程序

    作业需求:模拟实现一个ATM + 购物商城程序 额度15000或自定义 实现购物商城,买东西加入 购物车,调用信用卡接口结账 可以提现,手续费5% 每月22号出账单,每月10号为还款日,过期未还,按欠 ...

  3. Vue项目——Supermall移动端购物商城

    一.项目描述 基于Vue全家桶构建的移动端购物商城APP.页面一共分为:首页.详情页.分类页.购物车页面.登录页面和个人信息页面. 二.使用技术 使用Vue CLI3快速搭建Vue开发环境以及对应的w ...

  4. vue自己写了一个div菜单,点击按钮展开,点击其他地方关闭这个div菜单

    需求是通过点击body页面,在其他地方就关闭这个<div>菜单,给这个div一个id:problemList,但是点击我打开的按钮,不关闭. created () { document.o ...

  5. vue 自己写的一个日历

    样式: //quanbu全部代码 <template> <div class="priceListContent clearfix"> <!-- 顶部 ...

  6. vue elementui 写的一个后台管理页面模板

    https://github.com/PanJiaChen/vue-element-admin

  7. vue 仿写微信公众号自定义菜单

    先看效果图 代码参考 <template> <div> <!-- 公众号设置 --> <el-col :span="24" style=& ...

  8. vue仿写taobao

    1.less () cnpm install less less-loader --save ()在webpack.base.conf.js里 { test:/\.less$/, loader:'st ...

  9. vue 仿今日头条

    vue 仿今日头条 为了增加移动端项目的经验,近一周通过 vue 仿写今日头条,以下就项目实现过程中遇到的问题以及解决方法给出总结,有什么不正确的地方,恳请大家批评指正^ _ ^!,代码仓库地址为 g ...

随机推荐

  1. 「CSP-S 2020」动物园

    description luogu loj(暂无数据) solution 这道题作为T2,对选手们考试开始后先通看一遍所有题目的好习惯,以及判断究竟谁才是真正的签到题的重要能力进行了较好的锻炼, 特别 ...

  2. ⭐NES.css推荐⭐

    今天发现一个有意思的CSS框架,叫NES.css 官网地址:https://nostalgic-css.github.io/NES.css/ gitHub地址:https://github.com/n ...

  3. J2EE基本概念

    XO POJO:Plain Ordinary Java Object,简单java对象 PO:Persistant Object,持久层对象(对应数据库中一条记录) BO:Business Objec ...

  4. 配置Nginx 扩展实现图片剪裁

    在此之前需要安装ngx_http_image_filter_module,如果是采用的Docker的话可以看看我历史文章. 然后修改配置文件,增加几个location模块,配置如下,仅供参考 serv ...

  5. jvm系列(二)jvm垃圾收集器与内存分配策略

    众所周知,在java语言中,内存分配和回收是由jvm自动管理的.因此内存的分配和回收也是jvm三大功能之一.垃圾收集器(GC)需要完成三件事情: 哪些内存需要回收? 什么时候进行回收? 如何回收? 本 ...

  6. 关于你天天见到的JDK、JRE和JVM

    什么是JDK.JRE.JVM? 大家都知道电脑的操作系统是由汇编和C语言写出,因此操作系统无法直接识别其他语言.这时我们就需要为我们写的Java程序配备一名翻译官 ----- 编译环境,将Java程序 ...

  7. 七牛云实现前端js上传实现办法

    1.七牛云上传前台页面 1.1 安装相关包 npm install --save jquery@1.12.1 # 安装jquery 1.2 index.html 引入qiniu.min.js < ...

  8. 阿里云ECS服务器连接MongoDB

    第一次接触MongoDB,第一次部署.将一些步骤整理出来,希望以后会用到,也希望能帮组到有这方面需求的小伙伴. 设备说明: 服务器为阿里云ECS服务器,网络为专有网络VPC,Mango为买的阿里云Ma ...

  9. MoviePy v2.0.0.dev1尚不成熟,不建议大家使用

    ☞ ░ 前往老猿Python博文目录 ░ 在<重要消息:MoviePy v2.0.0.dev1预发布版本已经可以下载安装使用>之后老猿就安装了MoviePy v2.0.0.dev1这个版本 ...

  10. Python调用云服务器AWVS13API接口批量扫描(指哪打哪)

    最近因为实习的原因,为了减少一部分的工作量,在阿里云服务器上搭建了AWVS扫描器 方便摸鱼 但是发现AWVS貌似没有批量添加的方法,作者只好把整理的URL.txt捏了又捏 手动输入是不可能手动输入的, ...