Оптимизация производительности внешнего интерфейса - ленивая загрузка изображений (анти-дрожание, дросселирование)

оптимизация производительности

Ленивая загрузка сценариев использования

В некоторых сценариях с большим количеством изображений (домашняя страница электронной коммерции, домашняя страница апплета и т. д.), если мы загрузим все изображения при открытии страницы, это неизбежно приведет к зависанию страницы и появлению белого экрана, что создаст у пользователей неприятные ощущения. опыт, что приводит к оттоку пользователей.

Но давайте подумаем, действительно ли пользователям нужно, чтобы мы отображали все картинки вместе? На самом деле это не так, то, что видит пользователь — это только содержимое видимой области браузера. Итак, исходя из этой ситуации, мы можем сделать некоторую оптимизацию, отображать изображения только в видимой области пользователя, а затем запрашивать отображение их для пользователя, когда пользователь запускает прокрутку.

Идея ленивой загрузки

  • imgТеги имеют настраиваемые атрибутыdata-src
  • Над сгибом отображаются изображения в пределах видимой областиsrcзначение заменяется наdata-src
  • Прокрутите изображения, которые появляются в видимой области, для мгновенного отображения (повторите шаг 2)

Реализация ленивой загрузки

<style>
    html,
    body {
        width: 100%;
        height: 100%;
    }

    .containr {
        width: 100%;
        height: 100%;
        overflow-y: auto;

    }

    .img {
        width: 100%;
        height: 300px;
    }

    .pic {
        width: 100%;
        height: 300px;
    }
</style>

<div class="containr">
        <div class="img">
            <img class="pic" src="" alt="" data-src="./imgs/1.png">
        </div>
        <div class="img">
            <img class="pic" src="" alt="" data-src="./imgs/2.png">
        </div>
        <div class="img">
            <img class="pic" src="" alt="" data-src="./imgs/3.png">
        </div>
        <div class="img">
            <img class="pic" src="" alt="" data-src="./imgs/4.png">
        </div>
        <div class="img">
            <img class="pic" src="" alt="" data-src="./imgs/5.png">
        </div>
        <div class="img">
            <img class="pic" src="" alt="" data-src="./imgs/6.png">
        </div>
        <div class="img">
            <img class="pic" src="" alt="" data-src="./imgs/7.png">
        </div>
        <div class="img">
            <img class="pic" src="" alt="" data-src="./imgs/8.png">
        </div>
        <div class="img">
            <img class="pic" src="" alt="" data-src="./imgs/9.png">
        </div>

</div>
    
<script>
        // 获取所有图片的数组
        const imgs = document.querySelectorAll('.containr .pic')
        // 获取父元素
        const containr = document.querySelector('.containr')
        // 获取可视区域高度
        const viewHeight = window.innerHeight
        
        const load = lazyLoad()
        // 首屏渲染
        load()
        function lazyLoad() {
            // 运用闭包 count 进行计数 避免已显示的图片重复参与循环
            let count = 0
            return () => {
                for (let i = count; i < imgs.length; i++) {
                    // getBoundingClientRect()获取返回元素的大小及其相对于视口的位置
                    // 获取第i张图片是否在可视区域
                    let distance = viewHeight - imgs[i].getBoundingClientRect().top
                    if (distance >= 0) {
                        // 图片在可视区域时设置图片的src 为 当前元素 data-src
                        imgs[i].src = imgs[i].getAttribute('data-src')
                        // 图片已被显示,下次从count + 1 张开始检查是否在可视区域
                        count += 1
                    }
                }
            }
        }
        // 添加滚动事件触发加载
        containr.addEventListener('scroll', load, false)

</script>

Пока мы изначально завершили нашу ленивую загрузку, но, как мы все знаем,scrollЭто событие слишком легко срабатывает, как только пользователь прокручивает мышь, оно будет срабатывать много раз.Если вы продолжите прокручивать, это неизбежно приведет к повторному срабатыванию нашего события, что также приведет к резкому падению нашей производительности. , так что это приводит к нашему миксу.телоДроссельная защита от сотрясенийдля оптимизации нашей работы.

Стабилизатор и дроссель

Стабилизатор: в определенный период времени запускается несколько событий, распознается только первое инициированное событие, и событие выполняется по истечении времени.

 function debounce(fn, time) {
            let oldTime = 0;
            return () => {
                const nowTime = new Date()
                if (nowTime - oldTime >= time) {
                   fn()
                   oldTime = nowTime
                }
            }
        }

дросселирование: в определенный период времени запускать несколько событий, распознавать только последний триггер и сбрасывать время, а также выполнять событие по истечении времени.

function throttle(fn, time) {
            let timer = null
            return () => {
                if (timer) {
                    clearTimeout(timer)
                }
                timer = setTimeout(() => {
                    fn()
                }, time);
            }
        }

это здесьthrottleСуществует серьезная проблема, заключающаяся в том, что если пользователь продолжает инициировать события, он никогда не получит ответа, поэтому мы можем использовать идею защиты от сотрясений для оптимизации регулирования.

 function debounce(fn, time) {
            let oldTime = 0,
                timer = null;
            return function () {
                const nowTime = new Date()
                 // 保留调用时的this上下文
                let context = this
                // 保留调用时传入的参数
                let args = arguments
                if (nowTime - oldTime < time) {
                    if (timer) {
                        clearTimeout(timer)
                    }
                    timer = setTimeout(() => {
                        oldTime = nowTime
                        fn.apply(context, args)
                    }, time);
                } else {
                    // 用户重复触发,到达事件节点 还是会去执行事件 
                    oldTime = nowTime
                    fn.apply(context, args)
                }
            }
        }

Итак, в конце концов, мы можем изменить событие прослушивания наcontainr.addEventListener('scroll', debounce(load, 1000), false), что полностью соответствует цели нашей оптимизации.

Добро пожаловать, чтобы оставить сообщение для руководства, спасибо.