Vue project performance optimization-practical guide

Preface

Through the two-way data binding and virtual DOM technology, the Vue framework helps us deal with the dirtiest and most tiring part of DOM operation in front-end development. We no longer need to think about how to operate the DOM and how to operate the DOM most efficiently; but still in the Vue project There are problems such as optimization of the first screen of the project and optimization of Webpack compilation configuration, so we still need to pay attention to the optimization of the performance of the Vue project, so that the project has more efficient performance and better user experience. This article is summarized by the author through the optimization practice of actual projects. I hope that readers will have some enlightening thinking after reading this article, so as to help optimize their own projects. The content of this article is divided into the following three parts:

  • Optimization of Vue code level;
  • Optimization of webpack configuration level;
  • Optimization of the basic Web technology level.

I have worked hard to organize for a long time, and I hope to manually like and encourage~

The github address is: http://github.com/fengshi123/ …, a summary of all the author’s blogs, and welcome to follow and star~

1. Optimization at the code level

1.1, v-if and v-show distinguish the usage scenarios

v-if  is  true  conditional rendering, because it will ensure that the event listeners and subcomponents in the conditional block are properly destroyed and rebuilt during the switching process; it is also lazy : if the condition is false during the initial rendering, nothing is done Do-the conditional block will not be rendered until the condition becomes true for the first time.

v-show  is much simpler, no matter what the initial condition is, the element will always be rendered, and it is simply switched based on the CSS display property.

Therefore, v-if is suitable for scenarios that rarely change conditions during runtime and do not need to switch conditions frequently; v-show is suitable for scenarios that require very frequent switching of conditions.

1.2. Differentiating usage scenarios between computed and watch

 Computed : It is a calculated attribute, which depends on other attribute values, and the calculated value is cached. Only when the attribute value it depends on changes, the computed value will be recalculated the next time the computed value is obtained;

watch: It is  more of the function of "observation", similar to the monitoring callback of some data, whenever the monitored data changes, the callback will be executed for subsequent operations;

Application scenarios:

  • When we need to perform numerical calculations and rely on other data, we should use computed, because we can use the cache feature of computed to avoid having to recalculate every time we get a value;
  • When we need to perform asynchronous or expensive operations when the data changes, we should use watch. Using the watch option allows us to perform asynchronous operations (access to an API), limiting the frequency with which we perform the operation, and before we get the final result , Set the intermediate state. These are things that calculated properties cannot do.

1.3, v-for traversal must add key to item, and avoid using v-if at the same time

(1) V-for traversal must add key to item

When the list data is traversed and rendered, a unique key value needs to be set for each item, so that the internal mechanism of Vue.js can accurately find the list data. When the state is updated, the new state value is compared with the old state value, and the diff is located faster.

(2) v-for traversal avoid using v-if at the same time

v-for has a higher priority than v-if. If you need to traverse the entire array every time, it will affect the speed, especially when a small part of it needs to be rendered, it should be replaced with the computed property if necessary.

recommend:

<ul>
 <li
 v-for="user in activeUsers"
 :key="user.id">
 {
   
   { user.name }}
 </li>
</ul>
computed: {
 activeUsers: function () {
 return this.users.filter(function (user) {
	 return user.isActive
 })
 }
}
复制代码

Not recommended:

<ul>
 <li
 v-for="user in users"
 v-if="user.isActive"
 :key="user.id">
 {
   
   { user.name }}
 </li>
</ul>
复制代码

1.4, long list performance optimization

Vue will hijack the data through Object.defineProperty to realize that the view responds to data changes. However, sometimes our components are pure data display and there will be no changes. We don’t need Vue to hijack our data. In the case of data display, this can significantly reduce the initialization time of the component. How to prohibit Vue from hijacking our data? You can freeze an object through the Object.freeze method. Once the object is frozen, it can no longer be modified.

export default {
 data: () => ({
 users: {}
 }),
 async created() {
 const users = await axios.get("/api/users");
 this.users = Object.freeze(users);
 }
};
复制代码

1.5. Destruction of the event

When a Vue component is destroyed, it will automatically clean up its connection with other instances, unbind all its instructions and event listeners, but only for the events of the component itself. If you use addEventListene and other methods in js, it will not be automatically destroyed. We need to manually remove the listeners of these events when the component is destroyed to avoid memory leaks, such as:

created() {
 addEventListener('click', this.click, false)
},
beforeDestroy() {
 removeEventListener('click', this.click, false)
}
复制代码

1.6, lazy loading of image resources

For pages with too many images, in order to speed up the page loading speed, in many cases we need to load the images that are not in the visible area on the page first, and then load them after scrolling to the visible area. This will greatly improve the page loading performance and also improve the user experience. We use Vue's vue-lazyload plugin in the project:

