Wu Junjie, anxious struggling youth, have you finished fixing bugs?

preface

Performance optimization is a topic every engineer can’t escape. Here is my summary of some optimization techniques, I hope to help you, the follow-up will continue to update. Demo source and PPT unconditional sharing.

Presentation PPT (must see, super cool)

Landscape viewing is better: http://118.25.49.69:8086

Impact on front-end performance

An important indicator of front-end performance is page load time, which is not only relevant to user experience, but also a factor in search engine rankings.

  • Data from Google showed that a page with 10 pieces of data that took 0.4 seconds to load became a page with 30 pieces of data that took 0.9 seconds to load, and traffic and AD revenue dropped90%.
  • Google Map home page file size from100KBReduce to70-80KBTraffic went up in the first week10%It went up in the next three weeks25%.
  • Amazon’s data shows that loading times have increased100 milliseconds, salesA 1% drop in.

So: recasting performance of the light, our generation duty-bound 😎

First, debugging tools

Don’t cut wood work, after college to work again!

1.1 the Network

Here you can see the resource loading details and make an initial assessment of the factors that affect page performance. The right mouse button allows you to customize the tabs, and at the bottom of the page is an overview of the currently loaded resources. DOMContentLoaded DOM Load: the time when all the resources on the current page are loaded

Consider: How to determine which resources are useless for the current page load and optimize accordingly?

Shift + CMD + P bring up the console extension tool and add the rule

The extension tool makes more use of gestures

The waterfall flow waterfall

  • QueueingThe browser puts the resource into the queue time
  • StalledThe holdup time due to queue time
  • DNS LookupDNS Resolution time
  • Initial connectionThe time the HTTP connection was established
  • SSLThe time when the browser establishes a secure connection with the server
  • TTFBThe time to wait for the server to return data
  • Content DownloadThe time when the browser downloads the resource

1.2 Lighthouse

  • First Contentful PaintFirst screen render time, green within 1s
  • Speed IndexSpeed index, green within 4s
  • Time to InteractiveInterchangeable time to page

Do an automatic quality assessment of your site based on some of Chrome’s policies and make recommendations for optimization.

1.3 Peformance

The most professional analysis of the website ~ will be covered many times later

1.4 webPageTest

Can simulate different scenarios of access, such as simulate different browsers, different countries and so on, online test address: webPageTest

1.5 Resource packaging analysis

webpack-bundle-analyzer

NPM install --save-dev webpack-bundle-Analyzer // webpack.config.js file const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin module.exports={ plugins: [new BundleAnalyzerPlugin({analyzerMode: 'server', analyzerHost: '127.0.0.1', analyzerPort: 8889, reportFilename: 'report.html', defaultSizes: 'parsed', openAnalyzer: true, generateStatsFile: false, statsFilename: 'stats.json', statsOptions: null, logLevel: 'info' }), ] } // package.json "analyz": "NODE_ENV=production npm_config_report=true npm run build"Copy the code

Open source – the map

webpack.config.js

module.exports = {
    mode: 'production',
    devtool: 'hidden-source-map',
}
Copy the code

package.json

"analyze": "source-map-explorer 'build/*.js'",
Copy the code

npm run analyze

Second, the WEB API

To do a good job, he must sharpen his tools. Some of the analytics apis provided by browsers are critical

2.1 Monitor window activation status

Have universities taken MOOCs? The video will pause as soon as you leave the window

Or some exam sites that remind you that you can’t leave the current window

Or, this effect

// Let vEvent = 'visibilitychange'; if (document.webkitHidden ! = undefined) { vEvent = 'webkitvisibilitychange'; {if} function visibilityChanged () (document. Hidden | | document. WebkitHidden) {document. The title = 'the guest officer, ~' console.log("Web page is hidden.")} else {document.title = 'console.log("Web page is hidden. You are back ~ '. The console log (" Web page is visible. ")}} the document. The addEventListener (vEvent visibilityChanged, false);Copy the code

In fact, there are many hidden apis, here you can have a try if you are interested:

2.2 Observing Long Tasks (Tasks in Performance)

const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        console.log(entry)
    }
})

observer.observe({entryTypes: ['longtask']})
Copy the code

2.3 Monitoring Network changes

When the network changes, we will give users feedback on network problems. Sometimes, when you watch live broadcast, your network will be stuck, and the live broadcast platform will remind you or automatically give you the definition change

