项目需求分析
1 项目前期准备工作
1. cli搭建项目
2.css样式导入 公共资源图片等的导入
<script>
module.exports = {
configureWebpack: {
resolve: {
alias: {
'assets': '@/assets',
'common': '@/common',
'components': '@/components',
'network': '@/network',
'views': '@/views',
}
}
}
</script>
<script>
root = true
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
</script>
2 底部导航栏封装
3 Home 顶部导航栏封装(因我在其他的页面中也有这个组件)
4 Home组件首页轮播图组件导入
5 安装axios 网络封装
<script>
import axios from 'axios'
export function request(config) {
// 1.创建axios的实例
const instance = axios.create({
baseURL: 'http://123.207.32.32:8000/api/h8',
//baseURL: 'http://106.54.54.237:8000/api/v1,
timeout: 5000
})
// 2.axios的拦截器
// 2.1.请求拦截的作用
instance.interceptors.request.use(config => {
return config
}, err => {
// console.log(err);
})
// 2.2.响应拦截
instance.interceptors.response.use(res => {
return res.data
}, err => {
console.log(err);
})
// 3.发送真正的网络请求
return instance(config)
}
6 在组件刚一创建的时候就发送网络请求,在created生命周期函数进行网络请求
轮播图数据展示/推荐数据展示/流行数据展示(feature)
7 Tab'Cont'rol业务分析 因为在其他页面还有使用 应该是在业务组件中
8 请求首页goods数据
点击3个不同的按钮请求不同的数据,首页列表数据请求保存
goods: {
'pop': {page: 1, list:[]},
'new': {page: 1, list:[]},
'sell': {page: 1, list:[]}
}
//发送HomeGoods商品列表数据 需要进行参数传递
export function getHomeGoods(type, page) {
return request({
url: '/home/data',
params: {
type,
page
}
})
}
//请求数据
getHomeGoods('pop',1).then(res => {
console.log(res);
})
8.1如何点击不同tabControl 请求不同的数据
// 2 请求homeGoods商品数据
this.getHomeGoods('pop')
this.getHomeGoods('new')
this.getHomeGoods('sell')
getHomeGoods(type) {
const page = this.goods[type].page + 1
getHomeGoods(type,page).then(res => {
// console.log(res.data.list);
this.goods[type].list.push(
8.2对商品列表分析,和业务相关 而不是公共组件了
然后把数据传递到goodsList goodsListItem
9 引入better-scroll 解决在移动端滚动卡顿问题
需求: 为了减少对better-sroll的依赖,对better-scroll 进行封装 而且在多数页面需要用到,在公共组件进行创建创建,将引用插件进行封装成一个组件,那里需要进行导入这个组件即可,后期维护也方便,逻辑也i清晰
better-scroll安装:
1. 终端通过npm安装: npm install better-scroll --save
<template>
<div class="wrapper" ref="wrapper">
<div class="content">
<slot></slot>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
data() {
return {
scroll: null
}
},
props: {
probeType: {
type: Number,
default() {
return 0
}
}
},
mounted() {
//1 创建BScroll实力对象
this.scroll = new BScroll(this.$refs.wrapper, {
click: true,
probeType: this.probeType //默认为不触发scroll滚动事件,那个页面需要进行传值即可,这样性能也大大提高
})
滚动原理:
10 点击backTop按钮返回Home顶部
需求:
需求分析:
-
首先该功能会在页面滚动某个临界值进行显示或隐藏,其次不会随着页面滚动而滚动,所以简单方法进行fixed 固定定位
-
对页面需要进行实时监听滚动位置
-
该需求是在Home 首页组件进行监听,也就是需要对backTop这个组件进行监听
下面进行代码实现:
-
新建BackTop组件,引入一张箭头图片,如下
<template>
<div class="back-top" >
<img src="~assets/img/common/top.png" alt="">
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.back-top {
position: fixed;
right: 8px;
bottom: 50px;
}
.back-top img {
42px;
height: 42px;
}
</style> -
在Home首页组件导入并注册:(需要注意的是这个组件是不需要随着滚动的)
-
对这个组件进行事件点击监听 --------组件监听需要用到事件修饰符native(这里也可以在backTop组件内部进行点击事件,然后把这个事件发送出来在进行监听,这样做不过相对繁琐,还多写了代码)

然后通过ref拿到scroll这个组件调用scrollTo()这个方法
backTopClick() {
// console.log(this.$refs.scroll.scroll);
// this.$refs.scroll.scroll.scrollTo(0, 0, 400);
this.$refs.scroll.scrollTo(0, 0, 400);
}
//scrollTo(x, y, time, easing)
//参数:
//{Number} x 横轴坐标(单位 px)
//{Number} y 纵轴坐标(单位 px)
//{Number} time 滚动动画执行的时长(单位 ms)
//{Object} easing 缓动函数,一般不建议修改,如果想修改,参考源码中的 ease.js 里的写法
//返回值:无
//作用:滚动到指定的位置,引用:https://ustbhuangyi.github.io/better-scroll/doc/zh-hans/api.html#scrolltox-y-time-easing//this.$refs.scroll.scroll.scrollTo(0, 0, 400)
//此代码不方便阅读,对奇进行封装,把插件scrollTo()封装到组件scrollTo中 看代码吧 这样更清楚
4.对Home页面滚动进行实时监听,而我们封装的BScroll滚动插件是scroll这个组件,这里需要在scroll组件进行监听,然后把这个事件发送出了
mounted() {
//1 创建BScroll实力对象
this.scroll = new BScroll(this.$refs.wrapper, {
click: true,
probeType: this.probeType
}),
//发送事件监听事件
this.scroll.on('scroll', (position) => {
// console.log(position);
this.$emit('scroll',position)
})
}
然后在首页接受这个事件,设置一个变量控制backTop显示隐藏
contentScroll(position) {
console.log(position);
this.isShow = -position.y > 1300 ? true : false
}
目前代码效果:
11 点击商品列表进入商品详情页
分析: 每个商品都有不同id,所以在点击每个goodslistitem,我们需要拿到这个唯一的商品id,去后台接口请求对应的商品数据,然后在进行展示,而且从home也跳转到详情页,我们使用路由就可以
1 对goodslistitem进行点击事件,并将商品id传过去
goodsItemClick() {
console.log('111',this.goodsitem.iid);
this.$router.push('/detail/'+ this.goodsitem.iid)
}
//路由跳转传参 可以有2种方式 1 动态路由 2 query方式
goodsItemClick() {
// console.log('-----',this.goodsitem.iid)
//动态路由
this.$router.push('/detail/'+ this.goodsitem.iid)
//query方式
this.$router.push({
path: '/detail',
query: {
iid: this.goodsitem.iid
}
})
}
//路由配置方式也不一样
{
path: '/detail/:iid', //动态路引
component: Detail
},
{
path: '/detail', //query方式
component: Detail
}
//获取iid方式
this.iid = this.$route.params.iid
this.iid = this.$route.query.iid
// 另外在url中显示也有差异 如下
2 拿到iid在network请求数据,为了方便后期维护 阅读,我们单独新建详情页网络请求 dedtail.js
import {request} from './request'
export function getDetail(iid) {
return request({
url: '/detail',
params: {
iid
}
})
}
之后在Detail组件刚一创建出来created生命周期函数进行网络请求
12 上拉加载更多
在封装好的 BScroll 中 调用上拉加载更多事件,然后把这个事件发送出来,然后在home页 执行并调用finishPullUp()
//上拉加载更多
this.scroll.on('pullingUp',() => {
// console.log('上拉加载更多');
this.$emit('pullingUp')
// this.scroll.finishPullUp()
})
home页进行监听
//上拉加载更多
pullUpClick() {
// console.log('上拉加载');
this.getHomeGoods(this.currentType)
this.$refs.scroll.finishPullUp()
},
13 解决首页 详情页 滚动卡顿问题
-
分析:
-
主要原因是图片加载影响 better-scroll计算可滚动区域问题,不能滚动的问题主要是因为图片加载完成后,这个时候better-scroll得到可滚动区域height,没有刷新,所导致
-
我们对每一张加载图片进行监听 ,只要有一张图片你加载完成,我们就重新计算一次Bscroll的高度
-
如何监听图片加载完成
-
原生js监听 img.load = () => {}
-
Vue提供的加载方式 @load=() => {}
-
-
然后调用better-scroll的refresh()
//refresh()
//参数:无
//返回值:无
//作用:重新计算 better-scroll,当 DOM 结构发生变化的时候务必要调用确保滚动的效果正常。
-
-
这里涉及到非父子组件事件通信,我们可以使用Vuex或者事件总线 这里我们先使用事件总线
-
1 事件总线使用方法:
-
main.js入口文件 挂载到Vue原型实例上,全局任何地方都可以使用
-
Vue.prototype.$bus = new Vue()
-
发送事件
-
this.$bus.$emit('itemImageLoad',params)// 事件函数, 参数
-
接收事件
-
this.$bus.$on("", callback) // 事件函数 回调函数 一般是组件运行阶段,mounted函数中执行
-
取消事件
-
this.$bus.$off("", callback) // 事件函数 回调函数 取消事件是组件是否有keep-alive 有选择deactivated 否destroyed中
-
-
2 使用Vuex进行非父子组件通信
-
先创建·1一个变量,对这个变量植进行watch监听,每加载一张图片,变量植发生改变
-
14 实现详情页上下联动联动效果,点击对应标题滚动到对应的位置和内容滚动显示正确标题
-
效果图:
-
-
点击标题跳转到对应位置
-
每给标题对应一个值,只要我们拿到这个值,在点击标题的时候,调用scrollTo就可以滚 动到对应位置,所以我们新建一个数组,来保存offsetTop
-
首先对详情页 标题 进行监听
-
获取 详情页 所有组件的offsetTop
-
如何获取 不同组件 的offsetTop
-
首先我们会想到在mounted中获取offsetTop,然而获取的值完全不对,甚至出现了undefined
-
mounted() {
console.log('-----')
this.themeTops = [];
this.themeTops.push(0);
this.themeTops.push(this.$refs.paramsinfo.$el.offsetTop);
this.themeTops.push(this.$refs.commentinfo.$el.offsetTop);
this.themeTops.push(this.$refs.recommend.$el.offsetTop);
console.log(this.themeTops);
},
//[0, undefined, 498, 626, __ob__: Observer] -
值获取不正确,首先想到的是图片影响,之前我们有对图片的加载进行监听,图片加载完成后,我们再获取offsetTop值
-
//对详情页图片监听
detailImageLoad() {
//获取参数 评论 推荐的offsetTop值
this.themeTops = [];
this.themeTops.push(0);
this.themeTops.push(this.$refs.paramsinfo.$el.offsetTop);
this.themeTops.push(this.$refs.commentinfo.$el.offsetTop);
this.themeTops.push(this.$refs.recommend.$el.offsetTop);
console.log(this.themeTops);
this.$refs.scroll.refresh();
},
// 打印结果 [0, 9289, 10031, 10180, __ob__: Observer] -
然后调用scrollTo方法
-
titleClick(index) {
// console.log(index);
this.$refs.scroll.scrollTo(0, -this.themeTops[index], 300)
},
-
-
-
内容滚动显示正确标题
-
分析: 我们需要实时对页面滚动进行监听 ,在better-scroll中把scroll这个事件发送出来,我们在Detail进行接受这个事件
-
通过获取到posiiton.y 和之前获取到 this.themeTops这个数组中4个值进行比较 然后动态改变标题
-
-
14 Vue原生上下联动或左右联动效果
-
上下联动或左右联动效果非常常见 但是我们平时大多使用封装好的UI工具库,直接按需导入安装,这里我们不使用插件和UI
-
这里我们主要简单建立三个组件
-
-
1 点击ShopTitles中小titles 让活跃title显示背景颜色 其他不变,这个相对简单
-
2 点击不同title让右边的Shop组件跳转到不同位置
-
1 首先要监听SHop组件的滚动,其次调用JS原生滚动方法 scroll
-
2 对Shop组件添加滚动事件 需要注意的是 addEventListener第三个参数为true ,
-
3 父元素 overflow不能是hidden,而是scroll,要不然子元素滚动不了 (当然子元素内容高度高于父元素高度)
-
//父组件
<template>
<div class="category">
<div class="nav-bar" >
<div class="nav"> 我是顶部的商品展示区域</div>
</div>
<shop-titles :titles="titles" @titleClick="titleClick" ref="titles"/>
<shop ref="shop" @shopOpsitons="shopOpsitons" />
</div>
</template>
<script>
import Shop from 'components/common/shop/Shop'
import ShopTitles from 'components/common/shop/ShopTitles'
export default {
name: 'Category',
data() {
return {
titles:['热卖','特色精品','精选热菜','热卖','特色精品','精选热菜'],
shopScrollTop:[0, 300, 600, 900, 1200, 1500],
current: 0
}
},
components: {
Shop,
ShopTitles
},
mounted() {
},
methods: {
titleClick(index) {
this.$refs.shop.$el.scrollTo({left: 0, top: this.shopScrollTop[index],behavior: 'smooth'})
},
shopOpsitons(saveY) {
// console.log(saveY)
const Max = Number.MAX_VALUE;
this.shopScrollTop.push(Max);
const length = this.shopScrollTop.length;
for( let i = 0; i < length; i++) {
if((this.current !== i)&&(saveY >= this.shopScrollTop[i] && saveY < this.shopScrollTop[i+ 1])) {
this.current = i
this.$refs.titles.currentIndex = this.current
}
}
}
}
}
</script>
<style scoped>
.category {
height: 100vh;
}
.nav-bar {
100%;
border-bottom: 2px solid #333;
height: 160px;
margin: 20px 0;
position: fixed;