实现一个Vue自定义指令懒加载

更新日期: 2020-05-08阅读: 2.1k标签: 懒加载
文章首次发表在 个人博客

什么是图片懒加载

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

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

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



为什么要做图片懒加载

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

总结出来就两个点:

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

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


懒加载原理

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

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


懒加载实现

实现效果图:


<!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 >"https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657907683.jpeg">
        <img >"https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657913523.jpeg">
        <img >"https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657925550.jpeg">
        <img >"https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657930289.jpeg">
        <img >"https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657934750.jpeg">
        <img >"https://cdn.suisuijiang.com/ImageMessage/5adad39555703565e79040fa_1590657918315.jpeg">
    </div>
</body>

</html>

监听 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获取

滑到最后的时候刷新,会看到所有的图片都加载了

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]);
     }
 }


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>


链接: https://fly63.com/article/detial/9258

监听滚动条,实现数据懒加载

当滚动条滚动到底部时,加载新数据;需要根据距离底部的位置加载数据,考虑到新数据进来会使滚动条距离底部的位置有所改变,避免重复加载数据

vue懒加载

什么是懒加载?懒加载也叫延迟加载,即在需要的时候进行加载,随用随载。在单页应用中,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多

v-lazy图片懒加载

安装插件npm install vue-lazyload --save;在main.js文件中注册插件。配置参数:preLoad提前加载高度(数字 1 表示 1 屏的高度)、error图片加载失败时显示的图片

解决el-tree lazy懒加载时,连续勾选前两个子节点后第二次进入默认选中时,将父节点也勾选的问题

在用到el-tree的懒加载和默认勾选功能时,若第一次勾选前几个连续节点,第二次进入默认勾选时,由于el-tree子节点尚未完全加载(只加载出来前几个),默认勾选已经开始

前端性能优化之图片懒加载

在类电商类项目,往往存在大量的图片,如 banner 广告图,菜单导航图,美团等商家列表头图等。图片众多以及图片体积过大往往会影响页面加载速度,造成不良的用户体验,所以进行图片懒加载优化势在必行。

过度使用懒加载对 Web 性能的影响

如今为了提升应用性能,懒加载被广泛使用于 Web 应用中。它帮助开发者减少网站加载时间,节省流量以及提升用户体验。但懒加载的过度使用会给应用性能带来负面影响

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!