-----------------------------------------------------------
- 图片懒加载
- 自定义指令
- 使用mock模拟随机图片
- 列表组件如下(主要内容):
- 配置自定义指令
图片懒加载
实现思路
使用自定义指令实现通用图片懒加载(在图片到达视口内时再进行加载)
自定义指令
可以理解为另一个生命周期,在各个钩子函数中处理相关内容,常用的钩子函数如下
// 自定义指令配置
export default {// 只调用一次,指令第一次绑定到元素时调用bind(el,boundings){// 处理},// 被绑定元素插入父节点时调用 inserted(el,boundings){// 处理},// 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前update(el,boundings){// 处理}// 只调用一次,指令与元素解绑时调用unbind(el){// 处理}
}
通常是使用对象来配置,当bind钩子与update钩子内容一致,而不关心其它的钩子时可以简写为函数配置:
// 自定义指令配置
export default function(el,boundings){// 处理
}
使用mock模拟随机图片
Mock.mock(/^\/api\/blog(\?.+)?$/,"get",function(options){// 使用动态模拟数据const query = qs.parse(options.url);return Mock.mock({"code": 0,"msg": "string","data": {"total|200-800": 0,[`rows|${query.limit||10}`]: [{"id": "@guid","title": "@ctitle","description": "@cparagraph(1,10)","createDate": "@date('T')","scanNumber|1000-10000": 0,"commentNumber|0-400": 0,"category": {"id|0-10": 0,"name": "分类@id"},"thumb|1": ["@image(300x250,@color,#fff,@natural)",null]}]}})
})
列表组件如下(主要内容):
<template><div class="bloglist-container" v-loading="isLoading" ref="bloglistRef"><ul class="list"><li class="item" v-for="item in data.rows" :key="item.id"><router-link :to="{name: '/blogDetail',params: {id: item.id}}"><div class="img" v-if="item.thumb"><!-- 绑定自定义指令 --><img :src="item.thumb" class="image" v-lazy="item.thumb"></div></router-link><div :class="item.thumb?'txt':'ptxt'"><router-link :to="{name: '/blogDetail',params: {id: item.id}}"><h2 class="title">{{ item.title }}</h2></router-link><div class="info"><span>日期:{{ formatDate(item.createDate) }}</span><span>浏览:{{ item.scanNumber }}</span><span>评论:{{ item.commentNumber }}</span><router-link :to="{name:'/categoryBlog',params: {categoryId: item.category.id}}"><span class="cate">分类:{{ item.category.id }}</span></router-link></div><p class="desc">{{ item.description }}</p></div></li></ul><Pager :current="this.page" :total="data.total" :limit="this.limit" @pageChange="handlePageChange"/></div>
</template><script>
import * as blogApi from '@/api/blog.js'
import fetchData from '@/mixins/fetchData';
import { formatDate } from '@/utils/formatDate';
import Pager from '@/components/Pager';
import scrollEvent from '@/mixins/scrollEvent';
export default {mixins: [fetchData({}),scrollEvent("bloglistRef")],components: {Pager,BackTop},computed:{page(){return +this.$route.query.page || 1;},limit(){return +this.$route.query.limit || 10;},categoryId(){return +this.$route.params.categoryId || -1;}},methods:{// 获取文章列表async getFetchData(){return await blogApi.getBlog(this.page,this.limit,'',this.categoryId);},formatDate,},
}
</script>
配置自定义指令
使用一个数组保存ajax获取的所有列表元素相关内容,然后在元素插入父组件时先进行一次处理,将初始就在视口的图片加载,已经加载完毕的元素相关内容会被数组删除,然后通过滚动事件来进行图片处理,最后进行事件清除,具体如下:
import defaultImg from '@/assets/default.gif';
import eventBus from '@/eventBus';
import {deBounce} from '@/utils'// 刚开始ajax获取的所有图片相关节点、src都在内,处理后会被此数组剔除
let imgs = []; // 需要处理的图片数组/*** 处理图片,先赋值默认图片,如在视口内则加载源图片* @param {*} i 数组内容,包含该元素dom,以及图片源src*/
function setImage(i){// 先使用默认图片i.dom.src = defaultImg;// 判断是否位于视口内const clientHeight = document.documentElement.clientHeight;const domTop = i.dom.getBoundingClientRect().top;const domHeight = i.dom.getBoundingClientRect().height || 250;// console.log(clientHeight,domTop,domHeight);if(domTop >= -domHeight && domTop <= clientHeight){// 在视口内i.dom.src = i.src;// 处理完成后清除数组中相关内容imgs.filter((img)=> img!==i);}}
/*** 批量处理所有图片*/
function setImages(){for (const i of imgs) {setImage(i);}
}
/*** 用于触发setImages*/
function handleScroll() {setImages();}// 事件总线监听滚动
eventBus.$on('blogMainScroll',deBounce(handleScroll,50));// 自定义指令配置
export default {// 被绑定元素插入父节点时调用 inserted(el,boundings){const img = {dom: el,src: boundings.value};imgs.push(img);setImage(img) // 处理最开始的数组内容},// 只调用一次,指令与元素解绑时调用unbind(el){// 处理结束后,清除数组内相关内容,避免重复处理imgs = imgs.filter((img) => img.dom !== el);}
}