(1) Install the plug-in

npm install vue-lazyload --save-dev
复制代码

(2) Introduce and use in the entry file man.js

import VueLazyload from 'vue-lazyload'
复制代码

Then use it directly in vue

Vue.use(VueLazyload)
复制代码

Or add custom options

Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png',
loading: 'dist/loading.gif',
attempt: 1
})
复制代码

(3) Change the src attribute of the img tag directly to v-lazy in the vue file, thereby changing the image display mode to lazy loading display:

<img v-lazy="/static/img/1.png">
复制代码

The above is a simple use of the vue-lazyload plugin. If you want to see more parameter options of the plugin, you can check the github address of vue-lazyload.

1.7, routing lazy loading

Vue is a single-page application, and there may be many routes introduced. In this way, the files packaged with webpcak are very large. When entering the homepage, too many resources are loaded, and the page will appear blank, which is not conducive to the user experience. If we can divide the components corresponding to different routes into different code blocks, and then load the corresponding components when the routes are accessed, it will be more efficient. This will greatly increase the speed of the first screen display, but the speed of other pages may be reduced.

Lazy loading of routing:

const Foo = () => import('./Foo.vue')
const router = new VueRouter({
 routes: [
 { path: '/foo', component: Foo }
 ]
})
复制代码

1.8. On-demand introduction of third-party plug-ins

We often need to introduce third-party plug-ins in our projects. If we directly introduce the entire plug-in, the size of the project will be too large. We can use babel-plugin-component to introduce only the required components to reduce the size of the project. the goal of. The following is an example of introducing element-ui component library into the project:

(1) First, install babel-plugin-component:

npm install babel-plugin-component -D
复制代码

(2) Then, modify .babelrc to:

{
 "presets": [["es2015", { "modules": false }]],
 "plugins": [
 [
 "component",
 {
 "libraryName": "element-ui",
 "styleLibraryName": "theme-chalk"
 }
 ]
 ]
}
复制代码

(3) Introduce some components in main.js:

import Vue from 'vue';
import { Button, Select } from 'element-ui';
 Vue.use(Button)
 Vue.use(Select)
复制代码

1.9. Optimize the performance of unlimited lists

If your application has a very long or infinite scrolling list, then you need to use windowing technology to optimize performance, and only need to render a small part of the area, reducing the time to re-render components and create dom nodes. You can refer to the following open source projects vue-virtual-scroll-list and vue-virtual-scroller to optimize this infinite list scene.

1.10, server-side rendering SSR or pre-rendering

Server-side rendering means that the work of Vue rendering the entire html fragments of tags on the client side is completed on the server side, and the html fragments formed on the server side are directly returned to the client side. This process is called server-side rendering.

(1) Advantages of server-side rendering:

  • Better SEO: Because the content of the SPA page is obtained through Ajax, and the search engine crawling tool does not wait for the asynchronous completion of Ajax to crawl the page content, so the page cannot be crawled in the SPA. The page is obtained through Ajax Content; and SSR directly returns the rendered page from the server (the data is already contained in the page), so search engine crawling tools can crawl the rendered page;
  • Faster content arrival time (faster loading on the first screen): SPA will wait for all Vue compiled js files to be downloaded before starting to render the page. File downloading takes a certain amount of time, so the first screen rendering needs A certain period of time; SSR is directly rendered by the server and the page is directly returned to display, without waiting for the download of js files and rendering, so SSR has a faster content arrival time;

(2) Disadvantages of server-side rendering:

  • More restrictions on development conditions: For example, server-side rendering only supports two hook functions beforeCreate and created, which will cause some external extension libraries to require special processing before they can run in server-side rendering applications; and can be deployed in any static file The completely static single-page application SPA on the server is different. The server-side rendering application needs to be in the Node.js server running environment;
  • More server load: Rendering a complete application in Node.js will obviously consume more CPU resources than a server that only provides static files. Therefore, if you expect to use it in a high-traffic environment, please prepare the corresponding server load. And use caching strategies wisely.

If your project’s SEO and first-screen rendering are the key indicators for evaluating the project, then your project needs server-side rendering to help you achieve the best initial loading performance and SEO. For specific Vue SSR implementation, please refer to the author’s Another article "Vue SSR Trampled Tour". If your Vue project only needs to improve the SEO of a few marketing pages (such as /, /about, /contact, etc.), then you may need to pre-render and simply generate static HTML files for specific routes at build time. The advantage is that it is easier to set up pre-rendering, and you can use your front end as a completely static site. Specifically, you can use prerender-spa-plugin to easily add pre-rendering.

