Remember that blog post about lazy image loading? At the beginning, we analyzed the two common image lazy loading scenarios of fixed width and height and fixed width ratio, introduced their solutions, and did some technical selection work.

After a period of project practice, a lot of in-depth optimization work was done on the basis of the previous scheme. As a result, the opening speed of Curiositydaily’s Web page will be reduced to less than 1s, and the traffic consumption of loading three screens of data on the Web and Mobile terminals will also be greatly reduced.




Web page loading under simulated WIFI conditions

This article combines the specific project practice, will focus on how to access the web page faster, detailed to the specific technical scheme, as well as the practice may meet the pit, hope to have certain inspiration and help to everyone.

Why optimize web page loading speed?

Curiosity Daily pursues high quality both in design and content, so rich pictures and texts are mixed into the standard: banner picture of the home page, pictures of the article details page, interesting GIF pictures of the research institute and so on. Especially serious, there are more than ten GIF images in an article, which takes 10-20 seconds to load and consumes dozens of meters of traffic, seriously affecting the user experience! Especially Mobile terminal, an inch of traffic an inch of gold; 3-5s does not open the page, users will directly flee. So page loading speed optimization is imperative!

We all know that the loading process of a web page is as follows: 1. Parse the HTML structure. Load external scripts and stylesheet files. Parse and execute the script code. // Some scripts will block page loading. //DOMContentLoaded 6. The page is loaded. // Load: request HTML, and then request javascript /CSS/iconfont and other resources that depend on HTML. The most essential way to optimize the loading speed of web pages is to reduce the number of requests and the size of requests.

Reduce the number of requests

1, merge small ICONS into Sprite graphics or iconFONT font files 2, base64 to reduce unnecessary network requests 3, image lazy loading 4, JS/CSS packaging on demand 5, lazy loading GA statistics 6, etc…

Reduce request size

1, JS/CSS/HTML compression 2, Gzip compression 3, JS/CSS on demand loading 4, image compression, JPG optimization 5, webP optimization & SRcset optimization 6, and so on…

JS/CSS packing on demand and JS/CSS loading on demand are two different concepts: JS/CSS packing on demand is something that happens when precompiling, ensuring that only the logic related to the current page is packed. Loading JS/CSS on demand is something that happens at runtime, ensuring that only the logic that is first used on the current page is loaded.

Next, we will introduce specific practices combining two essential optimization approaches.

How can I reduce the number of requests?

1. Merge ICONS to reduce network requests

Merging ICONS is a common optimization method to reduce network requests. Small ICONS in web pages are characterized by small size and large number, and the number of concurrent requests initiated by browsers is limited. Therefore, these small ICONS will seriously affect the loading speed of web pages and hinder the request and presentation of key content

Sprite figure

Merging Sprite diagrams is a slow and subtle process that isn’t particularly technical, but it’s a skill that every front-end developer must master. Just enter the front of the direct manual cut map puzzle. An experienced front end can try to automate it with build tools and is recommended. Gulp. Spritesmith plug-in.

Gulp.task ('sprites', function() { var spriteData = gulp.src(config.src) .pipe(plumber(handleErrors)) .pipe(newer(config.imgDest)) .pipe(logger({ showChange: true })) .pipe(spritesmith({ cssName: 'sprites.css', imgName: 'sprites.png', cssTemplate: path.resolve('./gulp/lib/template.css.handlebars') })); var imgStream = spriteData.img .pipe(buffer()) .pipe(gulp.dest(config.imgDest)); var cssStream = spriteData.css .pipe(gulp.dest(config.cssDest)); return merge([imgStream, cssStream]); });Copy the code

Sprite diagrams are not suitable for reactive scenarios on the wireless side, so they are increasingly used as a backup.

Iconfont font file

Iconfont is a font encoded to achieve the effect of ICONS. Since it is text, you can set the color and size arbitrarily, which is relatively better than Sprite. But it only works with solid color ICONS. Alibaba vector icon library is recommended

Use Base64 to reduce unnecessary network requests




Base64 code compatibility

