实现一个Vue自定义指令懒加载的方法示例


Posted in Javascript onJune 04, 2020

在项目中如果有大量的图片需要加载的时候,就可以考虑使用懒加载了,懒加载其实就是监听浏览器的滚动,当滚动到一定的范围的时候就将图片的真实路径赋给src,然后取消监听。实现的方法也比较简单,可以通过懒加载的插件实现,也可以手写,手写通过vue自定义指令来实现,一般情况自定义指令用的也不多,比较vue自带的就够用了,大型复杂的项目的可能用的多。

什么是图片懒加载

当我们向下滚动的时候图片资源才被请求到,这也就是我们本次要实现的效果,进入页面的时候,只请求可视区域的图片资源这也就是懒加载。

比如我们加载一个页面,这个页面很长很长,长到我们的浏览器可视区域装不下,那么懒加载就是优先加载可视区域的内容,其他部分等进入了可视区域在加载。

这个功能非常常见,你打开淘宝的首页,向下滚动,就会看到会有图片不断的加载;你在百度中搜索图片,结果肯定成千上万条,不可能所有的都一下子加载出来的,很重要的原因就是会有性能问题。你可以在Network中查看,在页面滚动的时候,会看到图片一张张加载出来。

实现一个Vue自定义指令懒加载的方法示例

为什么要做图片懒加载

懒加载是一种网页性能优化的方式,它能极大的提升用户体验。就比如说图片,图片一直是影响网页性能的主要元凶,现在一张图片超过几兆已经是很经常的事了。如果每次进入页面就请求所有的图片资源,那么可能等图片加载出来用户也早就走了。所以,我们需要懒加载,进入页面的时候,只请求可视区域的图片资源。

总结出来就两个点:

1.全部加载的话会影响用户体验

2.浪费用户的流量,有些用户并不像全部看完,全部加载会耗费大量流量。

懒加载原理

图片的标签是 img标签,图片的来源主要是 src属性,浏览器是否发起加载图片的请求是根据是否有src属性决定的。

所以可以从 img标签的 src属性入手,在没进到可视区域的时候,就先不给 img 标签的 src属性赋值。

懒加载实现

实现效果图:

实现一个Vue自定义指令懒加载的方法示例

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Document</title>
 <style>
  div {
   display: flex;
   flex-direction: column;
  }
  img {
   width: 100%;
   height: 300px;
  }
 </style>
</head>
<body>
 <div>
  <img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657907683.jpeg">
  <img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657913523.jpeg">
  <img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657925550.jpeg">
  <img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657930289.jpeg">
  <img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657934750.jpeg">
  <img data-src="https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657918315.jpeg">
 </div>
</body>
</html>

监听滚动根据offsetTop判断

const imgs = [...document.getElementsByTagName('img')];
 let n = 0;

 lazyload();
 function throttle(fn, wait) {
 let timer = null;
 return function(...args) {
  if(!timer) {
   timer = setTimeout(() => {
    timer = null;
    fn.apply(this, args)
   }, wait)
  }
 }
 }
 window.addEventListener('scroll', throttle(lazyload, 200));
 
 function lazyload() {
 var innerHeight = window.innerHeight; 
 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
 for(let i = n; i < imgs.length; i++) {
  if(imgs[i].offsetTop < innerHeight + scrollTop) {
   imgs[i].src = imgs[i].getAttribute("data-src");
   n = i + 1;
  }
  
 }
 }

可能会存在下面几个问题:

  • 每次滑动都要执行一次循环,如果有1000多个图片,性能会很差
  • 每次读取 scrollTop 都会引起回流
  • scrollTop跟DOM的嵌套关系有关,应该根据getboundingclientrect获取
  • 滑到最后的时候刷新,会看到所有的图片都加载了

IntersectionObserver

const imgs = [...document.getElementsByTagName('img')];
// 当监听的元素进入可视范围内的会触发回调
 if(IntersectionObserver) {
  let lazyImageObserver = new IntersectionObserver((entries, observer) => {
   entries.forEach((entry, index) => {
    let lazyImage = entry.target;
    // 相交率,默认是相对于浏览器视窗
    if(entry.intersectionRatio > 0) {
    lazyImage.src = lazyImage.getAttribute('data-src');
    // 当前图片加载完之后需要去掉监听
     lazyImageObserver.unobserve(lazyImage);
    }

   })
  })
  for(let i = 0; i < imgs.length; i++) {
  lazyImageObserver.observe(imgs[i]);
  }
 }

codePen

vue自定义指令-懒加载

Vue自定义指令

下面的api来自官网自定义指令:

钩子函数

  • bind: 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted: 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update: 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
  • componentUpdated: 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind: 只调用一次,指令与元素解绑时调用。

钩子函数参数

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • 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 编译生成的虚拟节点。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

