I worked on an internal back-pipe platform of the company years ago. The front-end was developed using vue.js framework, and the project was constructed based on vuE-CLI3 scaffolding. The UI component library used Elemet-UI, and other components included AXIos, Echarts, Vue-Router, vuex, etc. This project has a simple function, involving 40 pages, all of which are simple forms or list pages. NPM run build command is directly packaged when going online, without doing any configuration in vue.config.js. Put the dist package into nginx server and make a reverse proxy.

The unoptimized packaging results are as follows:

As you can see, chunk-vendors.82702712.js files are over 1M in size, and the corresponding CSS files are 194 KB, app.653b22c5.js are 172 KB, All the third-party libraries in the project are packaged into chunk-Vendors.82702712.js, and all the pages, vendors’ custom components, and so on are packaged into app.653b22c5.js. Because there is no use of dynamic loading, so these 1M, hundreds of KB JS will be loaded together when visiting the site, PC is fine, once put on the mobile terminal, weak network can only show the user a large whiteboard.

Visit the project home page and check the resource usage of JS and CSS using The Instrument Converge function of Chrome. It is found that chunk-Vendor.82702712.js is not used at 85.4%. The corresponding chunk-vendor.ea3FA8E3. CSS is not used 98.3%, app.js is not used 88.4 %, and app. CSS is not used 85%, indicating that the resources accessed on the home page of the project are large because they contain a large number of unused components. If each page loads only the components it needs, the speed of the site will be greatly improved.

Based on the above problems, optimization of packaging can be started from the following aspects.

  1. Components are introduced dynamically, loaded on demand,app.653b22c5.jsCan be divided into several JS, use to load the corresponding JS.
  2. Tripartite library components, loaded on demand,chunk-vendors.82702712.jsCan do split, do not have to load all, can be used to load the corresponding part of JS.
  3. To compress resource files, you can see the 1069.96 KB file in the figure above. Gzip can be compressed to 310.03 KB.

Start optimizing project packaging

Route lazy loading

Combined with Vue’s asynchronous component and Webpack’s code segmentation function, the lazy loading of routing components can be easily realized. In router.js, the original way to import components is:

import Home from './views/Home.vue'
import Login from './views/Login.vue'
import Role from './views/system/Role.vue'
import User from './views/system/User.vue'.Copy the code

As a result, these components are packaged into a file app.653b22c5.js. We changed the way of importing components to dynamic import:

const Home = (a)= > import('./views/Home.vue')
const Login = (a)= > import('./views/Login.vue')...Copy the code

After modifying all the component imports, I found that the original app.653b22c5.js and CSS files were split after running the NPM run build package.

But split too fine, each page is split out alone, a larger JS 7KB, small only 2, 3KB, CSS split after some 0.1KB, and even 0.03KB, split granularity is too fine, will also cause more network resource requests, affecting the site loading.

In fact, one of our functional processes usually involves multiple pages, and it would be better if we could group and package multiple page components to avoid multiple requests for network resources.

The route lazily loads the group

The group modification method is as follows:

const Role = (a)= > import(/* webpackChunkName: "group-role" */'./views/system/Role.vue')
const AddRole = (a)= > import(/* webpackChunkName: "group-role" */'./views/system/AddRole.vue')
const EditRole = (a)= > import(/* webpackChunkName: "group-role" */'./views/system/EditRole.vue')

const User = (a)= > import(/* webpackChunkName: "group-user" */'./views/system/User.vue')
const AddUser = (a)= > import(/* webpackChunkName: "group-user" */'./views/system/AddUser.vue')
const EditUser = (a)= > import(/* webpackChunkName: "group-user" */'./views/system/EditUser.vue')
const UserAllotRole = (a)= > import(/* webpackChunkName: "group-user" */'./views/system/UserAllotRole.vue')
Copy the code

After grouping components by function, the package results are as follows:

Each function module packed js about ten KB, the number of files is also greatly reduced.

Node_modules packaging

Chunk-vendor. js is a package of the three party libraries in node_modules, and elemental-UI accounts for most of the chunk-vendor. js size in this project. Group-monitor.js is 327.92 KB because it introduces Echart.

I introduced element-UI in its entirety in main.js, rather than on demand:

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import http from './utils/network/http'

Vue.use(ElementUI)
Copy the code

So all the components of Element-UI are packaged into the project.

References to tripartite libraries must be loaded on demand, and components that are not needed should not be introduced. When optimized, the components commonly used by element-UI are introduced as needed in main.js.

import { Container, Menu } from 'element-ui';

Vue.use(Container);
Vue.use(Menu);
Copy the code

If there are separate element-UI components that are used on the page, add them to the page so that the UI components are loaded when the page is accessed.

As more than 40 pages used element-UI components, the transformation work was quite large. I used Echart to test other pages to see the packaging situation.

I found three pages to introduce echart.js on demand:

// MonitorDetail.vue imports echart.js on demand
var echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/pie');
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
require('echarts/lib/component/legendScroll');