2. Optimization at the Webpack level

2.1, Webpack compresses pictures

In the vue project, except that the limit size can be set in the url-loader in webpack.base.conf.js to process images, the images smaller than the limit are converted to base64 format, and the rest is not operated. Therefore, for some larger image resources, when requesting resources, the loading will be very slow. We can use image-webpack-loader to compress the images:

(1) First, install image-webpack-loader:

npm install image-webpack-loader --save-dev
复制代码

(2) Then, configure in webpack.base.conf.js:

{
 test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
 use:[
 {
 loader: 'url-loader',
 options: {
 limit: 10000,
 name: utils.assetsPath('img/[name].[hash:7].[ext]')
 }
 },
 {
 loader: 'image-webpack-loader',
 options: {
 bypassOnDebug: true,
 }
 }
 ]
}
复制代码

2.2, reduce the redundant code of ES6 to ES5

The Babel plugin will inject some auxiliary functions when converting ES6 code into ES5 code, such as the following ES6 code:

class HelloWebpack extends Component{...}
复制代码

When this code is converted into ES5 code that can run normally, the following two auxiliary functions are needed:

babel-runtime/helpers/createClass // 用于实现 class 语法
babel-runtime/helpers/inherits // 用于实现 extends 语法 
复制代码

By default, Babel will embed these dependent helper function codes in each output file. If multiple source code files rely on these helper functions, the codes of these helper functions will appear many times, resulting in code redundancy. . In order to prevent the code of these helper functions from reappearing, you can import them through require('babel-runtime/helpers/createClass') when relying on them, so that they can only appear once. The babel-plugin-transform-runtime plug-in is used to achieve this effect, by replacing related auxiliary functions with import statements, thereby reducing the file size of the code compiled by babel.

(1) First, install babel-plugin-transform-runtime:

npm install babel-plugin-transform-runtime --save-dev
复制代码

(2) Then, modify the .babelrc configuration file to:

"plugins": [
 "transform-runtime"
]
复制代码

If you want to see more details of the plug-in, you can check the detailed introduction of babel-plugin-transform-runtime.

2.3, extract public code

If the third-party libraries and public modules of each page are not extracted in the project, the project will have the following problems:

  • The same resources are repeatedly loaded, wasting user traffic and server costs.
  • The resources that need to be loaded for each page are too large, resulting in slow loading of the first screen of the web page and affecting user experience.

Therefore, we need to extract the common code of multiple pages into separate files to optimize the above problems. Webpack has built-in CommonsChunkPlugin which is specially used to extract the common parts of multiple Chunks. The configuration of CommonsChunkPlugin in the project is as follows:

// 所有在 package.json 里面依赖的包,都会被打包进 vendor.js 这个文件中。
new webpack.optimize.CommonsChunkPlugin({
 name: 'vendor',
 minChunks: function(module, count) {
 return (
 module.resource &&
 /\.js$/.test(module.resource) &&
 module.resource.indexOf(
 path.join(__dirname, '../node_modules')
 ) === 0
 );
 }
}),
// 抽取出代码模块的映射关系
new webpack.optimize.CommonsChunkPlugin({
 name: 'manifest',
 chunks: ['vendor']
})
复制代码

If you want to see more details of the plug-in, you can view the detailed introduction of CommonsChunkPlugin.

2.4, template precompilation

When using a template in the DOM or a string template in JavaScript, the template will be compiled into a rendering function at runtime. Normally this process is fast enough, but it is better to avoid this usage for performance-sensitive applications.

The easiest way to precompiled templates is to use single-file components-related build settings will automatically handle precompilation, so the built code already contains the compiled rendering function instead of the original template string.

If you use webpack and like to separate JavaScript and template files, you can use vue-template-loader, which can also convert template files into JavaScript rendering functions during the build process.

2.5, extract the CSS of the component

When using a single-file component, the CSS in the component will be dynamically injected through JavaScript in the form of style tags. This has some small runtime overhead, and if you use server-side rendering, this will cause a period of "flickering of unstyled content (fouc)". Extracting the CSS of all components to the same file can avoid this problem, and it will also allow CSS to be better compressed and cached.

Check out the respective documentation of this build tool to learn more:

  • webpack + vue-loader (The webpack template of vue-cli has been pre-configured)
  • Browserify + vueify
  • Rollup + rollup-plugin-vue

2.6, optimize SourceMap

After we package the project, we will package the code of multiple files under development into one file, and after compressing, removing extra spaces, and compiling with babel, the compiled code will finally be used in the online environment, then There will be a big difference between the processed code and the source code. When there is a bug, we can only locate the compressed code location, but cannot locate the code in the development environment. It is not easy to adjust the positioning for development. The problem, so sourceMap appeared, it is to solve the problem of bad debugging code.

