为什么要做图片懒加载,图片懒加载的原理及步骤

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

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

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

为什么要做图片懒加载,图片懒加载的原理及步骤

lazyLoad

为什么要做图片懒加载

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

总结出来就两个点:

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

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

懒加载原理

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

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

懒加载实现

实现效果图:

为什么要做图片懒加载,图片懒加载的原理及步骤

imgLazyLoad

<!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>

1.监听 scroll 事件判断元素是否进入视口

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)        }    } }   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;        }            } } window.addEventListener(‘scroll’, throttle(lazyload, 200));

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

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

2. Intersectionobserver

Intersection Observer API提供了一种异步观察目标元素与祖先元素或顶级文档viewport的交集中的变化的方法。

创建一个 IntersectionObserver对象,并传入相应参数和回调用函数,该回调函数将会在目标(target)元素和根(root)元素的交集大小超过阈值(threshold)规定的大小时候被执行。

var observer = new IntersectionObserver(callback, options);

IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数(即目标元素出现在root选项指定的元素中可见时,回调函数将会被执行),option是配置对象(该参数可选)。

返回的 observer是一个观察器实例。实例的 observe 方法可以指定观察哪个DOM节点。

具体的用法可以 查看 MDN文档

const imgs = […document.getElementsByTagName(‘img’)]; // 当监听的元素进入可视范围内的会触发回调  if(IntersectionObserver) {      // 创建一个 intersection observer      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>

 

推荐Vue学习资料文章:

《基于 Vue 和高德地图实现地图组件「实践」》

《一个由 Vue 作者尤雨溪开发的 web 开发工具—vite》

《是什么让我爱上了Vue.js》

《1.1万字深入细品Vue3.0源码响应式系统笔记「上」》

《1.1万字深入细品Vue3.0源码响应式系统笔记「下」》

《「实践」Vue 数据更新7 种情况汇总及延伸解决总结》

《尤大大细说Vue3 的诞生之路「译」》

《提高10倍打包速度工具Snowpack 2.0正式发布,再也不需要打包器》

《大厂Code Review总结Vue开发规范经验「值得学习」》

《Vue3 插件开发详解尝鲜版「值得收藏」》

《带你五步学会Vue SSR》

《记一次Vue3.0技术干货分享会》

《Vue 3.x 如何有惊无险地快速入门「进阶篇」》

《「干货」微信支付前后端流程整理(Vue+Node)》

《带你了解 vue-next(Vue 3.0)之 炉火纯青「实践」》

《「干货」Vue+高德地图实现页面点击绘制多边形及多边形切割拆分》

《「干货」Vue+Element前端导入导出Excel》

《「实践」Deno bytes 模块全解析》

《细品pdf.js实践解决含水印、电子签章问题「Vue篇」》

《基于vue + element的后台管理系统解决方案》

《Vue仿蘑菇街商城项目(vue+koa+mongodb)》

《基于 electron-vue 开发的音乐播放器「实践」》

《「实践」Vue项目中标配编辑器插件Vue-Quill-Editor》

《基于 Vue 技术栈的微前端方案实践》

《消息队列助你成为高薪 Node.js 工程师》

《Node.js 中的 stream 模块详解》

《「干货」Deno TCP Echo Server 是怎么运行的?》

《「干货」了不起的 Deno 实战教程》

《「干货」通俗易懂的Deno 入门教程》

《Deno 正式发布,彻底弄明白和 node 的区别》

《「实践」基于Apify+node+react/vue搭建一个有点意思的爬虫平台》

《「实践」深入对比 Vue 3.0 Composition API 和 React Hooks》

《前端网红框架的插件机制全梳理(axios、koa、redux、vuex)》

《深入Vue 必学高阶组件 HOC「进阶篇」》

《深入学习Vue的data、computed、watch来实现最精简响应式系统》

《10个实例小练习,快速入门熟练 Vue3 核心新特性(一)》

《10个实例小练习,快速入门熟练 Vue3 核心新特性(二)》

