Vue-lazyload Principle Description
The basic function is: loading images in the visible area, other images can temporarily have a placeholder loading map, and then request real images and replace them when they are rolled to the visible area.
Principle brief:
- Vue-lazyload is implemented in the form of directives, which are defined as V-lazy directives. In index.js, a custom directive is registered through vUE’s plug-in (install)
v-lazy
. - When an instruction is bind, it creates a listener, which is only responsible for state control, performing different tasks in different states. In this case, the loading graph is loaded. If the filter provides the progressive image mode, the thumbnail image is loaded first.
- The listener created in the previous step will be added to the listener queue and searched for the target DOM node, that is, the parent node whose overflow is closest to Scroll or Auto, or window. Register DOM events (listenEvents, such as Scroll events) for it.
- In the dom event callback above, the listener queue is iterated to determine whether the DOM bound to the Listener is in the perload position of the page, and if so, loadImageAsync() asynchronously loads the resource for the current image.
- At the same time, the listener will trigger the current DOM rendering function in loading, loaded, and error states during the loading process of the image, and render dom contents in the three states respectively
data-src
Value assigned to imgsrc
In the.
Vue-lazyload can be used to meet business requirements in most scenarios. However, I encountered a situation, actually can not work very well.
Failure Scenario Description
The H5 page developed by the author is embedded in the Webview of app, including ios and Android, and located in a second-level TAB under the first-level TAB. In order to improve the user experience of this page, the client uses preloading respectively. Ios preloads the H5 page when clicking this level TAB, while Android preloads the page when opening the app.
During a first screen optimization, in order to improve the performance of the component, the unused code of the component is stripped and the scope of listenEvents events is reduced.
const IMG_REG = /cdn1/; Vue.use(VueLazyload, {preLoad: 1.3.attempt: 3.error: errorImg,
// The first screen just happens to have a multicast graph. Switching a multicast graph will trigger the transitionEnd event
listenEvents: ['scroll'.'transitionend'].// Image listener filtering is performed once each image is loaded.
filter: {
progressive (listener, options) { // implement asymptotic image loading (load fuzzy image first)
const { src } = listener;
if (IMG_REG.test(src)) {
listener.el.setAttribute('lazy-progressive'.'true');
// Load the fuzzy graph first
let loadingUrl = listener.src + '/thumb1';
if (options.supportWebp) {
loadingUrl += '? imageMogr2/format/webp';
}
listener.loading = loadingUrl;
// Finally load the clearer graph
listener.src += '/thumb2';
}
},
webp (listener, options) { // Load the webP diagram
if(! options.supportWebp)return
const { src } = listener;
if (IMG_REG.test(src)) {
listener.src += '? imageMogr2/format/webp'; }}}})Copy the code
The test environment and pre-release environment were verified and successfully launched.
I thought it was over until a few days later, some colleagues reported that the ios online environment, after the completion of pre-loading, entered the TAB for the first time, the picture did not load, only to scroll or refresh the page, the picture can be loaded.
At this point, I have black question marks. There is no problem with online verification on Android, why is there a problem with ios?
To quickly solve
Online issues that affect user experience are of urgent importance. It is therefore a priority.
The checkInView() of the listener is false, so the image is not loaded. When the TAB is clicked, an event is triggered. When the event is listened, the load() method of lazyLoadHandler is executed. Load the image.
I quickly fixed the problem by letting go of the listening events and using the default events.
warren
But why? Why can you load the image when you release the listening event? Why can Android load the image when you don’t release the event? Why didn’t Android load the image a few days ago? Why is the test environment ok and the online environment not?
In order to facilitate debugging on the mobile side, vconsole-webpack-plugin is added to assist analysis. After perusing the vue-lazyload source code, add the event type and whether console.log is in view in _lazyLoadHandler().
After a hundred million little bit of exploration, finally solved the mystery.
First, before releasing the monitoring event
1. Why can Android load images but iOS can’t?
When the H5 page is preloaded in the background, the getBoundingClientRect() method gets the dom element values correctly on Android, so the checkInView() is true and the image is loaded in the background. On ios, the getBoundingClientRect() method returns a dom element value of 0, so checkInView() is false and the image is not loaded in the background.
2. Why did iOS load images in the beginning and why not later?
According to the above analysis, the theory is not loaded a few days ago. But why is it okay?
According to the source code, when the DOM element is first bound and newListener is generated, the filter() method is executed, and the progressive and webp methods in the configuration item above are executed in sequence. At this time, img loads the loading map. In progressive, the loading map is set to a thumbnail, so what you see in the online environment is XXX.png /thumb1? ImageMogr2 /format/ WebP thumbnail.
In theory, there’s something wrong with the online environment, but you can’t tell it’s occupied by the thumbnails.
Later, the operation students updated the page content on the new management back platform, and the online stock content was configured on the old management back platform, including different interfaces for uploading pictures and different CDN for returning image URLS. Therefore, if (img_reg.test (SRC)) is false. The loading map cannot be set to a thumbnail, so the image cannot be seen in the online environment.
The right solution for const IMG_REG = / (cdn1 | cdn2) /, where you can see thumbnail placeholders.
Two, after the release of monitoring events
Why is the ios online environment back to normal?
I explored the question for a long time, but could not get an answer. The default listening events for vue-lazyLoad are ‘Scroll ‘, ‘wheel’, ‘mousewheel’, ‘resize’,’ animationEnd ‘, ‘TransitionEnd ‘,’ touchMove ‘, Which listener event was triggered?
Antecedents feed
When rewriting vue-lazyload to remove unwanted code and throttle, I used a tool function that I had been using locally, as follows:
const throttle = (fn, delay, atleast) = > {
let timer = null;
let previous = null;
return function () {
let now = +new Date(a);if(! previous) previous = now;if (atleast && now - previous > atleast) {
fn();
previous = now;
clearTimeout(timer);
} else {
clearTimeout(timer);
timer = setTimeout(function () {
fn();
previous = null; }, delay); }}; };Copy the code
The official throttle function is:
function throttle (action, delay) {
let timeout = null
let lastRun = 0
return function () {
if (timeout) {
return
}
let elapsed = Date.now() - lastRun
let context = this
let args = arguments
let runCallback = function () {
lastRun = Date.now()
timeout = false
action.apply(context, args)
}
if (elapsed >= delay) {
runCallback()
} else {
timeout = setTimeout(runCallback, delay)
}
}
}
Copy the code
At first glance, they seem similar, but here’s the problem.
Blotting out the sun cloud
In theory, after letting go of listening events, I should be able to know in vConsole which event was triggered when I switched to the TAB for the first time after preloading, and then the matter should be resolved.
In the test environment, I replaced the utility functions with the official ones, including the throttling functions, and then let go of the listening events. After the preloading is complete, the first time I switch to the TAB, the image still does not load.
Then why let go of the line??
So lost more and more far…
Just beginning
After a careful study of the implementation of the next two throttling functions, I found that the official throttling function will execute immediately, while my personal throttling function, because atleast has no arguments, did not execute immediately at first.
The bottom of
LazyLoadHandler is placed in the throttling function. If the throttling function is executed immediately after the first TAB entry, and the page is moved from background to foreground, the checkInView() is false, so the image cannot be loaded. Wait until the page is switched from background to foreground, and then execute the callback, at which point checkInView() is true, and the image loads.
Change the test environment throttling function back to its own, and make adjustments, mainly for pass-through parameters, as follows:
const throttle = (fn, delay, atleast) = > {
let timer = null;
let previous = null;
return function (. args) {
let now = +new Date(a);const context = this;
if(! previous) previous = now;if (atleast && now - previous > atleast) {
fn.apply(context, args); // Passthrough parameters
previous = now;
clearTimeout(timer);
} else {
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args); // Passthrough parameters
previous = null; }, delay); }}; };Copy the code
After the TAB is preloaded, iOS will trigger the resize event for the first time. Android will not trigger the resize event. After a 200ms delay lazyLoadHandler() is executed, the page has switched to the foreground, checkInView() is true, and the image is loaded.
conclusion
- Jue-lazyload is responsible for dom-related processing, including creating dom listeners, registering DOM events for targets, and rendering dom. Listener.js is only responsible for state control and performs different services in different states.
- The official throttling function throttle() is fine to execute immediately in most scenarios, but in my case it is.