The optional values ​​of SourceMap are as follows (the more + signs, the faster the speed, the more-signs, the slower the speed, and o means medium speed)

 

 

Recommended development environment: cheap-module-eval-source-map

Production environment recommendation: cheap-module-source-map

The reasons are as follows:

  • cheap : The column information in the source code has no effect. Therefore, the packaged file does not want to contain column-related information. Only the row information can establish a dependency before and after packaging. Therefore, whether it is a development environment or a production environment, we want to add the basic type of cheap to ignore the column information before and after packaging;
  • module  : Whether it is a development environment or a formal environment, we all hope to locate the specific location of the source code of the bug. For example, if a certain Vue file reports an error, we hope to locate the specific Vue file, so we also need module configuration;
  • soure-map  : source-map will generate a separate soucemap file for each packaged module, so we need to increase the source-map attribute;
  • eval-source-map : The speed of eval packaging code is very fast, because it does not generate a map file, but you can use eval-source-map in combination with eval. The map file will be stored in the packaged js file in the form of DataURL. Do not use eval-source-map in a formal environment, because it will increase the size of the file, but in a development environment, you can try it out because they are packaged quickly.

2.7, output analysis of construction results

The code output by Webpack is very poorly readable and the file is very large, which gives us a lot of headaches. In order to analyze the output results more simply and intuitively, many visual analysis tools have appeared in the community. These tools display the results more intuitively in a graphical manner, allowing us to quickly understand the problem. Next, explain the analysis tool we used in the Vue project: webpack-bundle-analyzer.

We configure webpack.prod.conf.js in the project:

if (config.build.bundleAnalyzerReport) {
 var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
 webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}
复制代码

After executing $ npm run build --report, the analysis report is generated as follows:

 

 

2.8, Vue project compilation optimization

If your Vue project is compiled with Webpack and requires you to have a cup of coffee, then maybe you need to optimize the project's Webpack configuration to improve the efficiency of Webpack construction. For details on how to optimize the Webpack construction of the Vue project, you can refer to another article by the author "Vue Project Webpack Optimization Practice"

Three, basic Web technology optimization

3.1, turn on gzip compression

gzip is the abbreviation of GNUzip, which was first used for file compression in UNIX systems. The gzip encoding on the HTTP protocol is a technology used to improve the performance of web applications. The web server and client (browser) must support gzip together. The current mainstream browsers, Chrome, firefox, IE, etc. all support this protocol. Common servers such as Apache, Nginx, and IIS also support it. The gzip compression efficiency is very high, usually reaching a compression rate of 70%. That is to say, if your web page has 30K, it will become about 9K after compression.

Let’s take the server-side express as an example. It’s very simple to start gzip. The steps are as follows:

  • installation:
npm install compression --save
复制代码
  • Add code logic:
var compression = require('compression');
var app = express();
app.use(compression())
复制代码
  • Restart the service and observe the response header in the network panel. If you see the following fields in the red circle, it means that gzip has been opened successfully:

 

 

  •  

3.2, browser cache

In order to improve the speed of loading pages for users, it is necessary to cache static resources. According to the need to re-initiate requests to the server to classify, the HTTP caching rules are divided into two categories (mandatory caching, vs. caching). If the caching mechanism is It's not very clear yet. You can refer to the author's article on HTTP caching "In-depth understanding of HTTP caching mechanisms and principles", which will not be repeated here.

3.3, the use of CDN

The browser must connect to the server when downloading CSS, js, and pictures from the server. Most servers have limited bandwidth. If the limit is exceeded, the web page will not be able to respond for half a day. CDN can load files through different domain names, which greatly increases the number of concurrent connections for downloading files, and CDN has better usability, lower network delay and packet loss rate.

3.4. Use Chrome Performance to find performance bottlenecks

Chrome's Performance panel can record js execution details and time within a period of time. The steps to analyze page performance using Chrome Developer Tools are as follows.

  1. Open Chrome Developer Tools and switch to the Performance panel
  2. Click Record to start recording
  3. Refresh the page or expand a node
  4. Click Stop to stop recording

 

 

More information about Performance can be found here.

to sum up

This article is composed of the following three parts: optimization at the Vue code level, optimization at the webpack configuration level, and optimization at the basic web technology level; to introduce how to optimize the performance of the Vue project. I hope it will be helpful and enlightening to you after reading this article. If there are any shortcomings, please criticize and correct!

Guess you like

Origin blog.csdn.net/liuhao9999/article/details/114927092