Front End Performance Optimization-Inert Loading

  javascript, performance optimization

图片描述

Starting from the demand:

In the actual project development, I met a demand like this: a page module has a lot of list data display, each data has pictures, and the first display only needs less than 10 pictures, so should we load all the pictures at once? Obviously, this is wrong, not only affecting the rendering speed of the page, but also wasting bandwidth (because the list needs to be dragged and sorted, all the lists need to be loaded, and paging cannot be done). We can download when the browser scrolls to a certain position, which is what we usually call lazy loading. technically, the technology to be used is lazy loading of pictures-reloading in the visible area.
图片描述

Implementation plan:

1. By default, images are not loaded, only placeholders are loaded.
2, component scroll bar changes
3, calculating a visible area and triggering conditions
4. < img > tag src attribute load resource

Knowledge points:

ScrollTop: scroll height of outline elements
OffsetTop: The distance of the element from the nearest border of the positioning element that contains the element (has the position attribute and is not static). If there is no positioned element, the default body.
OffsetHeight: It returns the pixel height of the element, which contains the vertical inner margin and border of the element, and is an integer.
Calculation: the height of the visible area (offsetHeight)+scroll top > = the distance of the element from the outline (offsetTop)-offset (loaded in advance)
图片描述

Code implementation:

Page structure

<style type="text/css">
 .container{
 width:200px;
 height:200px;
 position:relative;
 overflow-y:scroll;
 }
 .img-area{
 width:100px;
 height:100px;
 }
 </style>
 <div class="container">
 <div class="img-area">
 <img class="pic" alt="loading" data-src="./img/img1.png" src="image-placeholder-logo.svg">
 </div>
 <div class="img-area">
 <img class="pic" alt="loading" data-src="./img/img2.png" src="image-placeholder-logo.svg">
 </div>
 <div class="img-area">
 <img class="pic" alt="loading" data-src="./img/img3.png" src="image-placeholder-logo.svg">
 </div>
 <div class="img-area">
 <img class="pic" alt="loading" data-src="./img/img4.png" src="image-placeholder-logo.svg">
 </div>
 <div class="img-area">
 <img class="pic" alt="loading" data-src="./img/img5.png" src="image-placeholder-logo.svg">
 </div>
 </div>

The src attribute uses a placeholder picture, and the alt attribute is the replacement text when the image cannot be displayed. Data-src is a custom attribute used to store the actual picture address, which can be accessed through HTMLElement.dataset
Script code:

var container = document.querySelector('.container');
 container.onscroll = function(){
 checkImgs();
 }
 function isInSight(el) {
 var sTop = container.scrollTop;
 var oHeight = container.offsetHeight;
 var oTop = el.offsetTop;
 return sTop + oHeight > oTop;
 }
 function checkImgs() {
 var imgs = document.querySelectorAll('.pic');
 Array.from(imgs).forEach(el => {
 if (isInSight(el)) {
 loadImg(el);
 }
 })
 }
 function loadImg(el) {
 var source = el.dataset.src;
 el.src = source;
 }
 checkImgs();

It can be seen that when the page is loaded, the scroll event bound to the outer frame, as the user scrolls down the mouse and assigns the src of img to a new value, the network re-initiates the request and pulls the picture. There should be some areas that can be optimized, such as
1. You can only monitor the events when scrolling down, and set the delay (using the intercept function) to prevent the callback function from being called many times.
2. You can set an identifier to identify the index of the loaded pictures. When the scroll bar scrolls, you do not need to traverse all the pictures but only the unloaded pictures.
3. During calculation, offset data can be added, pictures can be loaded in advance, and fade-in effect can be used to improve smoothness.

Another calculation method:

The getClientRects () method returns a set of rectangles, that is, the CSS border set associated with the element. Read-only attributes left, top, right, and bottom that contain borders, in pixels. All attributes except width and height are relative to the position of the upper left corner of the viewport.
图片描述

