Hungry? App is the main image display on new retail projects, guide the user clicks the wheel was broadcast ads or store the goods list into the specified page, so the page contains a large number of images, such as a search box below the shuffling of promotional ads, the central part of the bar and shop at the bottom of the list, show in these areas there are a lot of pictures. Therefore, the loading speed of images directly affects the loading speed of pages. The following will explain how to optimize the loading of new retail pictures from two aspects: the problems and causes of image loading and the solutions.
All data and images in this article were obtained through Charles analog 256 KBPS ISDN/DSL network environment. Only bitmaps are considered in this case, so all images mentioned in the text refer to bitmaps and not vector images.
Problems and causes of image loading
Problem 1: Loading too many images when launching the page
Figure 1: Waterfall of new retail image requests
Cause analysis: Startup, as shown in the above page loading about 49 images (specific graphics will change according to the back-end data returned), and these pictures request is almost simultaneous, in Chrome, for the same domain name, up to support concurrent requests, six other requests will be pushed to the queue waiting or stagnant, No new requests are issued in the queue until one of the six requests has been completed. In the waterfall image above, in the green marker box, we see white horizontal columns of different lengths. These are the queuing times of the requested image resources.
Problem two: part of the picture volume is too large
Figure 2. An image loading diagram in the top rotation diagram
Cause analysis: As shown in Figure 1, in the red box is an image in the round broadcast advertisement at the bottom of the search box. It can be seen from Figure 2 that this image is mainly spent in the Conent Download stage. It takes 13.50 seconds in the download phase. The total time of the request is 13.78 seconds. The reason for this problem can also be seen from Figure 1. The image size is 76.2KB, which directly leads to a long time to download the image.
Front-end solutions
A solution to problem one
As the new retail home page displays a large number of pictures, in fact, most of the 49 pictures are not required by the first screen, so we can delay the loading of pictures that are not needed by the first screen, and give priority to the loading of pictures required by the first screen. Here the meaning of the first screen refers to the area within the screen window when opening the new retail home page.
To determine whether an image is in the first screen, the first thing that comes to mind is to get the position information of the image through the getBoundingClientRect method and determine whether the image is in the viewPort. The possible code is as follows:
const inViewport = (el) => {
const rect = el.getBoundingClientRect()
return rect.top > 0
&& rect.bottom < window.innerHeight
&& rect.left > 0
&& rect.right < window.innerWidth
}
Copy the code
In our project, however, we didn’t use this method to determine whether the DOM element was in the first screen, because we didn’t know it was in the first screen until the DOM element was inserted into the DOM tree and the page was rearranged and redrawn. In our project we used the V-IMG directive, which the new retail project uses to load images and hash them into urls. [insert], contains two hook functions bind and INSERTED in the Vue directive. The official website explains the two hook functions as follows:
bind
: called only once, the first time a directive is bound to an element. This is where you can perform one-time initialization Settings.inserted
: called when the bound element is inserted into the parent node (the parent node is guaranteed to exist, but not necessarily inserted into the document).
[insert] We can only get the position of the element in the inserted hook function, and determine if the element is in the first screen. In a new retail project, we tested that the time lag between the two hook functions is about 200ms, so if an image is loaded inside the inserted hook function, it will be about 200ms later than if the image is loaded inside the bind hook function. The 200ms is more than enough for many images to load, so we eventually dropped the idea of loading the first screen image in the Inserted hook function.
If an element is not inserted into the DOM tree and rendered, how can you tell if it is in the front screen?
<img v-img=”{ hash: ‘xxx’, defer: true }”>
The layout of the new retail page is determined. Below the rotation advertising bar is the promotion bar and below is the store list. The height of these components is also relatively fixed, so we know in advance whether these components are on the first screen or not. Therefore, when the V-IMG command is actually used, v-IMG is told which images need to be loaded in advance and which images need to be loaded after the images loaded in advance are finished by passing the configuration item defer. This allows us to load the first images in the bind hook function. For example, images of the rote casting component, promotional component and display images in the first two stores need to be loaded first, and other images need to be loaded after the first screen images are fully loaded. The actual implementation code is as follows:
Const promises = [] // Vue. Directive ('img', {bind(el, binding, vnode) {//... const { defer } = binding.value // ... if (! defer) { promises.push(update(el, binding, vnode)) } }, inserted(el, binding, vnode) { const { defer } = binding.value if (! defer) return if (inViewport(el)) { promises.push(update(el, binding, vnode)) } else { Vue.nextTick(() => { Promise.all(promises) .then(() => { promises.length = 0 update(el, binding, vnode) }) .catch(() => {}) }) } }, // ... })Copy the code
First, declare an array promises to store the loaded images first. Inside the bind hook function, if the defer configuration item is false, it will load the images inside the bind hook function. And push the returned promise into promises array. Inserted Hook function, images that are lazy to load (defer is true), but are in the first screen, also have priority to load when the Inseted hook function is called. For non-first screen and lazy loading images wait until all images in Promises array are loaded. Of course, in real code, there would be fault-tolerant mechanisms, such as one of the above images failing to load or taking too long to load. So we can configure a maximum wait time.
The optimized image loading waterfall diagram is as follows:
Figure 3. Waterfall diagram with images loaded on demand
As shown above, the image in the red box below is not the first screen image, so it is lazily loaded. As you can see, it was loaded after all the images above, including the one that took the longest in the red box above, had loaded. This reduces network consumption during the first screen load and improves image download speed.
Comparison before and after optimization
Through the above optimization scheme, under the preset network environment (see note at the end of the paper), 5 times of parallel cache clearing loading were carried out respectively before and after optimization, with the average data as follows:
As can be seen from the table above, DOMContentLoaded and Loaded are not of much reference value. The time required for a complete display of the first screen is still determined by the image that loads the slowest (usually the image with the largest volume), that is, the Max_size_image in the table above. As can be seen from the table above, After optimization, the loading time of maximum volume image is shortened by 5.74s compared with that before optimization. That’s 41.41 percent faster. The change in loading speed of the slowest images also reflects the change in the first screen time.
Of course, the above data does not fully reflect the online scenario, after all, the test time and back-end data are different. We cannot simultaneously collect data before and after optimization at the same time and in the same network environment.
There are some follow-up solutions to problem 1:
- Under HTTP/1.0 and HTTP/1.1, Chrome supports only six concurrent requests from the same domain. You can use domain name splitting to increase the number of concurrent requests, or use HTTP/2.
The solution to problem two
The image size is too large, resulting in a long download time. Use smaller images while maintaining clarity. The volume of an image is determined by two factors: the total number of pixels in the image and the number of bytes required to encode each pixel. Therefore, the file size of an image is equal to the total number of pixels of the image multiplied by the number of bytes required to encode per pixel, which is the following equation:
FileSize = Total Number Pixels * Bytes of Encode single Pixels
Here’s an example:
A 100px * 100px image contains 100 * 100 = 10000 pixels, and each pixel is stored by RGBA color value. R\G\B\A has 0~255 values for each color channel, i.e. 2^8 = 256. Exactly 8 bits 1byte. Each pixel has four channels, and each pixel requires 4bytes. Therefore, the image size is 10000 x 4bytes = 40000bytes = 39KB.
With this background, we know how to optimize an image in two directions:
- One is to reduce the number of bytes per pixel
- The other is to reduce the total number of pixels in an image
Unit pixel optimization: Unit pixel optimization also has two directions, one direction is “lossy” delete some pixel data, the other aspect is to do some “lossless” image pixel compression. As mentioned in the example above, the RGBA color value can represent 256^4 colors, which is a large number, and usually we don’t need that many color values, so can we reduce the number of colors in the palette? This reduces the number of bytes per pixel. Lossless compression is an algorithm that minimizes image storage volume while storing pixel data. Such as a picture of a pixel and its surrounding pixels are close together, such as a picture of a blue sky, so we can store two pixel color values of difference (of course the actual algorithm may take more than two pixels maybe more), so that not only ensure the pixel data “condition”, but also reduce the storage volume. However, it also increases the cost of image decompression.
Different image formats, JPEG, PNG, GIF, webP, are derived from the optimization of the unit pixel. Different image formats have their own algorithms for reducing the volume per pixel. There are also advantages and disadvantages, such as JPEG and PNG do not support animation effects, JPEG images are small but do not support transparency, etc. Therefore, the project’s strategy in selecting the image format is to choose the smallest image format to meet their needs. The WebP format, which has been used uniformly in the new retail project, is 30% smaller than the JPEG format, and also supports animation and transparency.
Image pixel total optimization:
Figure 4: Image loading size vs. actual rendered size
This is one of the images shown in Chrome’s iPhone 6 emulator rotation of the new retail category page. The image is 750 by 188 pixels, but the actual image is 1440 by 360 pixels, which means we don’t need it at all. Large images not only increase the loading time of images (more on that later), but also increase the CPU burden due to image size reduction.
As mentioned above, we used the V-IMG command to load the required pictures in the project. If we could load pictures of different sizes (different total number of pixels) according to the size of the device, that is to say, we could use small pictures as far as possible on the premise of ensuring the clarity of the pictures, the problem would be solved. In this project, we use the picture service of Qiniu, which provides image processing functions such as image format conversion and size cutting. We only need to add the image width and height configuration to the V-img command, so can we load different image sizes for different devices?
In the project, we used lib-flexible to adapt to different mobile devices. The Lib-flexible library added two attributes, data-dPR and style, to the HTML element of our page. We’ll use the font-size attribute here, which is exactly one-tenth the width of the HTML element within a certain range of devices. We can use the style attribute to approximate the width of the device. At the same time, the design is based on iPhone6, that is, the design is 750px wide design, so that the size of the image in the design can be converted to the image size required by other devices.
Here’s an example:
A 200px wide image of the iPhone 6 is 750px wide. We calculated the width of the iPhone6 plus to be 1242px using the style attribute of the HTML element. This will allow you to calculate the image size required for the iPhone6 plus. The calculation is as follows:
200 * 1242/750 = 331.2px
The implementation code is as follows:
const resize = (size) => { let viewWidth const dpr = window.devicePixelRatio const html = document.documentElement const dataDpr = html.getAttribute('data-dpr') const ratio = dataDpr ? (dpr / dataDpr) : dpr try { viewWidth = +(html.getAttribute('style').match(/(\d+)/) || [])[1] } catch(e) { const w = html.offsetWidth if (w / dpr > 540) { viewWidth = 540 * dpr / 10 } else { viewWidth = w / 10 } } viewWidth = viewWidth * ratio if (Number(viewWidth) >= 0 && typeof viewWidth === 'number') { return (size * viewWidth) / 75 // 75 is the 1/10 iphone6 deivce width pixel } else { return size } }Copy the code
The resize method above is used to convert the configured width and height values into the actual required image size. In other words, the size parameter is the size in the iPhone 6 design draft, and the return value of resize is the size required by the current device, and then the size is configured into the image server’s transmission parameter. So we can get the image tailored to the device.
Effect is compared before and after optimization, the basis of the above, we in the Chrome different mobile terminal emulator, for our new retail category pages in the largest advertising photo in loading the data statistics of different equipment (three times in parallel to empty the cache loading), why did you choose the picture of the largest, have said above, It determines how long the first screen takes.
In the table above, except for the last line, which is not optimized for loading data, from top to bottom, the screen size of the device has gradually increased, and the size of the loaded images has also increased from 23.2 KB to 65.5 KB. The loading time and download time also increase with the increase of image size. The line graph below can better reflect the positive correlation between image size, loading time and download time. TTFB (the time required from sending the request to receiving the first byte) has no significant positive correlation with the size of the picture, and there may be little difference in the time required by the picture server to crop the above images of different sizes.
Figure 5: File size, load time and download time line changes when loading the same image on different devices
It can also be seen from the top line chart that the effect is particularly obvious for devices with small screens. Without optimization, the loading time of images in iPhone5 is 14.85s, but after optimization, the loading time is shortened to 3.90s. The loading time was reduced by a whopping 73.73%. The iPhone6 plus has a 26.00% duration optimization for the larger screen.
Of course, the above data is established in 256 KBPS ISDN/DSL network environment, in this low-speed network environment, the loading time of the image is mainly determined by the download time, so the image volume can be optimized to achieve good results. In 4G (Charles simulation) environments, the optimizations in iPhone5 were somewhat discounted and the loading time was reduced by 69.15%. In fact, it is easy to imagine that TTFB will have a greater impact on load time in a high-speed network environment than in a low-speed network environment.
The final summary
The above research and data results show that the optimization strategy for slow loading of new retail images is as follows:
- The first screen image is loaded first, and the non-first screen image is loaded only after the first screen image is fully loaded.
- Most of the pictures, especially the pictures in the round broadcast advertisement, are cut according to the size of the device to reduce the size of the picture, reduce the network overhead and speed up the download rate.
This article does not discuss the details of the code implementation, but focuses on the reasons for the slow loading of images, as well as the data analysis of the comparison before and after optimization. For more details of the code, please go to VUE-img.