This article is permalink: github.com/HaoChuan942…

preface

These optimizations apply to Vue CLI 2 and Vue CLI 3, this article is mainly based on Vue CLI 2, I have put in the Vue cli3-optimization repository, With detailed comments and a convenient loader for Sass to use, you don’t need to use Sass to introduce variables and mixins, and you don’t need to bother with @import every time. The practical methods and effects of these optimization schemes will be described in detail below:

Like many partners, I was also based on the webpack template of the official vue-cli@2 when developing Vue project. However, as the project gets bigger and bigger, I rely on more and more third-party NPM packages, and the files after the construction will also become bigger and bigger, especially vvendor. Js, even reaching about 2M. Coupled with the fact that it’s a single-page application, this can lead to long periods of white screen time on slow network speeds or with limited server bandwidth. To solve this problem, I did some exploring and found three optimizations that made a significant difference with very little change to the business code — CDN + Gzip + Prerender. I’ve put these together on the Github repository, with the intent of showing the different branches of optimization that can affect the performance of a Vue project. You can clone it and try it out, and thanks to Git history, you can easily see the details of the changes. I’ll show you the effects of these three optimizations with a simple project.

First, prepare oneSimple projects

Generated by the webpack template of vue-cli@2, it contains only the most basic Vue three sets ———— Vue, Vue-Router, vuex, and the commonly used Element-UI and Axios. Split the two routes — “home page” and “Contacts” — and get a list of contacts asynchronously through Axios and display them using an Elemental-UI table. Build directly, do not do any optimization, for reference.

1.1 Post-build file Description:

  1. app.css: Compressed merged style file.
  2. app.js: Mainly includes items in the projectApp.vue,main.js,router,storeAnd so on.
  3. vendor.js: mainly includes projects dependent on such asvuex.axiosThis is why this file is so large. The next step will be to explore how to optimize this piece, after all, with the development of the project, the library can also rely on more and more.
  4. Numbers. Js: Starts with a digit such as 0, 1, 2, or 3jsThese files are blocks of code that are shred out of each route, because I split the two routes and did thatThe route was loaded lazily. ProcedureSo there are two zeros and onejsFile.
  5. mainfest.js:mainfestIn English there areList, list, list, which contains the logic to load and process the routing module