The Sprite image and iconFONT font file mentioned above are not suitable for some scenes, such as: 1. Small background images cannot be placed in Sprite images, usually circular tiling small pieces to set up a large background. 2, small GIF, can not be put in the wizard map, send a request is too wasteful.




Base64 usage scenarios

Note: When cssnano compresses CSS, the base64 URI of some rules cannot be recognized, resulting in error damage. As shown in the following figure, cssnano compresses // to / :




Cssnano compression base64

Reason: cssnano will skip data: image/data: the strings behind the application, but won’t skip data: img, so if you use the tool to generate data: img, suggest changing tools or directly to the amended as data: image.

3. Image lazy loading

The picture is the web page traffic accounted for the most part, but also need to focus on the optimization of the part. The principle of lazy image loading is that the img SRC attribute is not set, and the image url is placed on the IMG SRC attribute when the appropriate time (such as scrolling, sliding, appearing in the window, etc.) is available. For more information, please refer to the previous post: Lazy image loading schemes

Fixed width and height values for pictures

Lazy loading of images with fixed width and height values is easier because the width and height values can be set in THE CSS, just consider the substitution of SRC. Lazysizes is recommended.

// Import js files // non-responsive examples// Automatically calculate the appropriate image/ / the iframe exampleCopy the code

Note: when the browser parses the IMG tag, if the SRC attribute is empty, the browser will consider the image as a broken image and will display the border of the image, affecting the appearance of the city.




The first block is the initial state, the fourth block is the successful state, the second block and the third block is the state that affects the appearance of the city

Lazysizes changes the class of the image during lazyloading: default lazyload, lazyloading, end of loading: lazyloaded. Opacity :0 is set, and opacity:1 is set during display.

Opacity: 0; // Opacity: 0; } .lazyloaded{ opacity: 1; transition: opacity 500ms; // Use transition to create a gradient}Copy the code

2. Use a default map, such as a 1×1 transparent or gray map.

Copy the code

In addition, we can add loading effect to make the effect better, especially the large images in the article details page.

