Vue first screen optimization full resolution

Open the GZIP

Package directly into a file called xxx.gz

Generating a GZ file during the front-end packaging process can reduce the packaging process of the deployment server, reduce the performance pressure of the server, and improve the response time.

Gzip packaging is implemented using the WebPack plug-in CompressionWebpackPlugin.

Add the WebPack configuration by creating webpack.config.js in the vue project root.

Here is a basic configuration, detailed in its documentation.

const CompressionPlugin = require("compression-webpack-plugin");

let config = {
    plugins: []}module.export = (env, argv) = > {
    if(env.mode === 'production') {let comress = new CompressionPlugin({
        // Test and compress js and CSS files
        test: /\.(js|css|svg)$/.// The default value is gzip
        algorithm: 'gzip'.// Compression options, all of which can be found in the Node documentation
        compressionOptions: {
            level: 1
        },
        // The minimum compressed volume, in bytes. The default value is 0
        threshold: 1600.// Minimum compression efficiency: ration = Volume after compression/volume before compression. Compression is performed only when the efficiency is lower than this value. The default is 0.8. Pass 1 and all are compressed.
        minRatio: 0.5.// The default file name is '[path][base].gz'. For other parameters, see the documentation
        filename: '[path][base].gz'.// Whether to delete source files. The default value is false
        deleteOriginalAssets: true
    })
       config.plugins.push(compress)
    }
    return config
}
Copy the code

CompressionOptions can be viewed here.

Note that there is a production and development environment to determine whether to inject plug-ins.

We can change the current environment with vue.config. productionTip = false /true.

Open the deployment server gzip

Typically we use Nginx to deploy front-end projects. So we will only cover nginx configuration here.

Since we have configured the direct packaging as gz, the server does not need to compress, and can pass directly to the browser. Add the corresponding configuration item to the nginx config file.

Gzip API:

Commonly used are the following:

| Syntax: | **gzip** on | off; | | : — — — — — — – | — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — – | | | default gzip off; | | | the parent node of the HTTP server, the location, if the location in | | introduce | whether open the gzip |

Syntax: **gzip_comp_level** *level*;
The default gzip_comp_level 1;
The parent node http.server.location
introduce Compression level
Syntax: **gzip_min_length** *length*;
The default gzip_min_length 20;
The parent node http.server.location
introduce The critical size of the file to be compressed

If the API belongs directly to the HTTP node, all sever servers will be activated. If the API is placed on the corresponding server node, only the corresponding server will be activated. Example:

server {
	gzip            on;
    gzip_min_length 1000;
    #gzip_proxied    expired no-cache no-store private auth;
    #gzip_types      text/plain application/xml;
}
Copy the code

In fact, the files we put on the server are all compressed GZ files, so all we need to do is open gzip on.

Manual subcontracting via SplitChunks and common package sharing

In Webpack, optimization.splitChunks are officially added for manual subcontracting. By default, it has certain rules (which only affect asynchronous chunks) :

  • New chunks can be shared, or modules can come fromnode_modulesfolder
  • The new chunk size is greater than 20KB (before min+gz)
  • When chunks are loaded on demand, the maximum number of parallel requests is less than or equal to 30
  • The maximum number of concurrent requests is less than or equal to 30 when the initialization page is loaded

Default configuration:

module.exports = {
  / /...
  optimization: {
    splitChunks: {
      chunks: 'async'.minSize: 20000.minRemainingSize: 0.minChunks: 1.maxAsyncRequests: 30.maxInitialRequests: 30.enforceSizeThreshold: 50000.cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10.reuseExistingChunk: true,},default: {
          minChunks: 2.priority: -20.reuseExistingChunk: true,},},},},};Copy the code

There are a few important apis that you need to know about here, and see the official WebPack documentation for the rest.

  1. splitChunks.chunks: What chunks are executed under the split rule. The default:async.
    1. all: All chunks will be broken.
    2. async: Only asynchronous chunks will be split.
    3. initial: Only portal chunks will be split.
  2. splitChunks.maxAsyncRequests: Maximum number of parallel requests when loading on demand. The default:30.
  3. splitChunks.maxInitialRequests: The maximum number of parallel requests at the entry point. The default:30.
  4. splitChunks.minChunks: Minimum number of chunks that must be shared before splitting. That is, if a shared module wants to be split, it must be referenced at least as many times as possibleminChunks. The default:1.
  5. splitChunks.minSize: Indicates the minimum size (bytes) of chunk. Default: 20000 is 2.5KB.
  6. splitChunks.maxSize: tell webpacktryWill be bigger than themaxSizeFour bytes of chunk divided into smaller parts.
  7. splitChunks.maxAsyncSize:maxAsyncSizemaxSizeThe difference betweenmaxAsyncSizeLoading chunk on demand will only be affected.
  8. splitChunks.maxInitialSize:maxInitialSizemaxSizeThe difference betweenmaxInitialSizeOnly the initial loading chunks will be affected.
  9. splitChunks.name: Indicates the name of the chunk to be split. Set tofalseThe same name will be kept for chunk, so the name will not be changed unnecessarily. This is the recommended value for a build in production.
  10. splitChunks.minRemainingSize: Ensure that the minimum chunk size left after splitting exceeds the limit to avoid zero-size modules. It only takes effect when a single chunk is left.

