点击 GoodListItem 跳转到详情页
创建 detail 视图,配置路由关系,点击 GoodListItem ,路由跳转到相应的 detail 界面。每个 GoodListItem 都有一个不同的 iid ,通过 iid 请求不同 GoodListItem 的数据。
1 | this.$router.push('/detail'+this.goodsItem.iid) |
1 导航栏
封装一个 DetailNavBar.vue,import navbar 对象;插槽
1)left 是一个返回图标,点击图标,页面返回到上一步
1 | goBack() { |
2)center 是一些 title ,v-for 遍历titles ,给个 title 添加个点击激活 active属性
2 轮播图
根据请求数据
1)为 detail 封装一个网络请求相关的 detail.js 文件。
2)在 Detail.vue 的 created 声明函数中,请求数据。
问题一:后端请求的图片太大,占满了整屏幕,实际可以参考设计图的。这里直接轮播图的高度设置为固定值,然后overflow:hidden
多余的图片
问题二:点击任何一个 GoodListItem ,进来的 Detail 页都是同一张。—》因为 keep-alive 的使用,就不会再执行 created 的方法 和 $router 了。
解决:<keep-alive exclude="Detail">
3 商品信息
抽离组件数据,将杂乱无章的数据,抽离成一个对象。
1) 比如这个商品信息,在networ的 details.js 将数据抽离成一个 Goods 对象
1 | export class Goods { |
2) 在 Detail 组件中created 找那个获取数据,最后将整理之后的数据存到 goods 对象中
1 | created() { |
可以通过dev-tool 查看,数据变得清晰明了

3)新建 DetailBaseInfo 组件,展示商品信息,最后暴露给 Detail.vue
4 商店shop 信息
封装 DetailShopInfo 组件,展示商店信息,最后暴露给 Detail.vue
4.1 filters 数据过滤
比如后端传来的总销量数据是一个10 进制数据,但是为了更好的显示数据,要求超过一万的数据转为为万数(123456 —>1.2 万)
1 | filters: { |
5 商品详情页
封装 DetailGoodsInfo 组件,展示商品详情页
【问题】 商品详情页有许多照片需要展示,可能造成 better-scroll 的计算高度问题。因此需要监听图片的加载,然后 refresh。
1.监听 DetailGoodsInfo 的img
2.加载完图片,emit Detail.vue imageLoad,在 Detail.vue 里面实现这个方法,即调用 scroll 的 refresh 方法
- 图片数量太多,每一张图片的 load 之后都需要 emit 效率太低,影响性能和用户体验,只需要所有图片加载完 emit 一次。
- 通过debounce 防抖
- watch 已经加载的图片数量,当图片加载到最后一张照片,即加载所有照片。
1 | data() { |
6 商品参数信息展示
1 请求数据 ;2 封装 DetailParamInfo 组件,展示商品详情页
8 商品评论信息
8.1 格式化时间戳
服务器一般返回时间都是一个时间戳 value(以 unix 时间元年为起点),但是页面展示一般都是具体的格式比如(yyyy-MM-dd)。
1.将时间戳(单位 s)转化为 Date(单位 ms) 类型:
const date = new Date(value*1000)
2.将 date 进行格式化,封装一个 formate 函数
1 | /** |
9 商品推荐信息展示
1.获取数据,接口换成了:path:’recommend’。把数据存储在 recommendInfo 里。
2.GoodList 的布局和 推荐页面布局一样,因此可以直接使用 GoodList 组件,*但是 *GoodList 里面展示的数据结构和 recommendInfo 存在差异
3.在存在差异的地方使用 计算属性进行判断选择
1 | //比如img 的路径不一致 |
【问题】: GoodListItem 监听图片 load 事件发送给 Home.vue ,但是因为 Detail 中复用了GoodListItem 组件, Detail 也会触发 load 事件,但是这个事件最终被 Home.vue 接收了。
【方法一】判断路由的路径,为不同路径 emit 不同方法
1 | imageLoad() { |
【方法二】利用事件总线的 $bus$off
在路由离开组件的时候,取消监听 itemImageLoad
1 | mounted() { |
在 detail 的 mounted 函数里进行事件监听
1 | mounted() { |
9.2 混入 mixin
在 home 和 detail 中的mounted中的代码一模一样,所以可以考虑混入(mixin)。类有相同的方法用继承,对象由相同的方法考虑 mixin。
组件对象中的声明周期函数和 mixin 中会合并,但是 methods 里的方法不会合并会覆盖。
1.创建混入对象,复用的部分写在混入对象里面
1 | // 定义一个混入对象 |
2 在组件中暴露混入对象
1 | import { itemListenerMixin } from "common/mixin"; |
10 点击标题滚到对应内容 (联动效果)
10.1 点击标题滚动到相应的内容
点击标题滚动到相应的内容 —》 获取相应 Dom 的 offsetTop
怎么获取 Dom 的 offsetTop
当每个组件对象获取数据之后,还需要渲染 DOM ,只有在 DOM 完全渲染之后,得到的 offsetTop 才是准确的,在生命周期函数中,update 钩子函数会在 数据更改导致的虚拟 DOM 重新渲染和打补丁调用,但是它不仅会多次调用,而且updated
不会保证所有的子组件也都一起被重绘。因此推荐使用 vm.$nextTick
vm.$nextTick( [callback] )
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法
Vue.nextTick
一样,不同的是回调的this
自动绑定到调用它的实例上。
1 | updated() { |
【问题】然而即使是在 update 中 使用 $nextTick 依然无法获取正确的 offsetTop,经过排查是 DetailGoodsInfo组件中的图片未加载完全导致。
【方案】将获取 offsetTop 的代码写在监听 DetailGoodsInfo 加载图片的 imageLoad 中
1 | imageLoad() { |
10.2 滚动页面,相应标题被激活
1 通过 Scroll 对象的scroll 获取position.y
2 将拿到的 position.y 和 themeTopYs 里面的数值进行比较,确定每个标题对应的数值区间。
数值区间可以通过遍历 themeTopYs ,普通做法
1 | // 1 增强型 for 循环 i 是 String 类型 |
hack 做法
空间换时间
1 | // 在 themeTopYs尾巴 push 一个极大数 |
3 将每个数值空间 和 navBar 的currentIndex 一一对应
11 底部工具栏
二倍精灵图(雪碧图)
1)计算几倍图:在 ps 中打开图片,依次打开图像——》图像大小,记录此时精灵图大小(width 是44px),而网页中图片预留位置是(width:22px),因此是二倍图
2)将这个精灵背景图等比例缩放到原来宽高的一半。即为(width:22px)
3)此时页面应显示的是缩放后的图片,打开 f8 找到它的坐标,将缩放后的xy坐标作为背景定位坐标
1 | .bar-left .shop { |
4)background-size的宽度是缩放后的宽度。这样,就完成了精灵二倍缩放,如果是三倍,将原图尺寸除以三就可以。
1 | display: block; |
12 回到顶部
12.1 复用 backTop 组件
1)定义变量 isShowBackTop 显示或者隐藏图标。
2)BACK_POSITION 是导入的常量,当滚动距离大于 改值,图标就显示
3)在Scroll 的 scroll 方法里面获取滚动的 y 值,和 BACK_POSITION 进行比较
4)监听点击事件,点击图标,调用scrollTo(0, 0 300),滚动头部。
12.2 mixin
在Home 和 Detail 中都需要用到回到顶部功能,因此可以考虑将这部分功能代码写入 mixin 中。
1 | import BackTop from "components/content/backTop/BackTop"; |
13 点击添加购物车
1) 监听底部添加按钮;2)addToCart 中获取商品信息,将信息存储在 vuex 里面
13.1 vuex
暴露、 注册、 新建 store 对象 、挂载到app
mutations 中的每个方法尽可能完成的时间比较的单一,如果功能复杂,比如下面的addCart 方法里面。跟踪 mutations 就不知道 addCart 是商品的count+1,还是向 countList 里面添加新的商品。
1 | mutations: { |
mutations 重构:mutations处理数据的改变,每个方法处理一种功能;添加商品相当于异步操作(因为页面并没有刷新)
1 | mutations: { |
抽离出 mutations 和 actions 代码分别封装在 mutations.js 和 actions.js 中,把 mutation 里的方法修改为常量类型 。
13.2 vuex 的 getters 新用法 — mapGetters 辅助函数
mapGetters 辅助函数将 store 中的 getter 映射到局部计算属性,也就是 getters 中的方法可以直接在组件中当做 computed 的计算属性来用
1)getters
1 | export default { |
2)在组件中导入 mapGetters 从 vuex
1 | import { mapGetters } from "vuex"; |
3)getters 映射到 computed 中
1 | computed: { |
4)使用自定义属性
小结
1)v-for 遍历如果后面跟的不是 Array 而是一个 Number,那么就会从 1 开始遍历到 n。
1 | <span v-for="index in 10" >{{index}}</span> 1.2.3...10 |
2) 判断一个对象是否为空
1 | const obj = { |
3.展示数据时 可以通过 v-if 判断数据是否为空,为空就不展示
1 | <img :src="goods.services[n-1].icon" v-if="goods.services[n-1].icon" alt /> |
4.动态绑定样式,打折描述背景颜色是由数据库的数据决定的,不同的 item 颜色不尽相同。
1 | <span :style="{background:goods.discountcolor}">{{goods.discount}}</span> |
5.在 vue cli3 如何用 less ,安装 less 和 less-loader
1 | npm install less less-loader --save-dev |
6.点击列表中的列表项,切换 active 属性
1)动态绑定 class
2) 监听点击事件,修改 currentIndex
1 | <div class="title-item" v-for="(item, index) in titles" :key="index" |
css