想利用暑假时间好好学习一下vue,会记录每一天的学习内容。
今天是学习vue的第17
天!
起起伏伏乃人生常态,继续加油~
学习内容
1. 轮播图展示
(不写具体代码,直接引入使用)
在Home.vue
中代码:
// 引入
import {Swiper} from '../../components/common/swiper/Swiper.vue';
import {SwiperItem} from '../../components/common/swiper/SwiperItem.vue'
// 注册组件
components: {
NavBar,
Swiper,
SwiperItem
},
<!-- 放在navbar下面-->
<!-- 不用写四个,直接用v-for遍历-->
<!-- 每张图实际上可以点击跳转链接,所以用<a>标签包裹-->
<swiper>
<swiper-item v-for="item in banners" :key="item.title">
<a :href="item.link">
<img :src="item.image" alt="">
</a>
</swiper-item>
</swiper>
目前效果:
还可以再抽一层,如果不想把轮播图的内部逻辑也放在Home.vue
抽到HomeSwiper.vue
中:
这里要注意在HomeSwiper.vue
中,我们是没有banners
的数据的,就要借助父子通信了
// HomeSwiper.vue
props: {
banners: Array
},
在Home.vue
中引入、注册、使用:
import HomeSwiper from './childComponents/HomeSwiper.vue'
components: {
NavBar,
HomeSwiper
},
<!-- 这里要把banners数据传过去-->
<home-swiper :banners="banners"></home-swiper>
2. 推荐信息的展示
新建一个组件文件:
要拿到的数据在Home.vue
中,依然需要父子通信
props: {
recommends: Array
}
在Home.vue
中引入、注册、使用:
import HomeRecommendView from './childComponents/HomeRecommendView.vue'
components: {
NavBar,
HomeSwiper,
HomeRecommendView
},
<!--这里要把recommends数据传过去-->
<home-recommend-view :recommends="recommends"></home-recommend-view>
HomeRecommendView.vue
中代码:
比较简单,没啥要说的
<template>
<div id="recommend-view">
<div v-for="item in recommends" :key="item.sort">
<a :href="item.link">
<img :src="item.image" alt="">
<div>{{ item.title }}</div>
</a>
</div>
</div>
</template>
样式比较简单也就不放代码了
3. 封装一张图片
是的,就是一张图片,但是因为依然是一个模块,所以还是把它封装了
步骤都跟上面的封装一样,这里跳过
4. tab切换栏的封装
tab切换栏在别的界面也可能会用到,所以封装在components/content
中比较合适
活跃状态的tab栏有颜色和文字底部一条线
主要逻辑是给选项卡动态绑定active
类
默认是第一个选项卡有颜色,所以设置一个currentIndex = 第一个选项卡的index = 0
点击选项卡的时候能拿到index
,这时候就应该让currentIndex = index
,使得{active: ture}
,让该点击的选项卡绑定上active
类
<template>
<div class="tab-control">
<div @click="clickTab(index)" v-for="(item,index) in titles" :key="index">
<span :class="{active: currentIndex == index}">{{ item }}</span>
</div>
</div>
</template>
<script>
export default {
props: {
titles: Array,
},
data() {
return {
currentIndex: 0
}
},
methods: {
clickTab(index) {
this.currentIndex = index
}
}
};
</script>
.active {
color: #ff8198;
position: relative;
}
5. 首页商品数据的请求和保存
// home.js
export function getHomeGoodsData(type,page) {
return request({
url: '/home/data',
params: {
type,
page
}
})
}
// Home.vue
data() {
return {
goods: {
'pop': {page: 1, list: []},
'new': {page: 1, list: []},
'sell': {page: 1, list: []},
}
}
},
methods: {
getHomeGoodsData(type) {
const page = this.goods[type].page;
getHomeGoodsData(type, page).then(res => {
console.log(res);
// 这里不要直接赋值,当下拉滚动条,要请求更多的数据,也要加进来
this.goods[type].list.push(...res.data.data.list);
this.goods[type].page += 1;
})
}
},
created() {
// 2.请求商品数据
this.getHomeGoodsData('pop');
this.getHomeGoodsData('sell');
this.getHomeGoodsData('new');
}
6. 首页商品数据的展示
Goods.vue
中代码:
先从Home.vue
中拿到一整组goods
数据,再传给子组件GoodsItem.vue
<template>
<div class="goods">
<goods-item v-for="item in goods" :key="item.title" :goods-item="item"></goods-item>
</div>
</template>
<script>
import GoodsItem from './GoodsItem.vue'
export default {
props: {
goods: Array
},
components: {
GoodsItem
}
}
</script>
GoodsItem.vue
中代码:
<template>
<div class="goods-item">
<img :src="goodsItem.show.img" alt="">
<div>
<p class="goods-item-title">{{goodsItem.title}}</p>
<span class="goods-item-price">{{goodsItem.price}}</span>
<span class="goods-item-star">🌟{{goodsItem.cfav}}</span>
</div>
</div>
</template>
<script>
export default {
props: {
goodsItem: Object
}
}
</script>
Home.vue
中代码:
<!-- 拿到goods对象中,'pop'对应的对象中的list-->
<!-- 传给Goods组件 -->
<goods :goods="goods['pop'].list"></goods>
(这里我们的tab栏还不能切换对应商品,暂时传了一个固定的'pop'
)
(样式不放了,不是重点)
7. 点击tab栏切换商品数据
其实就是下面这行代码,不能传一个固定的'pop'
实际上是用户点到了哪个选项卡再将对应页面的数据传给子组件Goods
->GoodsItem
,再显示对应的商品
<goods :goods="goods['pop'].list"></goods>
我们的TabControl
组件中是监听了click
事件的,这时候应该在子组件TabControl
中发射自定义事件,也就是将用户点击了哪个选项卡相关的信息发到父组件Home
中
TabControl.vue
中代码:
methods: {
clickTab(index) {
this.currentIndex = index;
// 自定义事件名用短横线命名
// index要传出去,(选项卡的索引
this.$emit('tab-item-click',index);
}
}
Home.vue
中代码:
<!-- 自定义事件监听器要与自定义事件名完全匹配,不能用驼峰-->
<tab-control class="home-tab" :titles="titles" @tab-item-click="clickTab"></tab-control>
上面的tab-item-click
事件对应了clickTab
方法,我们就要定义一个clickTab
方法来处理这个事件
我们再思考一下index
怎么与goods
对象中的三个属性'pop'
、'new'
、'sell'
对应起来:
data() {
return {
goods: {
'pop': {page: 1, list: []},
'new': {page: 1, list: []},
'sell': {page: 1, list: []},
},
goodsType: ['pop','new','sell'],
// 默认值是'pop'
currentType: 'pop'
}
}
clickTab(index) {
this.currentType = this.goodsType[index]
},
这个时候就可以把该行固定写了'pop'
改成动态的了:
<goods :goods="goods[currentType].list"></goods>
(样式凑合看,马马虎虎随便写了一下😢)
8. 回到顶部组件封装使用
先新建一个BackTop
组件,然后在Home
组件中应用
(这里跳过,不放代码了,上面重复过很多遍)
然后要实现一个点击BackTop
组件回到顶部的功能,
⚠️一下:组件不能直接监听点击事件
<!-- 错误写法,监听不到 -->
<back-top @click="backTop"></back-top>
如果想要监听组件的原生事件,要加修饰符.native
<!-- 正确写法 -->
<back-top @click.native="backTop"></back-top>
backTop() {
let timer = null;
timer = setInterval(() => {
document.documentElement.scrollTop > 0
? (document.documentElement.scrollTop =
document.documentElement.scrollTop - 10)
: clearInterval(timer);
}, 5);
我们还需要实现:当界面滚动到一定位置,这个回到顶部按钮再出现,否则不出现
不出现可以在样式中设置display:none
,这里我就用了动态绑定类的方式:
<!-- 当isHide为true时,会有hide类-->
<back-top :class="{hide: isHide}" @click.native="backTop"></back-top>
data() {
return {
// 默认不出现
isHide: true,
}
},
// 生命周期钩子函数,在这个阶段页面已经渲染好了
mounted() {
// 监听界面的滚动事件
window.onscroll = () => {
document.documentElement.scrollTop > 800
? (this.isHide = false)
: (this.isHide = true);
}
9. 上拉加载更多
当滚动条到达界面底部时会自动加载更多商品数据:
滚动条到达底部会调用getHomeGoodsData()
方法,会发送一次新的网络请求,同时将新请求到的结果push
进goods
的list
数组,同时page
+1
(这里判断滚动条是否到达底部我没写兼容)
methods:{
listenScroll() {
window.onscroll = () => {
document.documentElement.scrollTop > 800
? (this.isHide = false)
: (this.isHide = true);
if (
document.documentElement.scrollTop +
document.documentElement.clientHeight >=
document.documentElement.scrollHeight
) {
this.getHomeGoodsData(this.currentType);
}
};
},
}
mounted() {
this.listenScroll();
}
getHomeGoodsData(type) {
const page = this.goods[type].page;
getHomeGoodsData(type, page).then(res => {
console.log(res);
this.goods[type].list.push(...res.data.data.list);
this.goods[type].page += 1;
})
}