MaxSize is just a hint that may be violated if the module is larger than maxSize or if the split does not conform to minSize.

splitChunks.cacheGroups

The cache group can manually set the matching rules for the chunks. The cache group can inherit and/or override any option from splitchunk. *. But test, Priority, and reuseExistingChunk can only be configured at the cache group level.

  1. Splitchunk.cachegroups.{cacheGroup}. Priority: a module can belong to more than one cacheGroup. Optimization gives priority to cache groups with higher priorities. The default group has a negative priority to allow custom groups to get a higher priority (default is 0 for custom groups). Default value: -20.

  2. Splitchunk.cachegroups.{cacheGroup}. ReuseExistingChunk: If the current chunk contains a module that has been split from the main bundle, it will be reused instead of generating new modules. This may affect chunk’s result filename. Default: true.

  3. Splitchunk.cachegroups. {cacheGroup}. Test: This API can accept a function, string, and regular expression. You can generally use regular expressions to identify specified folders or files. You can also pass in a function. Function (module, {chunkGraph, moduleGraph}) => Boolean RegExp String. Omitting it selects all modules. It can match absolute module resource paths or chunk names. When a chunk name is matched, all modules in chunk are selected.

  4. Splitchunk.cachegroups.{cacheGroup}. Filename: indicates the output filename. The following placeholders can be used.

    The template describe
    [file] Filename and path, without query or fragment
    [query] With the prefix?The query
    [fragment] With the prefix#The fragments of
    [base] Only filename (including the extension name) does not contain path
    [filebase] Ditto, but deprecated
    [path] Filename is not included
    [name] The value can be filename but does not contain the extension name or path
    [ext] With the prefix.Extension of theoutput.filenameIs not available)

You can also pass in string function (pathData, assetInfo) => string

  1. splitChunks.cacheGroups.{cacheGroup}.enforce: tells Webpack to ignoresplitChunks.minSize,splitChunks.minChunks,splitChunks.maxAsyncRequestssplitChunks.maxInitialRequestsOption and always create a chunk for this cache group. (Note that this is externalminSizeEtc, while the cache group is internalminSizeWill not be ignored, or will it be broken up.)

Here’s an example of an element-plus split:

// Here is an on-demand reference to elementPlus's recommendation
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
  configureWebpack: {
    plugins: [
      Components({
        resolvers: [ElementPlusResolver()],
      }),
    ],
    optimization: {
      splitChunks: {chunks: "all".maxSize: 30000.minSize: 20000.cacheGroups: {
          // vendors: {
          // name: "chunk-vendors",
          // test: /[\\/]node_modules[\\/]/,
          // chunks: "initial",
          // priority: 2,
          // reuseExistingChunk: true,
          // enforce: true
          // },
          elementUI: {
            name: "chunk-elementplus".// The maximum size is 12KB, which can be customized or not added
            maxSize: 10000.minSize: 1000.// Regular expressions to get modules under node_modules/element-plus/
            test: /[\\/]node_modules[\\/]element-plus[\\/]/.// All chunks are split, asynchronously or synchronously
            chunks: "all".priority: 3.reuseExistingChunk: true.// The external minSize is ignored here, but the internal minSize is still adhered to
            enforce: true},}}}}}Copy the code

Note that cacheGroups can also do chunks chunks if the component is loaded on demand. This is excellent and can be loaded on demand component subcontracting is optimization-friendly.

Route lazy loading

When a component is large and not commonly used, routing load on demand is a good way to do it.

Vue-router also provides a friendly API for lazy route loading.

const Foo = () = > import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () = > import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () = > import(/* webpackChunkName: "group-foo" */ './Baz.vue')
Copy the code

In this way, components that are dynamically imported (using the import() function) will be packaged separately as a chunk.

And we can customize the name of chunk through comments.

Alternatively, you can use:

const Foo = () = >
  Promise.resolve({
    /* Component definition object */
  })
Copy the code

To define route lazy loading.

It is worth noting that import() dynamic import can also be used for normal component imports. And this dynamically imported component will also be subcontracted. Load on demand.

Components are imported on demand

In fact, in our usual applications, our own code is not very large, the bulk of the general are some third-party components, such as UI components, chart components and so on. But not all of these components are used. In particular, the chart component. Using on-demand import is critical at this point.

Generally, importing on demand means that we manually import and declare components in the parent of the component that is being used. Like in Element-UI. However, with the release of some new UI components, especially Vue3, many components have more intelligent import on demand. Instead of global import, we just need to use and configure the WebPack plug-in, which can automatically import components, such as the following element-Plus example:

NPM install unplugin-vue-componentsCopy the code
// webpack.config.js
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
  // ...
  plugins: [
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}
Copy the code

Note that the above is webpack.config.js, which needs to be configured in vue.config.js when using vue. See Webpack for details.