实现 v-lazyload 指令

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <style>
   img {
    width: 100%;
    height: 300px;
   }
  </style>
 </head>
 <body>
  <div id="app">
   <p v-for="item in imgs" :key="item">
    <img v-lazyload="item">
   </p>
  </div>
 </body>
 <script src="https://cdn.jsdelivr.net/npm/vue"></script>
 <script>
  Vue.directive("lazyload", {
   // 指令的定义
   bind: function(el, binding) {
    let lazyImageObserver = new IntersectionObserver((entries, observer) => {
     entries.forEach((entry, index) => {
      let lazyImage = entry.target;
      // 相交率,默认是相对于浏览器视窗
      if(entry.intersectionRatio > 0) {
       lazyImage.src = binding.value;
       // 当前图片加载完之后需要去掉监听
       lazyImageObserver.unobserve(lazyImage);
      }

     })
    })
    lazyImageObserver.observe(el);
   },
  });
  var app = new Vue({
   el: "#app",
   data: {
    imgs: [
     'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657907683.jpeg',
     'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657913523.jpeg',
     'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657925550.jpeg',
     'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657930289.jpeg',
     'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657934750.jpeg',
     'https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657918315.jpeg',
    ]
   },
  });
 </script>
</html>

codePen

参考

延迟加载(Lazyload)三种实现方式

原生js实现图片懒加载(lazyLoad)

到此这篇关于实现一个Vue自定义指令懒加载的方法示例的文章就介绍到这了,更多相关Vue自定义指令懒加载内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jquery.combobox中文api和例子,修复了上面的小bug
Mar 28 Javascript
一个js控制的导航菜单实例代码
Dec 03 Javascript
javascript解析json数据的3种方式
May 08 Javascript
Js实现无刷新删除内容
Apr 29 Javascript
javascript实现数字倒计时特效
Mar 30 Javascript
利用AngularJs实现京东首页轮播图效果
Sep 08 Javascript
浅谈EasyUi ComBotree树修改 父节点选择的问题
Nov 07 Javascript
Angular.JS中的this指向详解
May 17 Javascript
vue中axios处理http发送请求的示例(Post和get)
Oct 13 Javascript
JavaScript之数组扁平化详解
Jun 03 Javascript
javascript设计模式 ? 工厂模式原理与应用实例分析
Apr 09 Javascript
通过JS判断网页是否为手机打开
Oct 28 Javascript
vue基础知识--axios合并请求和slot
Jun 04 #Javascript
vue webpack build资源相对路径的问题及解决方法
Jun 04 #Javascript
微信小程序学习总结(一)项目创建与目录结构分析
Jun 04 #Javascript
JavaScript冒泡算法原理与实现方法深入理解
Jun 04 #Javascript
js实现无刷新监听URL的变化示例代码详解
Jun 03 #Javascript
JQuery实现折叠式菜单的详细代码
Jun 03 #jQuery
JavaScript字符和ASCII实现互相转换
Jun 03 #Javascript
You might like
《魔兽争霸3》重制版究竟重制了什么?玩家:这么糊弄真的好吗?
2020/05/04 魔兽争霸
PHP自定义函数获取URL中一级域名的方法
2016/08/23 PHP
浅谈ThinkPHP中initialize和construct的区别
2017/04/01 PHP
PHP registerXPathNamespace()函数讲解
2019/02/03 PHP
动态改变textbox的宽高的js
2006/10/26 Javascript
JS获取父节点方法
2009/08/20 Javascript
js实现select组件的选择输入过滤代码
2014/10/14 Javascript
jQuery实现带水平滑杆的焦点图动画插件
2016/03/08 Javascript
js实现的万能flv网页播放器代码
2016/04/30 Javascript
Bootstrap框架下下拉框select搜索功能
2020/03/26 Javascript
JS中使用正则表达式g模式和非g模式的区别
2017/04/01 Javascript
View.post() 不靠谱的地方你知道多少
2017/08/29 Javascript
BootStrap模态框不垂直居中的解决方法
2017/10/19 Javascript
利用node实现一个批量重命名文件的函数
2017/12/21 Javascript
bootstrap自定义样式之bootstrap实现侧边导航栏功能
2018/09/10 Javascript
JavaScript表格隔行变色和Tab标签页特效示例【附jQuery版】
2019/07/11 jQuery
JS正则表达式验证端口范围(0-65535)
2020/01/06 Javascript
JS检测浏览器开发者工具是否打开的方法详解
2020/10/02 Javascript
[01:08:10]2014 DOTA2国际邀请赛中国区预选赛 SPD-GAMING VS LGD-CDEC
2014/05/22 DOTA
Python基于回溯法子集树模板解决野人与传教士问题示例
2017/09/11 Python
python增加矩阵维度的实例讲解
2018/04/04 Python
mac下如何将python2.7改为python3
2018/07/13 Python
css3实现wifi信号逐渐增强效果实例
2017/08/09 HTML / CSS
html5 figure和figcaption的使用方法
2018/09/10 HTML / CSS
工程测量与监理专业应届生求职信
2013/11/27 职场文书
社区国庆节活动方案
2014/02/05 职场文书
优秀毕业生推荐信范文
2014/03/07 职场文书
2014年财政工作总结
2014/12/10 职场文书
乔迁之喜答谢词
2015/01/05 职场文书
幼儿园小班个人工作总结
2015/02/12 职场文书
护士个人年终总结
2015/02/13 职场文书
2015年教师师德师风承诺书
2015/04/28 职场文书
项目投资意向书范本
2015/05/09 职场文书
Nginx配置之实现多台服务器负载均衡
2021/08/02 Servers
mapstruct的用法之qualifiedByName示例详解
2022/04/06 Java/Android
Android Canvas绘制文字横纵向对齐
2022/06/05 Java/Android