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:
app.css
: Compressed merged style file.app.js
: Mainly includes items in the projectApp.vue
,main.js
,router
,store
And so on.vendor.js
: mainly includes projects dependent on such asvuex
.axios
This 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.Numbers. Js
: Starts with a digit such as 0, 1, 2, or 3js
These 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 onejs
File.mainfest.js
:mainfest
In 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 3G
Under theNetwork
Graph (running locallynginx
On 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.
- Will rely on the
vue
,vue-router
,vuex
,element-ui
andaxios
These five libraries, all changed to passCDN
Link to. With the help ofHtmlWebpackPlugin
Can be conveniently used in the loop syntaxindex.html
In the insertjs
andcss
theCDN
Link. Here,CDN
Most 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
- in
build/webpack.base.conf.js
Add the following code to make this in useCDN
In the case of importing external files, they can still be used in the projectimport
To introduce these third-party libraries, which means you don’t need to change the project code, the key name here isimport
thenpm
The 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
- Undependent
npm
Package,npm uninstall axios element-ui vue vue-router vuex
- delete
main.js
In theelement-ui
Related code.
See git history for details
2.1 Comparing files built before and after the CDN is added:
After the optimization:
app.css
: because it no longer passesimport 'element-ui/lib/theme-chalk/index.css'
, but directly throughCDN
Link inelement-ui
Style, so that the file is smallbytes
Level, as it now only contains a small number of itemscss
.app.js
: Almost no change, because it is mainly their own business code.vendor.js
: Will 5 dependenciesjs
All toCDN
Link, has been small to insufficient1KB
In fact, there are no third-party libraries.Numbers. Js
andmainfest.js
: These files are so small that changes are almost negligible.
2.2 Again, disable browser caching and limit the network speed toFast 3G
Under theNetwork
Graph (running locallynginx
On 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?gzip
The 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 setupnode
thegzip
service
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
- perform
npm i express compression
- Create one in the project root directory
serve.js
And 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
- perform
node 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
- You will need to
router
Set tohistory
Mode, and adjust the server configuration accordingly,It’s not complicated. 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
- in
build/webpack.prod.conf.js
Add 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
- will
config/index.js
In thebuild
In theassetsPublicPath
The 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 onstatic
Directory, 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-plugin
Warehouse mentionedISSUE
But with the help ofpostProcess
You can also write your own regular expression, if you have this requirement, can refer to the followingLazy route loading caused pits). - Adjust the
main.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:
- Do not use lazy route loading.
- will
HtmlWebpackPlugin
theinject
The field is set to'head'
So that theapp.js,vendor.js,manifest.js
It’s going to be inserted intohead
And in asynchronousscript
On the label. But because of the ordinaryscript
Is synchronous, until they are all loaded, the page cannot be rendered, which is a violationprerender
And you need to be rightmain.js
Make the following modifications to ensure thatVue
You 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.