Under this condition, suppose bound = el.getBoundingClientRect (), as the scroll bar scrolls downward, bound.top will become smaller and smaller, that is, the distance between the picture and the top of the visible area will become smaller and smaller. When bound.top===clientHeight, the top edge of the picture should be the critical point at the position of the lower edge of the visible area, and if you scroll a little more, the picture will enter the visible area.
In other words, when bound.top<=clientHeight, the picture is in the viewable area.

function isInSight(el) {
var bound = el.getBoundingClientRect();
var clientHeight = window.innerHeight;
return bound.top <= clientHeight;
}

Further consideration:

The above listens to scroll and calculates the element position to realize inert loading. When the data reaches a certain amount, event binding and loop position calculation will consume a lot of performance. Every call to getBoundingClientRect () will force the browser to recalculate the layout of the entire page, which may cause considerable flicker to your website. This method has some shortcomings.

Cross observer:

This is why the IntersectionObserver was created. It is an api added to HTML5. It can detect whether an element is visible or not. The IntersectionObserver can let you know when an observed element enters or leaves the viewport of the browser.
图片描述

It has limited compatibility.
Chrome 51+ (released on May 25, 2016)
Android 5+ (Chrome 56 Released on February 06, 2017)
Edge 15 (2017-04-11)
IOS does not support

However, don’t worry, WICG provides a polyfill that is compatible with the following versions:
图片描述

Its usage is also very simple, similar to observe in rxjs.

var observe = new IntersectionObserver(callback, option);

IntersectionObserver is a constructor provided by the browser and accepts two parameters: callback is the callback function when visibility changes, and option is the configuration object (optional). Returns an observation instance observe that specifies which DOM node to observe.

//Start observation
 observe.observe(document.getElementById('example'));
 callback = function(entries){
 entries.forEach((entry) => {
 if (entry.isIntersecting) {
 //Start to enter, cross state, and process picture logic here.
 } else {
 //Has entered or left completely
 }
 });
 }
 //Stop observation
 observe.unobserve(element);
 
 //Close the observer
 observe.disconnect();

Entries is an array, and each member is an IntersectionObserverEntry object. For example, if the visibility of two observed objects changes at the same time, the entries array will have two members. IsIntersecting, which returns a Boolean value and true if the target element intersects the root of the intersection area observer object. If true is returned, the state at the time of transition to intersection is described; If false is returned, it can be judged from this that the transition is from the crossed state to the non-crossed state.

The IntersectionObserverEntry object provides many useful attributes. For example, target is the observed target element and is a DOM node object. intersectionRatio is the visible ratio of the target element, that is, the ratio of the visible area and the total area of the DOM node. When completely visible, it is 1 and when completely invisible, it is less than or equal to 0. This attribute can be used to set the transparency of the picture and make it fade out.

Drop-down unlimited scrolling:

图片描述

There is a loading status label at the bottom of the page. Once the tag is visible, it means that the user has reached the bottom of the page, thus loading a new entry in front of the tag. The advantage of this is that it saves a lot of performance consumption than listening to scroll and computing, and the existing IntersectionObserver can be applied very simply. The following is the implementation method:

var intersectionObserver = new IntersectionObserver(
function (entries) {
//If not visible, return
if (entries[0].intersectionRatio <= 0) return;
//Load new data here
});
intersectionObserver.observe(document.getElementById('loading'));

Summary:

The inert loading of pictures (not only pictures, but mainly the resources occupied by pictures are the most common) is a web page optimization technology. Through comparison of various schemes, the picture is loaded only when it appears in the browser’s current window, thus reducing the number of requests for the first screen picture, optimizing the front-end performance and improving the user experience. No matter which method has its own advantages and disadvantages, mastering the principle and flexible application are the most important. This is a summary of the problems encountered in the development and the solutions. It is all the experience gained from actual combat. Please give your advice on the unclear or incorrect descriptions.