《教你部署搭建一个Vue-cli4+Webpack移动端框架「实践」》

《2020前端就业Vue框架篇「实践」》

《详解Vue3中 router 带来了哪些变化?》

《Vue项目部署及性能优化指导篇「实践」》

《Vue高性能渲染大数据Tree组件「实践」》

《尤大大细品VuePress搭建技术网站与个人博客「实践」》

《10个Vue开发技巧「实践」》

《是什么导致尤大大选择放弃Webpack?【vite 原理解析】》

《带你了解 vue-next(Vue 3.0)之 小试牛刀【实践】》

《带你了解 vue-next(Vue 3.0)之 初入茅庐【实践】》

《实践Vue 3.0做JSX(TSX)风格的组件开发》

《一篇文章教你并列比较React.js和Vue.js的语法【实践】》

《手拉手带你开启Vue3世界的鬼斧神工【实践】》

《深入浅出通过vue-cli3构建一个SSR应用程序【实践】》

《怎样为你的 Vue.js 单页应用提速》

《聊聊昨晚尤雨溪现场针对Vue3.0 Beta版本新特性知识点汇总》

《【新消息】Vue 3.0 Beta 版本发布,你还学的动么?》

《Vue真是太好了 壹万多字的Vue知识点 超详细!》

《Vue + Koa从零打造一个H5页面可视化编辑器——Quark-h5》

《深入浅出Vue3 跟着尤雨溪学 TypeScript 之 Ref 【实践】》

《手把手教你深入浅出vue-cli3升级vue-cli4的方法》

《Vue 3.0 Beta 和React 开发者分别杠上了》

《手把手教你用vue drag chart 实现一个可以拖动 / 缩放的图表组件》

《Vue3 尝鲜》

《总结Vue组件的通信》

《Vue 开源项目 TOP45》

《2020 年,Vue 受欢迎程度是否会超过 React?》

《尤雨溪:Vue 3.0的设计原则》

《使用vue实现HTML页面生成图片》

《实现全栈收银系统(Node+Vue)(上)》

《实现全栈收银系统(Node+Vue)(下)》

《vue引入原生高德地图》

《Vue合理配置WebSocket并实现群聊》

《多年vue项目实战经验汇总》

《vue之将echart封装为组件》

《基于 Vue 的两层吸顶踩坑总结》

《Vue插件总结【前端开发必备】》

《Vue 开发必须知道的 36 个技巧【近1W字】》

《构建大型 Vue.js 项目的10条建议》

《深入理解vue中的slot与slot-scope》

《手把手教你Vue解析pdf(base64)转图片【实践】》

《使用vue+node搭建前端异常监控系统》

《推荐 8 个漂亮的 vue.js 进度条组件》

《基于Vue实现拖拽升级(九宫格拖拽)》

《手摸手,带你用vue撸后台 系列二(登录权限篇)》

《手摸手,带你用vue撸后台 系列三(实战篇)》

《前端框架用vue还是react?清晰对比两者差异》

《Vue组件间通信几种方式,你用哪种?【实践】》

《浅析 React / Vue 跨端渲染原理与实现》

《10个Vue开发技巧助力成为更好的工程师》

《手把手教你Vue之父子组件间通信实践讲解【props、$ref 、$emit】》

《1W字长文+多图,带你了解vue的双向数据绑定源码实现》

《深入浅出Vue3 的响应式和以前的区别到底在哪里?【实践】》

《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》

《基于Vue/VueRouter/Vuex/Axios登录路由和接口级拦截原理与实现》

《手把手教你D3.js 实现数据可视化极速上手到Vue应用》

《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【上】》

《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【中】》

《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【下】》

《Vue3.0权限管理实现流程【实践】》

《后台管理系统,前端Vue根据角色动态设置菜单栏和路由》

作者: 木子星兮i

转发链接:https://mp.weixin.qq.com/s/36oBZMd-m-2k5EKPghfG3A

本文来自作者:江下科技小助手,不代表小新网立场!

转载请注明:https://www.xiaoxinys.cn/105055.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。