1.2 Disable the Browser Cache and set the network speed toFast 3GUnder theNetworkGraph (running locallynginxOn the server

You can see that the unoptimized base version takes more than 7 seconds to load on Fast 3G

Second,CDN optimization

For better development experience and error capture, we have made a distinction between dev and build. See git records for details. The following is for reference only.

  1. Will rely on thevue,vue-router,vuex,element-uiandaxiosThese five libraries, all changed to passCDNLink to. With the help ofHtmlWebpackPluginCan be conveniently used in the loop syntaxindex.htmlIn the insertjsandcsstheCDNLink. Here,CDNMost usedjsDelivrProvide.
<! -- CDN file, config/index.js --> <%for (var i in htmlWebpackPlugin.options.css) { %>
<link href="<%= htmlWebpackPlugin.options.css[i] %>" rel="stylesheet"> <%} %> <%for (var i in htmlWebpackPlugin.options.js) { %>
<script src="<%= htmlWebpackPlugin.options.js[i] %>"></script>
<% } %>
Copy the code
  1. inbuild/webpack.base.conf.jsAdd the following code to make this in useCDNIn the case of importing external files, they can still be used in the projectimportTo introduce these third-party libraries, which means you don’t need to change the project code, the key name here isimportthenpmThe package name, with the key being the global variable exposed by the library.Webpack documentation reference links.
  externals: {
    'vue': 'Vue'.'vue-router': 'VueRouter'.'vuex': 'Vuex'.'element-ui':'ELEMENT'.'axios':'axios'
  }
Copy the code
  1. UndependentnpmPackage,npm uninstall axios element-ui vue vue-router vuex
  2. deletemain.jsIn theelement-uiRelated code.

See git history for details

2.1 Comparing files built before and after the CDN is added:

After the optimization:

  1. app.css: because it no longer passesimport 'element-ui/lib/theme-chalk/index.css', but directly throughCDNLink inelement-uiStyle, so that the file is smallbytesLevel, as it now only contains a small number of itemscss.
  2. app.js: Almost no change, because it is mainly their own business code.
  3. vendor.js: Will 5 dependenciesjsAll toCDNLink, has been small to insufficient1KBIn fact, there are no third-party libraries.
  4. Numbers. Jsandmainfest.js: These files are so small that changes are almost negligible.

2.2 Again, disable browser caching and limit the network speed toFast 3GUnder theNetworkGraph (running locallynginxOn the server

It can be seen that under the same network environment, the loading speed has increased from more than 7 seconds to more than 3 seconds now, which is a very significant improvement. But more important is the original way, all of the js and CSS and other static resources are request our own nginx server, and now most of the static resource request is third-party CDN resource, it can not only bring the speed improvements, at the time of high concurrency, this undoubtedly greatly reduce the bandwidth of the server pressure, Imagine that the first screen was 900 KB of files and now there are only 20KB left to request from your own server!

Three,Gzip optimization

Two obvious benefits of using Gzip are that you can reduce storage space and that you can reduce transfer time when transferring files over the network.

3.1 How Can I Enable this Function?gzipThe compression

You can enable GZIP by modifying the server configuration. Take the Nginx server as an example. The following figure shows a Network comparison with the same set of code and only changing the gZIP status of the server

Gzip compression is not enabled:

To enable gZIP compression:

Response header after GZIP compression is enabled

As you can see from the figure above, there is a three to four times difference in file size before and after opening gzip, and the loading speed has increased from over 7 seconds to over 3 seconds

The configuration method of Nginx is attached

http {
  gzip on;
  gzip_static on;
  gzip_min_length 1024;
  gzip_buffers 4 16k;
  gzip_comp_level 2;
  gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
  gzip_vary off;
  gzip_disable "MSIE [1-6]\.";
}
Copy the code

3.2 What can the front-end do for Gzip

We all know that there is a productionGzip option in config/index.js, so what does it do? Let’s try NPM install –save-dev [email protected], set productionGzip to true, re-build, and put it on the nginx server to see what the difference is:

Gz and css.gz files, and vendor.js is smaller because we have nginx’s gzip_static on enabled. Option, if gzip_static is set to on, then the.gz file with the same name will be used without using the server’s CPU resources to compress it.

3.3 Quick front-end setupnodethegzipservice

Front-end partners who are unable to set up an Nginx environment can also follow these steps to quickly start an Express server with Gzip

  1. performnpm i express compression
  2. Create one in the project root directoryserve.jsAnd paste the following code
  var express = require('express')
  var app = express()

  // Enable gzip compression. If you want to close gzip, comment out the next two lines and re-execute 'node server.js'
  var compression = require('compression')
  app.use(compression())

  app.use(express.static('dist'))
  app.listen(3000.function () {
    console.log('server is runing on http://localhost:3000')})Copy the code
  1. performnode server.js

Here is the response header for Express with gzip enabled:

Four,Prerender pre-rendered

As you all know, the common Vue single-page application builds index.html as a blank page containing the root node. Once all the required JS is loaded, the vnode is parses and created, and then the real DOM is rendered. When these JS files are too large and slow network speed or unexpected error, there will be the so-called white screen, I believe that do Vue development partners must have encountered this situation. And a big downside to single-page apps is that they’re not SEO friendly. So how to solve these problems? SSR is certainly a good solution, but there are learning costs and maintenance costs involved, and if you already have a vue single-page app, switching to SSR is not a seamless process. Then pre-render is more appropriate. Both of these problems can be solved by installing a WebPack plugin and a few simple WebPack configurations.

4.1 How do I Convert a Single-page Application to Pre-render

  1. You will need torouterSet tohistoryMode, and adjust the server configuration accordingly,It’s not complicated.
  2. npm i prerender-spa-plugin --save-dev

Attention!! Pre-render requires Chromium download, and as you know, Google stuff can’t be downloaded in China, so I added.npmrc file to the root directory to use Taobao image download. Reference links. If your terminal can flip out of the country, skip this step and you may prefer a small plane

  1. inbuild/webpack.prod.conf.jsAdd the following configuration (no route is lazy loaded).
  const PrerenderSPAPlugin = require('prerender-spa-plugin')... new PrerenderSPAPlugin({staticDir: config.build.assetsRoot,
    routes: [ '/'.'/Contacts'].// Routes that need to be pre-rendered (depending on your project)
    minify: {
      collapseBooleanAttributes: true.collapseWhitespace: true.decodeEntities: true.keepClosingSlash: true.sortAttributes: true}})Copy the code
  1. willconfig/index.jsIn thebuildIn theassetsPublicPathThe field is set to'/'This is because when you use pre-render, the routing component is compiled into the corresponding folderindex.html, it will depend onstaticDirectory, while using relative paths will lead to dependency path errors, which also requires that pre-rendered projects are best placed in the root directory of the site (this pit I have inprerender-spa-pluginWarehouse mentionedISSUEBut with the help ofpostProcessYou can also write your own regular expression, if you have this requirement, can refer to the followingLazy route loading caused pits).
  2. Adjust themain.js
  new Vue({
    router,
    store,
    render: h= > h(App)
  }).$mount('#app'.true) // https://ssr.vuejs.org/zh/guide/hydration.html
Copy the code

When you run NPM run build, you’ll notice that the dist directory is different. Not only is there a folder with the same name as the specified route, but the index. HTML already renders the static page.

4.2 What is the effect?

As before, we still disable caching and limit the network speed to Fast 3G(running on the local Nginx server). As you can see, the page is fully rendered before vvendor. Js is loaded (about 700 kB, but only 200 kB is loaded at this time). In fact, as long as the index.html and app.css are loaded, the static content of the page is rendered perfectly. Pre-render is definitely a good choice for these pages with a lot of static content.

4.3 Crater Caused by Lazy Route Loading

If your project does not do lazy routing, then you can safely follow the above instructions. But if it is used in your project, you should see an error saying webpackJsonp is not defined. This is because when the prerender-spa-plugin renders a static page, Will also like < script SRC = “/ static/js / 0.9231 fc498af773fb2628. Js” type = “text/javascript” async Charset =” UTF-8 “> Such an asynchronous script tag is injected into the generated HTML head tag. This can lead to it before the app. Js, vendor. Js, the manifest. Js (located at the bottom of the body). (Async just doesn’t block later DOM parsing, which doesn’t mean it executes last). When the js is loaded, the asynchronous script tag is created repeatedly in the head tag. Although this error will not affect the application, it is best not to render these asynchronous components directly into the final HTML. Fortunately, the Prerender-spa-Plugin provides the postProcess option to process the HTML file once before actually generating it, so I use a simple regular expression to strip out the asynchronous script tags. This branch already uses lazy routing. You can look directly at Git history and compare the changes in the file and base branch to adjust your project accordingly.

  postProcess (renderedRoute) {
    renderedRoute.html = renderedRoute.html = renderedRoute.html.replace(/<script[^<]*src="[^<]*[0-9]+\.[0-9a-z]{20}\.js"><\/script>/g.function (target) {
      console.log(chalk.bgRed('\n\n removes lazy loaded tags :'), chalk.magenta(target))
      return ' '
    })
    return renderedRoute
  }
Copy the code

In addition to this solution, there are two unrecommended solutions:

  1. Do not use lazy route loading.
  2. willHtmlWebpackPlugintheinjectThe field is set to'head'So that theapp.js,vendor.js,manifest.jsIt’s going to be inserted intoheadAnd in asynchronousscriptOn the label. But because of the ordinaryscriptIs synchronous, until they are all loaded, the page cannot be rendered, which is a violationprerenderAnd you need to be rightmain.jsMake the following modifications to ensure thatVueYou can find it at instantiation time<div id="app"></div>, and mount it correctly.
    const app = new Vue({
      // ...
    })
    document.addEventListener('DOMContentLoaded'.function () {
      app.$mount('#app')})Copy the code

conclusion

While the official scaffolding already offers a number of out-of-the-box optimizations, such as CSS compression and merge, JS compression and modularization, small images to Base64, and so on, there’s a lot more we can do. I didn’t mention the details of code level optimization, but I hope to give you some practical solutions. All three of these options will, in one way or another, bring some benefit to your project. Optimization is also a metaphysical science, there are many things to study. I also hope that other partners can provide valuable comments in the comment section, or directly submit PR to my project vue-Optimization base branch, I will adopt and sort out good solutions. So far I’ve put the final result of the integration of the three solutions under the Master branch, and you can clone it and develop your project based on it.