preface
In actual projects, there are still a lot of lazy image loading requirements, especially c-side applications, in some list pages, such as the list of goods, there are a lot of images to display. One optimization we can make is to delay loading of image elements that do not enter the current viewport, reducing the number of requests to enter the page by one.
Lazy loading of images is implemented by not setting SRC for the img element at first, and by loading the image when it is visible during the slide, triggering the SRC setting.
Here is a brief introduction to the implementation of custom directives in VUE. Native JS and React are the same
Common implementation methods:
- Listening to the
onscroll
Scroll event - With the help of
IntersectionObserver
implementation
Listen for onScroll events
By listening for the OnScroll event, when the scroll event is triggered, determine whether the EL element is in the visible area.
The key is how do you tell if an element is in the viewable area?
We can use the getBoundingClientRect method, as explained in MDN:
Element. GetBoundingClientRect () method returns the Element size and its position relative to the viewport. Contains the left, top, right, bottom, x, y, width, and height attributes of the current element, which are read-only in pixels
The viewport defaults to the top left corner of the screen. Load five images to see the printed top valueThe width and height of each image is 300px. It is not hard to see that the top value is the distance from the element to the top of the screen. Slide up slowly and the top value decreases with the sliding distance. So we can draw the simple conclusion:
- when
Top < current viewport height
, can judge the element into the viewport visible area, obtain the viewport height can be combineddocumentElement.clientHeight
和body.clientHeight
, the specific implementation depends on the compatibility requirements of the project, here only give a general idea.
The code reference for determining that an element reaches the viewable area is as follows:
/** * determines whether the element is in view */
export const isElementInViewport = el= > {
if (typeofel.getBoundingClientRect ! = ='function') {
return true
}
const clientHeight = _getClientHeight()
const rect = el.getBoundingClientRect()
return rect.top < clientHeight
}
// Get the viewport height
const _getClientHeight = () = > {
const dClientHeight = document.documentElement.clientHeight
const bodyClientHeight = document.body.clientHeight
let clientHeight = 0
if (bodyClientHeight && dClientHeight) {
clientHeight = bodyClientHeight < dClientHeight ? bodyClientHeight : dClientHeight
} else {
clientHeight = bodyClientHeight > dClientHeight ? bodyClientHeight : dClientHeight
}
return clientHeight
}
Copy the code
Listen to scroll processing
// Create a map to cache
window.lazyMap = new Map(a)// Listen for scrolling events and add throttling
window.onscroll = throttle(() = > {
window.lazyMap.forEach((lazyImg, key) = > {
if (isElementInViewport(lazyImg.el)) {
lazyImg.el.src = lazyImg.value.src
lazyImg.value.callback(lazyImg.el)
window.lazyMap.delete(key)
}
})
}, 200)
Copy the code
Secondly, IntersectionObserver is employed
In the past, it was not easy to detect whether an element was visible or whether two elements intersected, and many of the solutions were unreliable or poor performance, such as the first method, which required listening for page scrolling, which affected performance somewhat
The Intersection Observer API now provides a way to asynchronously detect changes in the Intersection of a target element with an ancestor element or viewport.
Where intersection detection can come into play:
- Image lazy loading – images are loaded only when they are scrolled into view
- Content infinite scrolling – that is, when the user scrolls near the bottom of the content, more is loaded directly, without the user having to turn the page, giving the user the illusion that the web page can scroll indefinitely
- Detect AD exposure – In order to calculate AD revenue, you need to know the exposure of AD elements
- Perform a task or play an animation when the user sees an area
More instructions on IntersectionObserver and how to use the portal
A key attribute used here is isIntersecting, which is a Boolean value indicating whether the target element is converted into an intersection state (true) or out of the intersection state (false). When intersecting, the value of this property is true. Compared to the previous whether to enter the visual area of the judgment, simple is not a little bit 🌹
The core code is
window.observer = new IntersectionObserver(entries= > {
entries.forEach(entry= > {
let lazyImage = entry.target
// Determine whether to intersect
if (entry.isIntersecting) {
const src = lazyImage.getAttribute('data-src')
lazyImage.src = src
lazyImage.style.opacity = 1
lazyImage.style.display = 'block'
// Remove the listener
window.observer.unobserve(lazyImage)
}
})
})
Copy the code
Custom instruction
Well, back to custom directives, if you are not familiar with the use of custom directives please refer to the official Vue documentation portal
Use these hook functions:
- Bind: Called only once, the first time a directive is bound to an element. This is where you can perform one-time initialization Settings.
- Such as listening to the
onerror
Event, set default SRC if loading fails. - Set up the
onscroll
Listening to the IntersectionObserver
The initialization
- Such as listening to the
- Inserted: Called when the bound element is inserted into a parent (the parent is guaranteed to exist, but not necessarily inserted into the document).
onscroll
If el isInViewport, set SRC, otherwiseel
和binding
Added to thelazyMap
IntersectionObserver
The scheme just needs to be calledobserve
Listen on target object
- ComponentUpdated: Invoked when the VNode of the component where the directive resides and its child VNodes are all updated.
- Same inserted, just need to do a SRC judgment, if
value
和oldValue
Consistent, do not proceed
- Same inserted, just need to do a SRC judgment, if
- Unbind: Called only once, when an instruction is unbound from an element.
onscroll
Scheme that takes the current target element fromlazyMap
removeIntersectionObserver
Scheme, callunobserve
Remove target object listening
The final result
conclusion
The existence must have its rationality, so far, the two schemes have their advantages and disadvantages
plan | advantages | disadvantages |
---|---|---|
Based on the onscroll | Good compatibility | Poor performance, need to continuously listen for rolling events; The implementation is not very elegant and the code is heavy |
IntersectionObserver | Good performance, elegant implementation, less code | Poor compatibility, IE does not support it at all, and Safari requires more than 12 support |
MDN has a good article on this, if you are interested, take a look at the portal
The last
If there is not quite reasonable place, I hope to point out in time, more exchanges ~
Talk is always empty, real knowledge comes from practice, check the code please everyone children shoes move to github, don’t forget to click a like yo 👍
Project Address 👉 : github.com/MrLeihe/vue…
The resources
- MDN: developer.mozilla.org/zh-CN/docs/…
- MDN:developer.mozilla.org/zh-CN/docs/…
- Custom command: cn.vuejs.org/v2/guide/cu…