一、详情页-导航栏的封装
-
导航稍微有点复杂,不要在直接
Deatil.vue
里面做 -
detail
文件夹下新建文件夹childComps
,里面新建文件DetailNavBar.vue
Detail.vue
Detail.vue
-
效果
二、数据请求以及轮播图展示
1.数据请求
network
文件夹新建文件detail.js
2.轮播图展示
DetailSwiper.vue
<template>
<swiper class="detail-swiper">
<swiper-item v-for="(item,index) in topImages" :key="index">
<img :src="item" alt="">
</swiper-item>
</swiper>
</template>
<script>
import {Swiper, SwiperItem} from 'components/common/swiper'
export default {
name: "DetailSwiper",
components: {
Swiper,
SwiperItem
},
props: {
topImages: {
type: Array,
default() {
return []
}
}
}
}
</script>
<style scoped>
.detail-swiper {
height: 300px;
overflow: hidden;
}
</style>
- bug:每一个详情的数据没有更新,因为keep-alive缓存了
三、商品基本信息的展示
数据整合,使用类Class
detail.js
Detail.vue
- 目前goods里的数据如下
商品基本信息展示
- 新建
DetailBaseInfo.vue
,内容如下
<template>
<!-- 有数据展示,无数据就不展示 Object.keys().length判断对象是不是空的 -->
<div v-if="Object.keys(goods).length != 0" class="base-info">
<div class="info-title">{{ goods.title }}</div>
<div class="info-price">
<span class="n-price">{{ goods.newPrice }}</span>
<span class="o-price">{{ goods.oldPrice }}</span>
<span v-if="goods.discount" class="discount">{{ goods.discount }}</span>
</div>
<div class="info-other">
<span>{{ goods.columns[0] }}</span>
<span>{{ goods.columns[1] }}</span>
<span>{{ goods.services[goods.services.length - 1].name }}</span>
</div>
<div class="info-service">
<!-- services在info-other已经展示过了,所以这里 -1 循环的是一个数字 -->
<span
class="info-service-item"
v-for="index in goods.services.length - 1"
:key="index"
>
<img :src="goods.services[index - 1].icon" alt="" />
<span>{{ goods.services[index - 1].name }}</span>
</span>
</div>
</div>
</template>
<script>
export default {
name: "DetailBaseInfo",
props: {
goods: {
type: Object,
default() {
return {};
},
},
},
};
</script>
<style scoped>
.base-info {
margin-top: 15px;
padding: 0 8px;
color: #999;
border-bottom: 5px solid #f2f5f8;
}
.info-title {
color: #222;
}
.info-price {
margin-top: 10px;
}
.info-price .n-price {
font-size: 24px;
color: var(--color-high-text);
}
.info-price .o-price {
font-size: 13px;
margin-left: 5px;
text-decoration: line-through;
}
.info-price .discount {
font-size: 12px;
padding: 2px 5px;
color: #fff;
background-color: var(--color-high-text);
border-radius: 8px;
margin-left: 5px;
/*让元素上浮一些: 使用相对定位即可*/
position: relative;
top: -8px;
}
.info-other {
margin-top: 15px;
line-height: 30px;
display: flex;
font-size: 13px;
border-bottom: 1px solid rgba(100, 100, 100, 0.1);
justify-content: space-between;
}
.info-service {
display: flex;
justify-content: space-between;
line-height: 60px;
}
.info-service-item img {
width: 14px;
height: 14px;
position: relative;
top: 2px;
}
.info-service-item span {
font-size: 13px;
color: #333;
}
</style>
四、店铺信息的解析和展示
DetailShopInfo.vue
直接复制源码,目前效果如下
五、加入滚动效果的scroll
详情页不显示tabBar
加入scroll
六、商品详情数据展示
直接复制源码的DetaiLGoodsInfo.vue
,并修改相关代码
七、商品参数的展示
直接复制源码的DetaiLParamInfo.vue
,并修改相关代码
八、商品评论信息的展示
直接复制源码的DetaiLCommentInfo.vue
,并修改相关代码
(重点)时间格式化
服务器返回的时间为时间戳,需要格式化成时间字符串
九、商品推荐数据的展示
-
detail.js
封装请求推荐数据函数
-
Detail.vue
请求推荐数据
-
使用之前封装的
GoodsList.vue
,传recommends给它,由于与首页的商品数据结构有些不同,这里需要在GoodsListItem.vue
用computed
做一个判断
DetailGoodsInfo.vue
需要改一下,不然不占位会有bug,会覆盖住上面的图片
十、首页和详情页监听全局事件和mixin的使用
首页和详情页监听全局事件
- 推荐商品是使用之前封装的
GoodsLIst.vue
,里面对图片加载进行监听,并通知到首页进行刷新,但是我们这里是在详情页,却对首页刷新,不应该这样。应该做一个区分,怎么区分监听首页还是详情页?- 方式一:路由
- 方式二:离开时取消事件监听
Home.vue
Detail.vue
也做一层封装
- 方式一:路由
- 我们会发现,
mounted
里面的代码是一样的,所以用mixin对它进行一下封装
mixin(混入)的使用
common
文件夹新建mixin.js
,把mounted
重复代码和data
里面的itemImgListener: null
,剪切进去
- 在
Home.vue
和Detail.vue
里面导入使用
十一、点击标题滚动到对应内容
- 在
created
里面这样写不行
- 需要在图片加载完后,再获取
offsetTop
,避免获取频繁,使用防抖debounce
十二、滚动内容显示对应标题
- 普通做法
- 优点: 不需要引入其他的内容, 通过逻辑解决
- 缺点: 判断条件过长, 并且不容易理解
十三、对复杂判断条件分析与优化
- hack做法
- 条件: 给themeTops最后添加一个很大的值, 用于和最后一个主题的top进行比较.
- 优点: 简洁明了, 便于理解
- 缺点: 需要引入一个较大的int数字
十四、底部工具栏的封装
- 直接复制
DetailBottomBar.vue
并引入
十五、BackTop的混入封装
- 视频将BackTop做了混入封装,但是个人觉得太乱太麻烦,就不抽取了,直接复制过去
十六、将商品加入购物车中
十七、将商品添加到store
十八、Vuex中代码重构
mutations
每个方法尽可能完成的事件比较单一一点,但是我们这里的addCart
做了两件事:数量加一和新添加商品,这种有逻辑判断的放actions里面比较好-
Detail.vue
-
index.js
-
- 视频中还对
store
做了模块抽取,我这里就不抽取了