.article-detail-bd { .lazyload { opacity: 0; } .lazyloading { opacity: 1; background: #f7f7f7 url(/images/loading.gif) no-repeat center; }}Copy the code
Fixed aspect ratio of the picture

The image with fixed aspect ratio is relatively complicated, for example, the image of the detail page of the article, because the width value of the device is uncertain, so the height value is also uncertain. At this time, the focus of work is instead on how to determine the height of the image. Why determine the height of the image? Page jitter is not only a poor user experience, but also a high performance cost, as each jitter triggers a reflow event. The previous blog site performance optimization for rendering performance also analyzed the performance cost of repainting.

There are two main ways to solve the problem of image jitter with fixed aspect ratio: 1. The first method uses padding-top or padding-bottom to achieve a fixed aspect ratio. Advantages are pure CSS solutions, disadvantages are HTML redundancy and unfriendly to output to third parties.

Copy the code
      

2. The second scheme uses ratio to set the actual width and height value in the page initialization stage. The advantage is that HTML is clean and friendly to the output to a third party.

Copy the code

So, this padding-top: 75%; And data-ratio=”0.75″? When you upload an image, the background returns the original aspect value to you, calculates the aspect ratio, and saves it to data-ratio.

The second option, adopted by Curioso daily, is mainly because the first option is unfriendly to third-party output: additional styles are required for IMG, but third-party platforms generally do not allow the introduction of external styles.

With the second option settled, we define a function that sets the height of the image:

Function resetImgHeight(els, placeholder) {var ratio = 0, I, len, width; for (i = 0, len = els.length; i < len; i++) { els[i].src = placeholder; width = els[i].clientWidth; / / must use clientWidth if (els [I] attributes [' data - thewire ']) {thewire = els [I] attributes [' data - thewire] value | | 0; ratio = parseFloat(ratio); } if (ratio) { els[i].style.height = (width * ratio) + 'px'; }}}Copy the code

We put the above code definition and call are directly into THE HTML, for the purpose of calculating the height of the image in the first time, reduce the user perception of the possibility of page jitter, to ensure the best effect.

// Native code// Parsed codeCopy the code

We not only save the aspect ratio, but also save the image format, in order to further optimize the GIF later.

Matters needing attention

1. Avoid premature loading of images and lower the threshold. In actual projects, it is not necessary to request images too early, especially in Mobile projects. Early requests not only waste traffic, but also lead to slow page loading due to too many requests. 2, for the best anti-shake effect, set the image height JS code embedded in the HTML for the first time to execute. 3. When setting the height according to the image width, use clientWidth instead of width. This is because Safari failed to get the width of the image in the first place, so use clientWidth to fix the problem.

4. JS/CSS package as required

On-demand packaging is a unique advantage of WebPack and is highly recommended for managing dependencies between modules in this way! Webpack has a high threshold, check out my previous blog: Getting Started with WebPack Modularity

Curcuriosity is a typical multi-page application. In order to cache common code, we use WebPack as needed, and use webPack’s CommonsChunkPlugin to pull out common JS/CSS code for easy caching, which makes a good balance between the number of requests and the cache of common code.

5. Delayed loading GA statistics

The async & defer property

Html5 has introduced async and defer attributes for script tags. Script tag with async attribute will download the script immediately when the browser parses and does not block subsequent events such as Document rendering and script loading, thus realizing asynchronous loading of the script. The Script tag with the defer attribute has similar functionality to async. And they can have an onload event attached ‘); , which can also avoid blocking.

Ga statistical code

Ga statistical code is used to dynamically create script tag schemes. This method does not block page rendering or subsequent requests, but it does block the window.onload event. The page is represented by a progress bar that keeps loading or a loading chrysanthemum that keeps spinning. So we delay the execution of the GA initialization code and place it in the window.onload function to prevent the GA script from blocking the window.onload event. So that users feel faster loading speed.




Bind the GA load to the onLoad event

How can I reduce the request size?

1, JS/CSS/HTML compression

This is also a common method, which will not be described too much. The main methods are: 1. Build tools to achieve this, such as Webpack /gulp/fis/ Grunt etc. 2. Background precompilation. 3. Use the third-party online platform to upload and compress manually. Both the second and third approaches have their limitations. The first approach is the current mainstream approach, with a good plug-in ecosystem, which can achieve rich build tasks. In the Curioso Daily project, we used Webpack & Gulp as the basis for building the system.

This article briefly introduces the compression methods of JS/CSS/HTML and some matters needing attention

JS compressed

JS compression: Use webPack’s UglifyJsPlugin plug-in, while doing some code checking.

new webpack.optimize.UglifyJsPlugin({
    mangle: {
        except: ['$super', 'Copy the code

, ‘exports’, ‘require’] } })

CSS compression

CSS compression: Use CSSNano compression, and use PostCSS for automatic operations, such as automatic prefix, fallback support, syntax detection, and so on.

    var postcss = [
        cssnano({
            autoprefixer: false,
            reduceIdents: false,
            zindex: false,
            discardUnused: false,
            mergeIdents: false
        }),
        autoprefixer({ browers: ['last 2 versions', 'ie >= 9', '> 5% in CN'] }),
        will_change,
        color_rgba_fallback,
        opacity,
        pseudoelements,
        sorting
    ];Copy the code
HTML compression

HTML compression: Use HTMLmin to compress HTML and correct non-standard HTML writing.

Gulp.task ('build:views', ['clean:views'], function() {return streamQueue ({objectMode: true }, gulp.src(config.commonSrc, { base: 'src' }), gulp.src(config.layoutsSrc, { base: 'src' }), gulp.src(config.pagesSrc, { base: 'src/pages' }), gulp.src(config.componentsSrc, { base: 'src' }) ) .pipe(plumber(handleErrors)) .pipe(logger({ showChange: true })) .pipe(preprocess({ context: { PROJECT: project } })) .pipe(gulpif(function(file) { if (file.path.indexOf('.html') ! = -1) { return true; } else { return false; } }, htmlmin({ removeComments: true, collapseWhitespace: true, minifyJS: true, minifyCSS: true, ignoreCustomFragments: [/<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/, //] }))) .pipe(gulp.dest(config.dest)); });Copy the code

A third party platform required it to be written as a decimal point. Htmlmin formats decimals as integers by default, so an additional exclusion was added: //. To now all don’t understand this third party platform how think!

Conditional compilation

Due to the large number of curcuriosity daily projects, we spent a lot of effort to separate the front-end projects and realize the separation of the front and back. However, the use of the gulp-Preprocess plugin for conditional compilation is highly recommended in cases where we want to maintain related code in a single file while executing different logic for different projects.




Conditional compilation

2. Gzip compression

Gzip compression is also a common optimization method. The front end does not need to do any actual work, background configuration of the server on the line, the effect is very obvious. If you find that your site is not yet gzip enabled, take action.

Gzip compression principle

If the browser supports gzip compression, the request header will contain accept-encoding :gzip. Then the server will gzip the original response, and transmit the gzip compressed response to the browser, which then carries out gzip decompression, and finally feeds back to the web page.




Request headers that support GZIP compression

Gzip compression effect

So how good is gzip compression? A conservative estimate would be about 60-80% size reduction on top of the JS/CSS/HTML compression already done.




Gzip compression effect

However, it is important to note that gzip compression can consume server performance and should not be overcompressed. Therefore, gzip compression is recommended only for JS, CSS, and HTML resources. You are advised to enable gzip compression for images that are hosted on a third party. You are not advised to enable gzip compression for images that are hosted on your own application server.

3. Load JS/CSS on demand

This is different from the packaging on demand mentioned earlier. JS/CSS on-demand packaging is what happens when precompilation ensures that only the logic related to the current page is packaged. Loading JS/CSS on demand is something that happens at runtime, ensuring that only the logic that is first used on the current page is loaded.

So how do you implement load on demand? Curio daily uses the require and Require. ensure methods provided by WebPack to implement on-demand loading. It is worth mentioning that, in addition to the specified on-demand file list, WebPack will automatically resolve the dependency of callback function and the deep dependency of the specified list, and finally package into a file.




Webpack loads on demand

The appeal code will only load the login-related JS/CSS resources when the login button is clicked. Resources are automatically executed after successfully loading.

4, picture compression, JPG optimization

Image compression hosted to the application server

It can be handled manually or through gulp subtasks. Manual processing, recommend a site tinypng, although lossy compression, but the compression effect is excellent. Gulp sub-task processing, the recommended use of gulp-Imagemin plug-in, automatic processing, the effect is also good.

Gulp.task ('images', function() { return gulp.src(config.src) .pipe(plumber(handleErrors)) .pipe(newer(config.dest)) .pipe(logger({ ShowChange: true}).pipe(imagemin()) // zip.pipe(gulp.dest(config.dest)); });Copy the code
Image compression hosted to a third party platform

For example, qiuniuyun platform, they will have a set of special programs for image compression, format conversion, tailoring and so on. Just add the corresponding parameters to the URL, and while there are occasional bugs, hosting is generally better than using your own application server.




Change parameters to achieve different degrees of compression

JPG optimization

In addition to the image compression, there is no requirement for transparent image bed scenes, I strongly recommend converting PNG to JPG, the effect is very obvious! As shown below, PNG is formatted as JPG, and the images are almost 8 times different!




PNG to JPG, eight times the volume difference

Again, images that can be converted to JPG are strongly recommended!

5. Webp optimization & SRCSET optimization

Webp optimization

At first glance, alas, the compatibility is so poor that android and Chrome support is good enough.




Webp compatibility

On the other hand, webP optimization can reduce JPG size by nearly 50%. The optimization effect is obvious. In addition, if the browser supports WebPAnimation, it can also compress giFs!




General image webP optimization




GIF image optimization

Poor compatibility, but good effect! Eventually Curiosity decided to give it a try. 1. Determine the compatibility of browsers with WebP and WebPAnimation. 2. If your browser supports WebP and WebPAnimation, replace them with webP images.

In view of the limited webP support of browsers, we use a progressive upgrade method to optimize: for browsers that do not support WebP, no processing is done; For browsers that support WebP, replace image SRC with webP format. So how do you judge WebP compatibility?

// Check whether the browser supports webp // It is not written as a callback because isSupportWebp=false does not matter, (function() {function webpTest(SRC, name) {var img = new Image(), isSupport = false, className, CLS; img.onload = function() { isSupport = !! (img.height > 0 && img.width > 0); cls = isSupport ? (' ' + name) : (' no-' + name); className = document.querySelector('html').className className += cls; document.querySelector('html').className = className.trim(); }; img.onerror = function() { cls = (' no-' + name); className = document.querySelector('html').className className += cls; document.querySelector('html').className = className.trim(); }; img.src = src; } var webpSrc = 'data:image/webp; base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoB\ AAEAAwA0JaQAA3AA/vuUAAA=', webpanimationSrc = 'data:image/webp; base64,UklGRlIAAABXRUJQVlA4WAoAAAA\ SAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAA\ AAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA'; webpTest(webpSrc, 'webp'); webpTest(webpanimationSrc, 'webpanimation'); }) ();Copy the code

Using Modernizr for reference, the function to detect compatibility of WebP/WebpAnimation is realized. As can be seen from the code, the principle of detection is to simulate downloading pictures of corresponding formats, and compatibility results can be obtained in the asynchronous function.

The next step is to replace the URL with webP format

/ / get the webp SRC function _getWebpSrc (SRC) {var DPR = math.h round (window. DevicePixelRatio | | 1), thewire = [1, 1, 1.5, 2, 2, 2], elHtml = document.querySelector('html'), isSupportWebp = (/(^|\s)webp(\s|$)/i).test(elHtml.className), isSupportWebpAnimation = (/(^|\s)webpanimation(\s|$)/i).test(elHtml.className), deviceWidth = elHtml.clientWidth, isQiniuSrc = /img\.qdaily\.com\//.test(src), format = _getFormat(src), isGifWebp, isNotGifWebp, regDetailImg; if (! src || ! isQiniuSrc || ! format || format == 'webp') { return src; } isNotGifWebp = (format ! = 'gif' && isSupportWebp); isGifWebp = (format == 'gif' && isSupportWebpAnimation); Calculate according to the screen resolution / / size SRC = SRC. Replace (/ / / (thumbnail | crop) \ /. *? (\d+)x(\d+)[^\/]*\//ig, function(match, p0, p1, p2) { if(dpr > 1){ p1 = Math.round(p1 * ratio[dpr]); p2 = Math.round(p2 * ratio[dpr]); match = match.replace(/\d+x\d+/, p1 + 'x' + p2) } return match; }); If (isNotGifWebp | | isGifWebp) {/ / replace the webp, SRC = src.replace(/\/format\/([^\/]*)/ig, function(match, p1) {return '/format/webp'; }); }}Copy the code
Matters needing attention

1. The pixel density of the Window screen is not necessarily an integer, nor is the pixel density of the MAC browser when zoomed. So get DPR must take the whole: DPR = math.h round (window. DevicePixelRatio | | 1); . 2. Ratio = [1, 1, 1.5, 2, 2, 2] means: 1x screen uses 1x map, 2x screen uses 1.5x map, and 3x screen uses 2x map. The rules here can be set as required. 3, WebP optimization is more suitable for hosting to the third party pictures, simple modification of parameters can obtain different pictures.




DevicePixelRatio compatibility

Srcset compatibility



Srcset compatibility

As mentioned above, when optimizing WebP, we simulated srCSet: setting the best image width and height based on the screen pixel density. Lazysizes originally provides the srcset option. You can also use the solution of Lazysizes to implement srcset. If you are interested, check out the source code.

Time to wrap it up again?

This blog around the specific practice of Curiositydaily in optimizing page loading speed to do a series of thinking. On the whole, it covers a wide range of knowledge, including webpack & gulp construction system, WebP optimization of pictures, GZIP configuration of servers, loading sequence of browsers, lazy loading scheme of pictures and so on.

The gulP subtask mentioned in this paper will be followed by a series of related practices of The Curcuriosity Daily project, which will cover the design ideas of gulP subtask, construction system architecture, as well as the analysis and explanation of specific subtasks. Please pay attention.

If this post has been helpful to you, please click “like” to support it and leave your suggestions and discussions in the comments section.