var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
var type = connection.effectiveType;

function updateConnectionStatus() {
  console.log("Connection type changed from " + type + " to " + connection.effectiveType);
  type = connection.effectiveType;
}

connection.addEventListener('change', updateConnectionStatus);
Copy the code

2.4 Calculate DOMContentLoaded time

window.addEventListener('DOMContentLoaded', (event) => {
    let timing = performance.getEntriesByType('navigation')[0];
    console.log(timing.domInteractive);
    console.log(timing.fetchStart);
    let diff = timing.domInteractive - timing.fetchStart;
    console.log("TTI: " + diff);
})
Copy the code

2.5 More calculation rules

DNS resolution time: domainLookupEnd - domainLookupStart TCP connection time: connectEnd - connectStart SSL security connection time: ConnectEnd - secureConnectionStart Network request Time (TTFB): responseStart - requestStart Data transmission time: DomInteractive - responseEnd Resource loading time: LoadEventStart - domContentLoadedEventEnd First Byte Time: responseStart - domainLookupStart White screen time: ResponseEnd - fetchStart first interactive time: domInteractive - fetchStart DOM Ready time: DomContentLoadEventEnd - fetchStart page full load time: LoadEventStart - fetchStart HTTP header size: transferSize - encodedBodySize redirection number: performance. Navigation. RedirectCount redirection time consuming: redirectEnd - redirectStartCopy the code

Three, platitudes, Yahoo catch-22

After sharpening the knife, you should think about where to poke it better ~ 🗡🗡🗡

About Yahoo catch rules, how many do you know, write at ordinary times what to use again? There are many optimizations we can do for the following rules

3.1 Reduce cookie transmission

Cookie transmission will cause bandwidth waste, you can:

  • Reduce what is stored in cookies
  • Static resources do not need cookies, you can use other domain names, do not take the initiative to bring cookies.

3.2 Avoid excessive backflow and redrawing

The page backflow operation is continuously triggered

  let cards = document.getElementsByClassName("MuiPaper-rounded");
  const update = (timestamp) => {
    for (let i = 0; i <cards.length; i++) {
      let top = cards[i].offsetTop;
      cards[i].style.width = ((Math.sin(cards[i].offsetTop + timestamp / 100 + 1) * 500) + 'px')
    }
    window.requestAnimationFrame(update)
  }
  update(1000);
Copy the code

Look at the effect. It’s clearly stuck

performanceAnalyzing the results,loadThere is a lot of backflow after the event, andchromeThey’re all marked red

Use fastDom optimization to separate and merge dom reads and writes

 let cards = document.getElementsByClassName("MuiPaper-rounded");
  const update = (timestamp) => {
    for (let i = 0; i < cards.length; i++) {
      fastdom.measure(() => {
        let top = cards[i].offsetTop;
        fastdom.mutate(() => {
          cards[i].style.width =
            Math.sin(top + timestamp / 100 + 1) * 500 + "px";
        });
      });
    }
    window.requestAnimationFrame(update)
  }
  update(1000);
Copy the code

Look at the effect again, very smooth ~ performanceAs a result, there are not as many red flags after the load event

FastDom: Github fastDom online Preview: fastDom Demo

The React Fiber architecture does a great job of splitting and combining tasks. If you are interested, check out how the scheduling algorithm works in Fiber

Fourth, the compression

Hum hum, make sure there is no wrong field, continue to continue!

4.1 Gzip

For details about how to enable gzip, see nginx enabling Gzip

Another way to do this is to generate a gz file at package time and upload it to the server so that nginx does not need to compress it and the server is less stressed. See: gzip archive &webPack configuration Compression-webpack-plugin

4.2 Server Compression

server.js

const express = require('express');
const app = express();
const fs = require('fs');
const compression = require('compression');
const path = require('path');


app.use(compression());
app.use(express.static('build'));

app.get('*', (req,res) =>{
    res.sendFile(path.join(__dirname+'/build/index.html'));
});

const listener = app.listen(process.env.PORT || 3000, function () {
    console.log(`Listening on port ${listener.address().port}`);
});
Copy the code

package.json

"start": "npm run build && node server.js",
Copy the code

4.3 compression of JavaScript, Css and Html