Static resources are manually compressed using CDN

The full name of CDN is Content Delivery Network. CDN is an intelligent virtual network built on the basis of the existing network. It relies on the edge servers deployed in various places, through the central platform of load balancing, content distribution, scheduling and other functional modules, users can get the content nearby, reduce network congestion, and improve user access response speed and hit ratio.

We can upload our static resources to THE CDN. When the user makes a request, the CDN will select an appropriate server for resource transmission according to its location. For better network performance.

It is worth noting that some relatively large libraries can also be introduced with CDN, which can not only reduce the server network pressure. It can also ensure its stability. For example, some common CDN sites, such as bootCDN.

  1. Introduce script tags into HTML files.

    <! DOCTYPEhtml>
    <html lang="">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
        <link rel="icon" href="<%= BASE_URL %>favicon.ico">
        <! -- Key, introduce the corresponding tag -->
        <script src="https://unpkg.com/vue@next"></script>
        <title><%= htmlWebpackPlugin.options.title %></title>
      </head>
      <body>
        <noscript>
          <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
        </noscript>
        <div id="app"></div>
        <! -- built files will be auto injected -->
      </body>
    </html>
    
    Copy the code
  2. Configure externals for webpack. Here is vue.config.js:

    module.exports = {
        // Other parameters
      configureWebpack: {
        externals: {
          'vue': 'Vue'}}// Other parameters
    }
    Copy the code
  3. Two points are worth noting:

    1. Libraries are imported directly from the library, not from the path (node_modules is usually installed from the library, so just be careful). That is:
    import { createApp } from "vue"
    Copy the code
    1. packge.jsonThe version in should be the same as that introduced in HTML, otherwise an error will occur.

When Vue was tested using an external CDN, the Vender volume dropped from 62.79 KiB under GZIP to 37.84KiB. Optimization is obvious!

CDN services are available on all major domestic cloud platforms, such as Ali Cloud and Tencent Cloud. The following is a simple demonstration of replacing vue.js with CDN.

Use ES6 module instead of Node module to help Webpack do tree-shake

Because Webpack’s tree-shake is based on the static structure of ES6. So called static structure. That is, it cannot be imported dynamically based on code conditions.

That is only

import a from 'mw'
Copy the code

But can’t

//Error syntax
import {a} from 'mw'
if(a){
    import(xxx)
}else{
    import(yyy)
}

Copy the code

Dynamic introduction is not possible, that is, the introduction relationship between modules can be determined at the lexical analysis stage. You can then do this by simply removing unreferenced modules.

So we can use ES6 modules as much as possible, and we can configure sideEffects properties in ackage.json to help Webpack determine which modules are pure ES6 modules, which can be tree-shake directly.

Close the source – the map

Source-map is born because the package generated code is compressed and difficult to debug when errors are generated. We use source-Map to find the location of the uncompressed source code when it generates errors or information.

But in a production environment, we don’t want to expose the source code to the user, and source-Maps tend to be bulky because they are not compressed. So we can usually turn it off in production.

Vue can be turned off like this:

//vue.config.js
module.exports = {
    productionSourceMap: true
}
Copy the code

Remove redundant code

Minimizer, webPack’s native compression tool, has limited functionality. We want to use UglifyJS for the ultimate compression merge configuration, and the UglifyJS kernel is Uglip-JS.

Compress options include the following:

  • drop_console (default: false) — Pass true to discard calls to console.* functions. If you wish to drop a specific function call such as console.info and/or retain side effects from function arguments after dropping the function call then use pure_funcs instead.
  • drop_debugger (default: true) — remove debugger; statements
  • booleans (default: true— Various optimizations for Boolean context, for example!!!!! a ? B: C → A? b : c

The first two are turned off by default, and when turned on you can remove the console and debugger from your code.

Here is a simple example:

module.exports = {
    / /...
    configureWebpack : {
        optimization: {
          minimizer: [
              new UglifyJsPlugin({
                test: /\.js(\? . *)? $/i,
                parallel: true.uglifyOptions: {
                    compress: {
                        drop_console: true.drop_debugger: true}}}),],}}/ /...
}
Copy the code

SSR or Prerendering

If it’s just home page optimization, it’s better to use prerendering. Because compared with SSR, the server does not need to undertake the compilation of HTML files. Prerendering is the process of converting a route into HTML.

A simple example of prerendering is as follows:

//vue.config.js
const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')

module.exports = {
  configureWebpack = {
    plugins: [...new PrerenderSPAPlugin({
      // Required - The path to the webpack-outputted app to prerender.
      staticDir: path.join(__dirname, 'dist'),
      // Required - Routes to render.
      routes: [ '/'.'/about'.'/some/deep/nested/route'],})]}}Copy the code

reference

  1. CompressionWebpackPlugin
  2. Nginx official documentation
  3. Split-chunks-plugin is the official webpack documentation
  4. Vue official website – Dynamic Components & Asynchronous components
  5. prerendering

instructions

My ability is limited, unavoidably there will be mistakes, if there are mistakes please correct.