目录
内容大概:
首页是由header和home两个组件构成,组件里面使用了element组件库,实现了轮播图、下拉显示一系列效果。
组件中安装element组件库:
npm i element-ui -S
步骤
在home组件的mounted里通过api向后端请求轮播图数据,再通过element进行渲染,—全观—代码如下:
<template>
<div class="home">
<!-- 轮播 start -->
<el-carousel :interval="5000" type="card" height="300px">
<el-carousel-item v-for="item,index in list">
<!--/detail?menuId=5d83bfba2f7cb93a4009cf98-->
<router-link :to="{name:'menulist'}">
<img
:src="item.product_pic_url"
width="100%"
alt=""
>
</router-link>
</el-carousel-item>
</el-carousel>
<!-- 轮播 end -->
<!-- 内容精选 瀑布流形式 start -->
<div>
<h2>内容精选</h2>
<!-- :info='info' -->
<waterfall ref="waterfall" @view="loadingMenuCard">
<menu-card :margin-left="13" :info="menuList"></menu-card>
</waterfall>
</div>
<!-- 内容精选 瀑布流形式 end -->
</div>
</template>
<script>
import MenuCard from '@/components/menu-card.vue'
import Waterfall from '@/components/waterfall.vue'
import { getBanner, getMenus } from '@/service/api.js'
// 引入 注册 使用
export default {
name: 'home',
components: {
MenuCard: MenuCard,
Waterfall
},
data(){
return {
list:[],
menuList:[],
page:1,
pages:0,
}
},
created(){
getBanner().then(res=>{
console.log(res)
this.list=res.data.list
})
getMenus({page:this.page}).then(({data})=>{
this.menuList=data.list;
this.pages=Math.ceil(data.total/data.page_size);
})
},
methods:{
loadingMenuCard(){
console.log('在外部监听到的滚动')
this.page++
if(this.page>this.pages){//当数据加载完毕,不在进行新数据渲染
this.$refs.waterfall.isLoading = false;
console.log('没有了崽子')
// console.log('111')
return;
}
this.$refs.waterfall.isLoading=true;
getMenus({ page: this.page }).then(({ data }) => {
this.menuList.push(...data.list)
this.$refs.waterfall.isLoading = false;
})
},
}
}
</script>
<style lang="stylus">
.home
h2
text-align center
padding 20px 0;
.el-carousel__item h3
color #475669
font-size 14px
opacity 0.75
line-height 200px
margin 0
.el-carousel__item:nth-child(2n)
background-color #99a9bf
.el-carousel__item:nth-child(2n+1)
background-color #d3dce6
</style>
data中声明的为:
list:[],//轮播图数据
menuList:[],//传给menu-card的值
page:1,//初始值,显示页数,后期为给pages作比较。
pages:0,初始值,后在计算属性中,将总数据的总页数赋给了它,后期为给page作比较。
有人问ref="waterfall"是干什么?真正作用最后列出;
ref是给这个元素盖个标签,方便取获。
1.获取轮播图数据并渲染:
created(){
getBanner().then(res=>{
this.list=res.data.list
})
},
api文件中:
export async function getBanner(){
return await http.get('/banner');
}
将值赋予给本组件中的属性中,并将数据渲染到页面中。
2.显示菜品
首先在home组件里的created周期里通过api向后端请求数据内容精选的第一页,并赋值给data里面的数组menuList,通过**:info**方法传递给子组件menu-card。
获取数据:
getMenus({page:this.page}).then(res=>{
this.menuList=res.data.list//存了第一页的五条
this.pages=Math.ceil(res.data.total/res.data.page_size)
})
传递数据:
<div>
<h2>内容精选</h2>
<waterfall ref="waterfall" @view='loadingMenuHanle()'>
<menu-card :margin-left="13" :info="menuList"></menu-card>
</waterfall>
</div>
api:
export async function getMenus(params){
return await http.get('/menu/query', {params});
}
3.让我们将注意移入menu-card组件中
从menu-card组件里面获取父组件home传递的info,通过v-for遍历,进行渲染
<template>
<el-row class="menu-card" type="flex" justify="start">
<el-col
style="flex:none;"
:style="{'margin-left':marginLeft+'px'}"
v-for=" item in info" :key="item.id"
>
<el-card :body-style="{ padding: '0px' }">
<router-link :to="{ name: 'detail', query: { menuId: item.menuId }}">
<img :src="item.product_pic_url" class="image" style="width: 232px;height: 232px;">
<div style="padding: 14px;" class="menu-card-detail">
<strong>菜品名称:{{item.title}}</strong>
<span>{{ item.comments_len}} 评论</span>
<router-link :to="{name:'space',query:{userId:item.userId}}" tag="em">
<!-- //em作用a标签不能嵌套 -->
作者--{{item.name}}
</router-link>
</div>
</router-link>
</el-card>
</el-col>
</el-row>
</template>
<script>
export default {
name: 'menu-card',
props:{
marginLeft: {
type: Number,
default: 22
},
info:{
type: Array,
default:() => []
}
}
}
</script>
<style lang="stylus">
.menu-card
flex-wrap wrap
.el-col-24
width auto
margin-bottom 20px
margin-left: 22px
.menu-card-detail
> *
display block
strong
height 24px
line-height 24px
font-size 14px
font-weight bold
color #333
span
height 26px
line-height 26px
font-size 12px
color #999
em
height 23px
line-height 23px
font-size 12px
color #ff3232
</style>
完成后—
4再来到waterfall组件中:
在home组件中通过滚动事件进行判断,当从可视窗上边到waterfall元素下边小于可视窗高的时候,调用父组件home的事件loadingMenuHanle。如代码:
<template>
<div class="waterfall" ref="waterfall">
<slot></slot>
<div class="waterfall-loading" ref='loading' v-show="isLoading">
<i class="el-icon-loading"></i>
</div>
</div>
</template>
<script>
//节流
import {throttle} from 'throttle-debounce'
export default {
name: 'Waterfall',
data(){
return {
isLoading:false,
}
},
mounted(){
//节流 性能优化:每隔一段时间,再去执行指定的功能
// 参数,第一个参数,间隔多少
// 第二个参数,节流谁
this.scrollHandler = throttle(500,this.scroll.bind(this))
window.addEventListener('scroll', this.scrollHandler);
},
//防止组件被解绑候,监听事件依然存在,需要解绑
destroyed(){
window.removeEventListener('scroll',this.scrollHandler)
},
methods:{
scroll(){
if (this.isLoading) return;
//clientHeight 可视高度
if (this.$refs.waterfall.getBoundingClientRect().bottom < document.documentElement.clientHeight){
//console.log('已到达可视区域')
this.isLoading = true;
this.$emit('view');//通知外部,滚动已完成,可以显示数据了
}
}
}
}
</script>
<style lang="stylus">
.waterfall-loading
width 100%
height 20px
text-align center
</style>
window.addEventListener:添加事件监听;
window.removeEventListener:移除事件监听;(在销毁周期destroyed时移除事件)
this.$ emit(‘view’)
有人问,为什么父级的滑动事件,在子级是怎么启用的, this.$emit(‘view’) 给你答案,它通过父子通信,通知Home.vue中waterfall组件中的@view事件处理完成。
5.Home的waterfall区域中,监听到滑动事件触发,执行Home中loadingMenuCard事件:
loadingMenuCard(){
this.page++
if(this.page>this.pages){//当数据加载完毕,不在进行新数据渲染
this.$refs.waterfall.isLoading = false;
alert('没有了崽子')
return;
}
this.$refs.waterfall.isLoading=true;
getMenus({ page: this.page }).then(({ data }) => {
this.menuList.push(...data.list)
this.$refs.waterfall.isLoading = false;
})
判断当前页数(page)大于总页数(pages)结束运行。如果小于总页数,向后添加到数组menuList里面。
为什么要再获取一次getMenus? 因为在page属性得到++时,需要获取更多页数,让其活跃起来。
让新请求的数据,将原本的menuList覆盖;
若有疑问,请及时点出哦
小作者在持续更新中…