Directly use the corresponding plug-in in engineering projects, webpack mainly has the following three:

  • UglifyJS
  • webpack-parallel-uglify-plugin
  • terser-webpack-plugin

Specific advantages and disadvantages can be referred to: Webpack commonly used three JS compression plug-ins. The principle of compression is simply to remove some whitespace, line breaks and comments. With the help of ES6 modularity, we did some tree-shaking optimization. At the same time, some code obfuscation is done, on the one hand for smaller size, on the other hand also for source security.

CSS compression is mainly mini-CSS -extract-plugin, of course, the previous JS compression plugin will also give you CSS compression. Use posture:

npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
plugins:[
 new MiniCssExtractPlugin({
       filename: "[name].css",
       chunkFilename: "[id].css"
   })
]
Copy the code

HTML compression can be used with HtmlWebpackPlugin, a single page project on an index.html, performance improvement is minimal ~

4.4 HTTP2 header Compression

The characteristics of http2

  • Binary framing
  • The first compression
  • Flow control
  • multiplexing
  • Request priority
  • Server pushhttp2_push: 'xxx.jpg'

The specific upgrade method is also very simple, modify the nginx configuration, please Google

Five, Webpack optimization

Some of the WebPack plug-ins were mentioned above, but I’ll take a look at the others

5.1 DllPlugin improves build speed

With the DllPlugin, some large packages that are rarely upgraded are split into xx.dll.js files, which are referenced through manifest.json

webpack.dll.config.js

const path = require("path");
const webpack = require("webpack");
module.exports = {
    mode: "production",
    entry: {
        react: ["react", "react-dom"],
    },
    output: {
        filename: "[name].dll.js",
        path: path.resolve(__dirname, "dll"),
        library: "[name]"
    },
    plugins: [
        new webpack.DllPlugin({
            name: "[name]",
            path: path.resolve(__dirname, "dll/[name].manifest.json")
        })
    ]
};
Copy the code

package.json

"scripts": {
    "dll-build": "NODE_ENV=production webpack --config webpack.dll.config.js",
  },
Copy the code

5.2 splitChunks unpacking

optimization: {
        splitChunks: {
            cacheGroups: {
                vendor: {
                    name: 'vendor',
                    test: /[\\/]node_modules[\\/]/,
                    minSize: 0,
                    minChunks: 1,
                    priority: 10,
                    chunks: 'initial'
                },
                common: {
                    name: 'common',
                    test: /[\\/]src[\\/]/,
                    chunks: 'all',
                    minSize: 0,
                    minChunks: 2
                }
            }
        }
    },
Copy the code

Six, skeleton screen

Use CSS to position resources well in advance and fill them when they are loaded, reducing backflow and redrawing of pages while giving users the most direct feedback. Plug-ins used in the figure:react-placeholder

There are many ways to implement skeleton screens, many of which are rendered by the Puppeteer server

Using CSS pseudo-classes: A skeleton screen solution that can be implemented with only CSS

, etc.

Seven, the window

Principle: Load only the DOM elements that can be displayed in the current window. When the view changes, remove the hidden ones and add the DOM to be displayed to ensure that the number of DOM elements on the page is never too small, and the page will not lag

The plug-in used in the figure:react-window

Install: NPM I react-window

Import {FixedSizeList as List} from ‘react-window’;

Use:

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);
 
const Example = () => (
  <List
    height={150}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);
Copy the code

Eight, the cache

8.1 HTTP cache

keep-alive

Check whether Connection: keep-alive is enabled for Response Headers. After this function is enabled, no Initial connection takes time to view the waterfall flow of network

Nginx keep-alive (default enabled)

Keepalive_timeout 0; # 65s Keepalive_timeout 65; Keepalive_requests 100;Copy the code

Cache-Control / Expires / Max-Age

Set whether the resource is cached and the cache duration

Etag / If-None-Match

The resource is uniquely identified for comparison, and if it changes, the resource is pulled from the server. If there is no change then the cache resource, status code 304, is negotiated cache

Last-Modified / If-Modified-Since

Compare the time difference to decide whether to fetch resources from the server

For more HTTP caching parameters, see: Using HTTP caching: Etag, Last-Modified, and cache-Control

8.2 the Service Worker

Load Serviceworker.js with the Webpack plugin WorkboxWebpackPlugin and ManifestPlugin, and register with Serviceworker.register ()

new WorkboxWebpackPlugin.GenerateSW({ clientsClaim: true, exclude: [/\.map$/, /asset-manifest\.json$/], importWorkboxFrom: 'cdn', navigateFallback: paths.publicUrlOrPath + 'index.html', navigateFallbackBlacklist: [ new RegExp('^/_'), new RegExp('/[^/?]+\\.[^/]+$'), ], }), new ManifestPlugin({ fileName: 'asset-manifest.json', publicPath: paths.publicUrlOrPath, generate: (seed, files, entrypoints) => { const manifestFiles = files.reduce((manifest, file) => { manifest[file.name] = file.path; return manifest; }, seed); const entrypointFiles = entrypoints.app.filter( fileName => ! fileName.endsWith('.map') ); return { files: manifestFiles, entrypoints: entrypointFiles, }; }}),Copy the code

Nine, preloading && lazy loading

9.1 preload

Taking the fonts in the demo as an example, the normal loading order would look like this:

Join our preload:

<link rel="preload" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.119 .woff2" as="font" crossorigin="anonymous"/> <link rel="preload" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.118 .woff2" as="font" crossorigin="anonymous"/> <link rel="preload" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.116 .woff2" as="font" crossorigin="anonymous"/>Copy the code

9.2 prefetch

Scenario: the first page does not need such font files, the next page does: the first page will load ahead of time with Lowest priority

Join the prefetch:

<link rel="prefetch" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.113 .woff2" as="font"/> <link rel="prefetch" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.118 .woff2" as="font"/> <link rel="prefetch" href="https://fonts.gstatic.com/s/longcang/v5/LYjAdGP8kkgoTec8zkRgqHAtXN-dRp6ohF_hzzTtOcBgYoCKmPpHHEBiM6LIGv3EnKLjtw.117 .woff2" as="font"/>Copy the code

Need pages fromprefetch cacheTo take

Webpack also supports these two properties :webpackPrefetch and webpackPreload

9.3 a lazy loading

The picture

Machine images

Progressive image (similar to Gaussian blur)UI needs to specify this format when the draft

Responsive picture

Native mode:<img src="./img/index.jpg" sizes="100vw" srcset="./img/dog.jpg 800w, ./img/index.jpg 1200w"/>

Route lazy loading

This is done with the function + import

const Page404 = () => import(/* webpackChunkName: "error" */'@views/errorPage/404');

SSR && React-Snap

  • The server renders SSR, vue uses nuxt.js and React uses next.js
  • React-snap can use the Puppeteer implementation to render a single page, then preserve the DOM and send it to the client

11. Experience optimization

White during loading

loading.htmlYou need to pick it up. There’s another way. Use itwebpackThe plug-inHtmlWebpackPluginInsert the loading resource into the page

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, Word-wrap: break-word! Important; "/> <title>Loading</title> <style> body {margin: 0; } #loadding { position: fixed; top: 0; bottom: 0; display: flex; width: 100%; align-items: center; justify-content: center; } #loadding > span { display: inline-block; width: 8px; height: 100%; margin-right: 5px; border-radius: 4px; -webkit-animation: Load 1.04s ease infinite; Animation: Load 1.04s ease Infinite; } @keyframes load { 0%, 100% { height: 40px; background: #98beff; } 50% { height: 60px; margin-top: -20px; background: #3e7fee; } } </style> </head> <body> <div id="loadding"> <span></span> <span style="animation-delay: <span style=" max-width: 100%; clear: both; min-height: 1em; 0.39 s "> < / span > < span style =" animation - delay: 0.52s"></span> </div> </body> <script> window.addEventListener("DOMContentLoaded", () => { const $loadding = document.getElementById("loadding"); if (! $loadding) { return; } $loadding.style.display = "none"; $loadding.parentNode.removeChild($loadding); }); </script> </html>Copy the code

Reference article:

  • Yahoo’s 35 catch-22 for front-end performance optimization
  • Webpack practices – Use of webpack-bundle-Analyzer
  • Nginx open gzip
  • Gzip compressed files &webPack configuration Compression-webpack-plugin
  • Webpack three JS compression plug-ins commonly used
  • As long as CSS can achieve skeleton screen scheme
  • Use HTTP caching: Etag, Last-Modified, and cache-Control
  • WebpackPrefetch and webpackPreload