本项目是基于 uni-app 技术栈开发的,能够运行在多端 (微信小程序、h5、ios/Android) 的移动端商城项目。
码值 | 说明 |
---|
http 状态码 500 | 服务器端异常 |
http 状态码 404 | 服务器端异常 |
http 状态码 401 | 授权信息不正确 |
测试环境,登录短信验证码统一为:246810 (不再提供真实的短信验证服务)
接口文档地址
Vant2 官网地址
本项目使用 ESLint+Standard config
的代码规范标准
## 一级路由配置
将每个基础的一级页面以文件夹形式存放于 views 包中。包括:
具体项目结构如下:
路由页如下:
二级路由配置
实现底部导航栏 Tabbar
配置二级路由之前需要完成底部 Tabbar 栏的设计,可以结合 vant 官网文档进行设计。vant2 官网
实现二级路由配置
在组件中的 vant2 的 Tabbar 要实现路由模式如下:
于是有:
最后优化路由逻辑,将默认匹配的页面重定向到 home
登录静态页
新建 styles/common.less
重置默认样式(可以对一些想要多组件生效的样式进行重新调整)
导入于 main.js 中:import '@/styles/common.less'
接着准备一些素材图片于 assets
配置头部 NavBar
$router.go(-1)
的作用是返回上一页。
接着将左边的返回符号 left-arrow 颜色样式改为灰色 #333,于是找到 common.less 进行编写(两个类名增加权重)
这样的好处是,一旦配置好了将来其他页面使用到导航栏此处的返回颜色都是我们此时配好的颜色。这就是通用样式的覆盖。
完善主体
接着编写主体的静态结构:
页面效果如下:
图形验证码暂时写死,短信验证码暂时不作处理
完善登录逻辑
登录页面一共有三个请求需要发送,图形验证码、短信验证码和登录请求。我们使用 axios 来请求后端接口,一般都会对 axios 进行一些配置(配置基础地址,请求响应拦截器等),于是封装 axios 为一个 request 模块,便于维护。以后使用 axios 都是创建实例去请求,这样多个实例相互独立,互不干扰。
新建 request.js 于 utils 包下,创建一个 axios 实例(cv 中文文档中的实例和拦截器进行改造):
响应器中,response.data 原本为 response,这里改为上文是因为 axios 默认会对响应多包装一层 data,所以这里需要取出 data
接着回到登录页面 index.vue 上,导入 request 模块,异步检查一下:
调试返回如下
解析 base64 并完善点击刷新图片验证码
由于提交的图形验证码必须带 key 才能让后端进行唯一校验,所以在 data 中提供:
接着进行响应结果的解构,存储好数据以便后续提交后端进行校验
完善 template 相关代码:
封装登录请求到 api 包下,新建 login.js
使用时按需导入。于是到登录页 index.js 中改 import request from '@/utils/request'
为 import { getPicCode } from '@/api/login'
并修改调用语句:
上述语句不会因为重名冲突,我们需要知道的是,如果是引入的 getPicCode 前面是不带 this 的,而自身的 getPicCode 是需要在前面加上 this. 的
Toast 轻提示
校验手机号是否输入、是否输入格式正确等并给予提示。
使用 vant 库中的 Toast 轻提示,完成提示显示。需要注意的是他的两种调用方式:
- Toast (' 提示内容 ');
- this.toast (' 提示文案 '); 引入 Toast 组件后,会自动在 Vue 的 prototype 上挂载 `toast` 方法,便于在组件内调用。
这两种调用,第一种是任意地方都可以调用显示出提示内容,第二种是只有组件内才可以显示提示内容。Toast 默认采用单例模式,即同一时间只会存在一个 Toast,如果需要在同一时间弹出多个 Toast,可以参考下面的示例:
短信验证倒计时
点击获取验证码之后要开始倒计时,一分钟只能发送一次。给获取短信验证码的按钮注册点击事件并改写显示文字
提供短信验证码获取函数 getCode
并考虑到性能问题,于是增加在用户离开登录页面之后清除定时器的功能,实现在 destroy 生命函数
手机号和图形验证码类型校验
由于前端无法校验具体,只能校验数据类型和长度。所以新增两个正则数据和绑定用户输入的手机号:
增加校验的函数 validFn
并在短信验证码获取函数 getCode 中加入一行判断
封装发送短信验证码请求
转到 login.js 中进行封装(结合接口文档)
然后在短信验证码获取函数 getCode 中加入调用代码
实现登录功能 - 封装登录接口
封装登录请求
绑定登录按钮事件为 login,绑定用户输入的短信验证码为 smsCode(默认短信为 246810)
后面失败的多种情况可以通过响应拦截器进行处理,在这里暂时只考虑成功的情况
响应处理器统一处理错误提示
对 utils 包下的 request 拦截器进行修改
登录权证信息存储
使用 vuex 构建 user 模块存储登录权证(token 和 userID)。好处是易获取、响应式,分模块便于管理维护。
在 store 包下新建 modules 包,然后在 modules 包下新建 user.js
然后在 vuex 中进行挂载
并在登录页面将要实现跳转到首页的前一刻完成用户权证信息的存储
vuex 持久化处理
vuex 刷新就会丢失信息,于是引入持久化存储。我们将获取、设置和移除信息的操作封装为 storage 模块。
在 utils 包下新建一个 storage.js
然后到 vuex 的 user 子模块中进行导入使用即可
添加请求 Loading 效果
需求分析:有时候因为网络原因,一次请求的结果可能需要一段时间后才能回来。此时,需要给用户添加 loading 提示。
添加 loading 提示的好处:
- 节流处理:防止用户在一次请求还没回来之前,多次进行点击,发送无效请求
- 友好提示:告知用户,目前是在加载中,请耐心等待,用户体验会更好
加在哪呢?可以统一加在拦截器中,这样一来后面也可以复用:
- 请求拦截器中,每次请求,打开 loading
- 响应拦截器中,每次响应,关闭 loading
转到 utils 包下的 request 拦截器,在请求的时候显示 Toast 提示,并设置背景不可点击且使其不会定时消失,只能由我们清除。
在响应拦截器添加清除提示的代码
全局路由前置守卫
Vue-router 官网地址
在 router 中编写:
首页
完成首页静态结构
要用到的 vant 组件有
- search(搜索框)
- swipe & swipe-item (轮播图)
- grid & grid-item (宫格)
grid 宫格主要是使用自定义列数的:
所以在 utils 包下的 vant2-ui.js 中进行添加:
静态结构和样式 layout/home.vue
其中将商品项封装成组件 GoodsItem.vue 在 components 包下:
首页 - 动态渲染
根据接口文档封装请求首页数据模块于 api 包下,新建 home.js:
接着转到 layout 的 home.vue 处理动态渲染:
改造 template 中轮播图和导航部分:
接着改造商品组:
父传子将 item 整个对象传给 GoodsItem 进行接收:
然后改造 template
注意将商品 id 携带进行跳转时要动态取值使用反引号。
搜索
页面设计:
搜索页静态结构
view/search/index.vue
(需要导入 vant 组件 Icon)
历史记录管理
目标:构建搜索页的静态布局,完成历史记录的管理
需求分析:
- 搜索历史基本渲染
- 点击搜索 (添加历史)
添加历史说明:
点击搜索按钮或底下历史记录,都能进行搜索
- 若之前没有相同搜索关键字,则直接追加到最前面
- 若之前已有相同搜索关键字,将该原有关键字移除,再追加(相当于置顶)
持久化存储代码:
搜索列表页
静态布局
渲染
封装请求获取商品列表 api/product.js
基于搜索词进行渲染
基于分类页面的分类 id 进行渲染
封装请求分类页数据 api/category.js
完成分类页静态结构
修改搜索页列表的查询参数
商品详情页
静态结构
封装获取商品详情的请求模块 api/goodsDetail.js
获取的一个示例:
于是进行动态渲染
商品说明动态渲染
改造 template 中对图片的渲染:
script 部分如下:
修改商品说明进行动态渲染:
到这里还没有完成评论区的动态渲染
商品评论区渲染
封装请求获取商品评价详情的模块 api/goodsDetail.js
请求解构:
完成商品评价渲染(默认只展示三条评价)
在生命周期钩子 created 中:
data 提供:
现在剩下商品说明的图片未渲染、商品评论区未渲染
商品说明的图片渲染
接口返回的数据中 content 就是商品详细说明的图:
于是再于 data 中提供两个数据用于处理:
再到 created 中编写:
接下来就剩下完全的评价区渲染了。
商品评价区
这是一个新的页面所以直接新建了。views/productdetail/evaluation.vue
并转到 router 包下修改路由:
加入购物车 / 购买
该功能使用到弹层组件,可以在 vant 组件库中找到 ActionSheet
页面代码如下:
样式如下:
data 中提供两个数据:
methods 中提供两个方法:
接下来还需要将弹层中的数量展示封装成数字框组件。
数字框组件封装
分析:组件名 CountBox
- 静态结构,左中右三部分
- 数字框的数字,应该是外部传递进来的 (父传子)
- 点击
+-
号,可以修改数字(子传父) - 使用
v-model
实现封装(:value
和 @input
的简写) - 数字不能减到小于 1
封装组件 components/CountBox.vue
使用组件
加入购物车 - 判断登录状态
需要使用到 vant 的 Dialog 组件
给弹层的加入购物车按钮绑定点击事件
添加 token 鉴权判断,跳转携带回跳地址
登录后如果有回跳地址则 replace 地回跳回去:
this.$router.replace
和 this.$router.push
的区别
假设有 A、B、C 三个页面。
我从 A 点击进入 B 页面,然后在 B 页面需要进行跳转到 C 页面,此时我触发的是 this.$router.replace
进行跳转,那么跳转到 C 之后 C 就会将 B 的访问记录完全覆盖。如果我在 C 页面的操作结束之后,进行返回上一页的操作,就会直接返回到 A 页面而不是 B 页面。
相对的,this.$router.push
则会将记录保持,你的每次访问都是一个累加的过程,不会清除。
如果把页面比作真实的纸张,跳转的过程就像把我们手里的一张纸覆盖到桌子上的另一张纸上面一样,桌子上纸张堆的是已经访问过的页面,桌子上的纸张堆中最上面那一张就是我们现在正在访问的页面,而手里的是将要访问的页面。但覆盖的方式上述两种各有不同:replace
会在覆盖桌子上的纸张之前将桌子上最上面的那一张丢到垃圾桶,然后再将手中的纸张覆盖上去;push
则是简单的将纸张放在桌子上的纸张上面,不进行其他任何操作,最终你的访问记录都在桌子上。
加入购物车 - 封装接口进行请求
接口如下:
封装加入购物车的请求接口模块 api/cart.js
由于需要携带请求头且携带的是鉴权信息,每次请求手动去写比较麻烦,所以直接在请求拦截器中携带 utils/request.js
修改购物车渲染代码进行购物车内数量角标展示 views/productdetail/index.vue
导入并将商品规格赋值给 data 数据
提供 data 数据来接收必要信息
编写请求代码
购物车页面
需求分析:
- 基本静态结构 (快速实现)
- 构建 vuexcart 模块,获取数据存储
- 基于数据居动态渲染购物车列表
- 封装 getters 实现动态统计
- 全选反选功能
- 数字框修改数量功能
- 编辑切换状态,删除功能
- 空购物车处理
静态页面
修改 layout/cart.vue
使用到了 vant 的 Checkbox, CheckboxGroup 组件
静态页面如下:
构建 vuex cart 模块
新建子模块 cart:@/store/modules/cart
到 vuex 中挂载:
购物车页面中使用 created 调用 actions 异步
动态渲染
接口地址:/cart/list
使用辅助函数映射数组:
将 template 中对应的地方进行改造:
但未完成其他功能按钮等事件。
封装 getters 实现动态统计
合计商品数量使用到 getters
reduce 函数的 20 个高级用法
最终完整结果:
修改购物车页面的相关 template
全选反选
提供 mutations 便于修改 state
给全选和商品选择框注册点击事件:
辅助函数导入 isAllChecked
数字框修改数量功能
提供更新购物车的接口模块:api/cart.js
在 vuex 的 cart 子模块导入使用
cart 子模块增加修改商品数量的 mutations 方法
cart 子模块增加同步购物车到后台的 actions 方法
给数量框组件添加绑定事件
给结算按钮添加绑定事件
methods 中提供相对应的方法
真正的结算待做。
编辑切换状态,删除功能
点击编辑按钮,结算按钮变为删除按钮。
给购物车页面提供一个数据用于标识是否处于编辑状态
并监视其状态
相对应的对 template 中结算按钮和编辑按钮进行修改
删除功能待做。
删除商品
封装接口模块 api/cart.js
vuex 子模块 Cart 提供异步方法
接着修复了一些小 bug:商品详情页初始不显示购物车数量、添加到购物车之后数量不会自动更新、购物车页面编辑状态退出后按钮未恢复到结算按钮。下面是修复后的 store/modules/cart.js
和 views/productdetail.vue
商品详情页增加:
空购物车处理
将除标题以外的盒子用大盒子包裹起来:
并提供计算属性
并引入对应 Empty 组件的 css 样式
地址管理
地址选择里面涉及到一个地区选择器,可以自行定义数据也可以使用官方的数据。可以参考:Vant 使用 Vant Area Data
配置
封装 api 接口:api/address.js
Vuex 子模块:store/modules/address.js
Vuex 子模块 addressMap 处理引入的 vant 官方地区数据:store/modules/addressMap.js
配置路由并新增守卫规则:router/index.js
地址列表
地址列表代码:views/address/index.vue
地址编辑
地址编辑可以是新建也可以是更新操作:views/address/edit.vue
导入 vant 组件略过。
订单结算台
静态结构
渲染
订单结算
购物车携带参数:
文件上传 ——unfinished
图片上传
接口要求:POST /upload/image
Header 参数:
- Access-Token (String) 示例:
1741f74aed758a688515f72572dc8e37
- platform (String) 示例值:H5
Body 参数:multipart/form-data
上传图片通常涉及到裁剪操作,于是使用 vue-cropper 插件辅助完成。
安装 vue-cropper
在组件中使用 vue-cropper
创建上传的组件,允许用户选择图片、裁剪图片并实时预览裁剪效果,然后上传裁剪后的图片。
打包优化
优化访问路径
打包命令:yarn build
或者 npm run build
打包如果没有配置 vue.config.js 的 publicPath
,默认生成的匹配文件的写法是绝对路径,这意味着将来的可移植性降低。于是配置:
懒加载
打包实际上将多个文件多合一,如果一次性加载所有的 js 文件是非常消耗性能的,因此推荐配置懒加载。
路由懒加载
对比: