图片懒加载是一个很常用的功能, 特别是一些电商平台, 这对性能优化至关重要. 今天就用 vue 来实现一个图片懒加载的插件. 这篇博客采用 "三步走" 战略 --Vue.use(),Vue.direction,Vue 图片懒加载插件实现, 逐步实现一个 Vue 的图片懒加载插件.
Vue.use()
就像开发 jQuery 插件要用 $.fn.extent() 一样, 开发 Vue 插件我们要用 Vue.use(). 其实就是官方内部实现的一个方法, 供广大开发者灵活开发属于自己的插件. 只需要按照约定好的规则开发就行.
用法
安装 vue.js 插件. 如果插件是一个对象, 必须提供 install 方法. 如果插件是一个函数, 它会被作为 install 方法. install 方法调用时, 会将 Vue 作为参数传入.
该方法需要在调用 new Vue() 之前被调用.
当 install 方法被同一个插件多次调用, 插件将只会被安装一次.
注: install 方法或者被当做 install 方法的方法它的第一个参数是 Vue 构造器, 第二个参数是一个可选的选项对象.
参考链接:
- https://cn.vuejs.org/v2/api/#Vue-use
- https://segmentfault.com/a/1190000012296163
Vue.direction 自定义指令
用法 -- 全局注册和局部注册
全局注册
- // 注册一个全局自定义指令 `v-focus`
- Vue.directive('focus', {
- // 当被绑定的元素插入到 DOM 中时......
- inserted: function (el) {
- // 聚焦元素
- el.focus()
- }
- })
局部注册
- directives: {
- focus: {
- // 指令的定义
- inserted: function (el) {
- el.focus()
- }
- }
- }
钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind: 只调用一次, 指令第一次绑定到元素时调用. 在这里可以进行一次性的初始化设置.
inserted: 被绑定元素插入父节点时调用 (仅保证父节点存在, 但不一定已被插入文档中).
update: 所在组件的 VNode 更新时调用, 但是可能发生在其子 VNode 更新之前. 指令的值可能发生了改变, 也可能没有. 但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下).
componentUpdated: 指令所在组件的 VNode 及其子 VNode 全部更新后调用.
unbind: 只调用一次, 指令与元素解绑时调用.
钩子函数的参数
el: 指令所绑定的元素, 可以用来直接操作 DOM .
binding: 一个对象, 包含以下属性:
name: 指令名, 不包括 v- 前缀.
value: 指令的绑定值, 例如: v-my-directive="1 + 1" 中, 绑定值为 2.
oldValue: 指令绑定的前一个值, 仅在 update 和 componentUpdated 钩子中可用. 无论值是否改变都可用.
expression: 字符串形式的指令表达式. 例如 v-my-directive="1 + 1" 中, 表达式为 "1 + 1".
arg: 传给指令的参数, 可选. 例如 v-my-directive:foo 中, 参数为 "foo".
modifiers: 一个包含修饰符的对象. 例如: v-my-directive.foo.bar 中, 修饰符对象为 { foo: true, bar: true }.
vnode:Vue 编译生成的虚拟节点. 移步 VNode API 来了解更多详情.
oldVnode: 上一个虚拟节点, 仅在 update 和 componentUpdated 钩子中可用.
参考链接:
Vue 图片懒加载插件实现
思路: 事先提供俩个空数组 listenList(收集未加载的图片元素和资源) 和 imageCacheList(收集已加载的图片资源). 然后, 判断图片是否到达可视区, 如果到达, 则用 Image 对象去加载资源图片, 加载完毕后赋值给绑定元素的 src 让其显示. 同时, 将加载过的资源放入 imageCacheList 数组, 用 isAlredyLoad 方法做个判断, 防止之后相同的资源重复加载. 如果没到达, 则将元素和资源对象放到 listenList 数组, 最后进行滚动监听. 监听 listenList 数组中的元素是否可以加载资源.
插件的实现:
- // 引入 Vue 构造函数
- import Vue from 'vue'
- var lazyload = {
- // Vue.use() 默认加载 install, 并且将 Vue 当做第一个参数传递过来
- install(vue,payload) {
- // 数组扩展移除元素
- if(!Array.prototype.remove){
- Array.prototype.remove = function(item){
- if(!this.length) return
- var index = this.indexOf(item);
- if( index> -1){
- this.splice(index,1);
- return this
- }
- }
- }
- // 默认值图片
- var defaultImage = payload.defaultImage || 'https://gw.alicdn.com/tps/i1/TB147JCLFXXXXc1XVXXxGsw1VXX-112-168.png';
- var errorImage = payload.errorImage || 'https://gw.alicdn.com/tps/i1/TB147JCLFXXXXc1XVXXxGsw1VXX-112-168.png';
- // 默认离可视区 10px 时加载图片
- var distanece = payload.scrollDistance || 10;
- // 收集未加载的图片元素和资源
- var listenList = [];
- // 收集已加载的图片元素和资源
- var imageCacheList = [];
- // 是否已经加载完成的图片
- const isAlredyLoad = (imageSrc) => {
- if(imageCacheList.indexOf(imageSrc)> -1){
- return true;
- }else{
- return false;
- }
- }
- // 检测图片是否可以加载, 如果可以则进行加载
- const isCanShow = (item) =>{
- var ele = item.ele;
- var src = item.src;
- // 图片距离页面顶部的距离
- var top = ele.getBoundingClientRect().top;
- // 页面可视区域的高度
- var windowHeight = Windows.innerHight;
- // top - distance 距离可视区域还有 distance 像素
- if(top - distanece <Windows.innerHeight){
- var image = new Image();
- image.src = src;
- image.onload = function() {
- ele.src = src;
- imageCacheList.push(src);
- listenList.remove(item);
- }
- image.onerror = function() {
- ele.src = errorImage;
- imageCacheList.push(src);
- listenList.remove(item);
- }
- return true;
- }else{
- return false;
- }
- };
- const onListenScroll = () => {
- Windows.addEventListener('scroll',function(){
- var length = listenList.length;
- for(let i = 0;i<length;i++ ){
- isCanShow(listenList[i]);
- }
- })
- }
- //Vue 指令最终的方法
- const addListener = (ele,binding) =>{
- // 绑定的图片地址
- var imageSrc = binding.value;
- // 防止重复加载. 如果已经加载过, 则无需重新加载, 直接将 src 赋值
- if(isAlredyLoad(imageSrc)){
- ele.src = imageSrc;
- return false;
- }
- var item = {
- ele: ele,
- src: imageSrc
- }
- // 图片显示默认的图片
- ele.src = defaultImage;
- // 再看看是否可以显示此图片
- if(isCanShow(item)){
- return
- }
- // 否则将图片地址和元素均放入监听的 lisenList 里
- listenList.push(item);
- // 然后开始监听页面 scroll 事件
- onListenScroll();
- }
- Vue.directive('lazyload',{
- inserted: addListener,
- updated: addListener
- })
- }
- }
- export default lazyload;
插件的调用:
- import Vue from 'vue'
- import App from './App'
- import router from './router'
- import Lazyload from './common/js/lazyload'
- // 参数均为可选
- Vue.use(Lazyload,{
- scrollDistance: 15, // 距离可视区还有 15px 时开发加载资源
- defaultImage: '', // 资源图片未加载前的默认图片 (绝对路径)
- errorImage:'' // 资源图片加载失败时要加载的资源 (绝对路径)
- })
参考链接:
http://www.cnblogs.com/mdengcc/p/6773672.html
来源: https://www.cnblogs.com/chenwenhao/p/10505387.html