// Bulletin. Vue introduces echart.js on demand
var echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/bar');
require("echarts/lib/chart/tree");

// about.vue introduces echart.js on demand
var echarts = require('echarts/lib/echarts');
// Public introduction with Bulletin. Vue
require('echarts/lib/chart/bar');
require("echarts/lib/chart/tree");
// Public import with monitordetail.vue
require('echarts/lib/chart/pie');
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
require('echarts/lib/component/legendScroll');
// Own unique introduction
require("echarts/lib/chart/treemap");
require("echarts/lib/chart/graph");
require("echarts/lib/chart/gauge");
Copy the code

Some of the echart components introduced by about. vue are the same as monitordetail. vue and some are the same as bulletin. vue, as well as three common parts of the page. The about. vue unique section is also packaged with acount. vue.

Packing results are as follows:

About ~group-bulletin~group-monitor.js is the common part of the three pages of Echart, About ~group-monitor.js is the common part of about. vue and monitordetail. vue echart, About ~group-bulletin. Js is a common part of about. vue and bulletin. Vue, while the echart component unique to about.js is still in its own JS.

Vue-cli3 can intelligently help us package properly, as long as we introduce tripartite library components as needed, mainly through the SplitChunksPlugin plug-in.

Gzip compression

If the Nginx server is enabled with GZIP, static resources will be compressed on the server side. After the compressed package is transmitted to the browser, the browser will decompress it for use. This greatly improves the efficiency of network transmission, especially for js, CSS and other text compression, the effect is very obvious.

Here is the configuration for Nginx to enable gzip:

Open | close # gzip.
gzip on|off;

File size is larger than the specified size, in KB.
gzip_min_length 10;

# Compression levels, 1-9, the higher the value, the higher the compression ratio, but more CPU, and less efficient compression.
gzip_comp_level 2;

Type of file to compress.
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript;

If a.gz file can be found, the server compression will not be enabled.
gzip_static on|off
    
# Whether to add response headers Vary: accept-encoding It is recommended to enable.
gzip_vary on;

The number and size of buffers requested for compression, in 4k, in multiples of 32.
gzip_buffers 32 4K;
Copy the code

If Nginx does not have Gzip enabled, the front end can package a compressed version of the resource when it is packaged, and Nginx will transfer the compressed file to the browser.

Start by installing a plug-in:

npm i -D compression-webpack-plugin
Copy the code

Configure the plugin in vue.config.js:

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

module.exports = {
  configureWebpack: config= > {
    if (process.env.NODE_ENV === 'production') {
      return {
        plugins: [
          new CompressionPlugin({
            test: /\.js$|\.html$|\.css/.threshold: 10240.deleteOriginalAssets: false})]}}}}Copy the code

Nginx server also needs to do some simple configuration:

gzip_static on;
Copy the code

The packaging results after using the compression-webpack-plugin are as follows:

The.gz figure is a compressed version of a resource file.

Once the configuration is successful, redeploy the code to nginx and reload the Nginx configuration.

Let’s see that gzip_static off is not enabled; Gzip access:

Let’s look directly at the two largest files chunk-Vendors. js and chunk-vendors. CSS, which are 748KB and 194KB in size, and take 622ms and 247ms to load, respectively.

Then we open gzip gzip_static on; Let’s look at the load of the resource:

Also chunk-vendor. js and chunk-vendor.css, their sizes become 190KB and 29.3KB, and their load times become 245ms and 46ms.

That’s what gzip looks like.

Vuex dynamic registration module

Vuex usually uses static modules, which are packaged into app.js, but if a module is too large and not immediately needed, we can dynamically register the module into Vuex.

Only register when using a vuEX module:

mounted () { 
    this.$store.registerModule('myModule', MyModule)
}
Copy the code

Unregister the module when not in use:

beforeDestroy () {
    this.$store.unregisterModule('myModule')}Copy the code

The effect is that the page downloads the module content when it loads, rather than when it visits the site.

tool

When packaging, we can use some auxiliary tools to help analyze the packaging situation and focus on optimization.

Open Chrome debug mode, CMD+SHIFT+P bring up the command panel, type Coverage, select Show Coverage, then visit the specified site, click Instrument Converge to view the unused usage of loaded resources.

Use the Webpack-bundle-Analyzer plug-in to analyze the packaged file structure.

NPM I -d webpack-bundle-Analyzer, vue. Config. js

  chainWebpack: config= > {
    if (process.env.NODE_ENV === 'production') {
      if (process.env.npm_config_report) {
        config
          .plugin('webpack-bundle-analyzer')
          .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
          .end()
        config.plugins.delete('prefetch')}}}Copy the code

Then use the NPM run build –report command to package and see the structure of the packed file.

This packed file structure page is a valuable reference for optimizing packaging.

Refer to the article

Vue performance Optimization: How to implement lazy loading and code splitting? Vue-cli3 +nginx configures gzip compression