preface

The Vue framework helps us deal with the most dirty and tiring PART of the DOM operation in front-end development through two-way data binding and virtual DOM technology. We no longer need to think about how to operate the DOM and how to operate it most efficiently. However, there are still some problems in the Vue project, such as the optimization of the first screen of the project, the optimization of Webpack compilation and configuration, so we still need to pay attention to the optimization of the performance of the Vue project, so as to make the project have 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 inspiration to think after reading this article, so as to help them optimize their own projects. This paper is divided into the following three parts:

  • Vue code level optimization;
  • Optimization of webPack configuration layer;
  • Basic Web technology level optimization.

Hard to arrange for a long time, but also hope to manually thumbs up to encourage ~

Github: github.com/fengshi123/… , summarized all the author’s blog, also welcome to follow and star ~

1. Code level optimization

1.1 V-IF and V-show are used in different scenarios

V-if is true conditional rendering because it ensures that event listeners and child components within the conditional block are properly destroyed and rebuilt during the toggle; It is also lazy: if the condition is false at the initial render, nothing is done — the condition block is rendered until it becomes true for the first time.

V-show is much simpler, the element is always rendered regardless of the initial condition, and simply switches based on the DISPLAY property of the CSS.

Therefore, V-if is suitable for scenarios where conditions are rarely changed at runtime and do not need to be switched frequently; V-show is suitable for scenarios that require very frequent switching conditions.

1.2 Use scenarios for computed and Watch

Computed: The value of computed is cached. Only when the value of the dependent attribute changes, the value of computed is recalculated the next time the value of computed is obtained.

Watch: More of a “watch” function, similar to the listening callback of some data, every time the listening data changes, the callback will be executed for subsequent operations;

Application scenario:

  • Use Computed when you need to perform numeric computations that depend on other data because the caching feature of computed avoids having to recalculate the value every time it is fetched.

  • Watch should be used when we need to perform an asynchronous or expensive operation when data changes. Using the Watch option allows us to perform an asynchronous operation (access an API), limits how often we perform the operation, and sets the intermediate state until we get the final result. These are things that computational properties can’t do.

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

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

In the traversal rendering of the list data, it is necessary to set a unique key value 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 to the old state value, locating diff more quickly.

(2) V-for traversal avoids 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 if you need to render a small portion of the array. Replace it with computed if necessary.

Recommendation:

<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
    })
  }
}
Copy the code

Is not recommended:

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id">
    {{ user.name }}
  </li>
</ul>
Copy the code

1.4 Long list performance optimization

Vue will hijack the data through object.defineProperty to enable the view to respond to changes in the data. However, sometimes our components are just displaying the data without any changes, so we don’t need Vue to hijack our data. In the case of large data display, This can significantly reduce component initialization time, so how do we prevent Vue from hijacking our data? You can freeze an Object by using the object.freeze method. Once the Object is frozen, it cannot be modified.

export default {
  data: (a)= > ({
    users: {}}),async created() {
    const users = await axios.get("/api/users");
    this.users = Object.freeze(users); }};Copy the code

1.5 Destruction of events

When a Vue component is destroyed, it automatically cleans up its connections to other instances and unbinds all of its instructions and event listeners, but only the component’s own events. If we use addEventListene and other methods in JS, we need to manually remove the listener for these events when the component is destroyed, so as to avoid memory leak. For example:

created() {
  addEventListener('click'.this.click, false)
},
beforeDestroy() {
  removeEventListener('click'.this.click, false)}Copy the code

1.6. Image resources are loaded lazily

For pages with too many pictures, in order to speed up the page loading speed, so many times we need to do not load the images that do not appear in the visual area of the page, wait until the scrolling to the visual area to load. This will greatly improve the page loading performance and improve the user experience. We used Vue’s Vue-Lazyload plugin for this project:

(1) Install the plug-in

npm install vue-lazyload --save-dev
Copy the code

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

import VueLazyload from 'vue-lazyload'
Copy the code

Then use it directly in vue

Vue.use(VueLazyload)
Copy the code

Or add custom options

Vue.use(VueLazyload, {
preLoad: 1.3.error: 'dist/error.png'.loading: 'dist/loading.gif'.attempt: 1
})
Copy the code

(3) In the vue file, change the SRC attribute of img tag to V-lazy, so as to change the image display mode to lazy loading display:

<img v-lazy="/static/img/1.png">
Copy the code

This is a simple example of vue-lazyload. To see more options, check out vue-lazyload’s Github address.

1.7. Routes are loaded lazily

Vue is a single-page application, and many routes may be imported. In this way, the files packaged by WebPCAk are large. When entering the home page, too many resources are loaded, and the page will appear white screen, which is not good for user experience. It would be more efficient if we could split the components of different routes into separate code blocks and then load the components when the route is accessed. This will greatly speed up the first screen, but may slow down the rest of the page.

Lazy route loading:

const Foo = (a)= > import('./Foo.vue')
const router = new VueRouter({
  routes: [{path: '/foo'.component: Foo }
  ]
})
Copy the code

1.8 Introduction of third-party plug-ins on demand

We often need to introduce third-party plug-ins in our projects. If we directly introduce the whole plug-in, the volume of the project will be too large. We can use Babel-plugin-Component and then only introduce the required components to reduce the volume of the project. The following is an example of introducing the Element-UI component library in a project:

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

npm install babel-plugin-component -D
Copy the code

(2) Then change.babelrc to:

{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [["component",
      {
        "libraryName": "element-ui"."styleLibraryName": "theme-chalk"}}]]Copy the code

(3) Introduce some components in main.js:

import Vue from 'vue';
import { Button, Select } from 'element-ui';

 Vue.use(Button)
 Vue.use(Select)
Copy the code

1.9. Optimize the performance of infinite lists

If your application has very long or infinitely scrolling lists, you need to use windowing techniques to optimize performance, rendering only a small area of content, reducing the time to re-render components and create DOM nodes. You can optimize this infinite list scenario by looking at the following open source projects vue-virtual-scl-List and Vue-virtual-Scroller.

1.10. Server render SSR or pre-render

Server-side rendering refers to the process where Vue renders the entire HTML fragments of the tags on the client side and the HTML fragments formed by 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 crawl tool will not wait for Ajax asynchronous completion of the page to crawl the content, so in the SPA is to crawl the page through Ajax to get the content; SSR is directly returned by the server to the rendered page (the data is already contained in the page), so search engine crawler tools can crawl the rendered page;

  • Faster content arrival time (faster first screen loading) : SPA will wait for all Vue compiled JS files to be downloaded before rendering the page. It takes a certain amount of time to download files, so the first screen rendering needs a certain amount of time. SSR renders the page directly by the server and returns to display, without waiting to download JS file and render again, so SSR has faster content arrival time;

(2) Disadvantages of server-side rendering:

  • More development constraints: for example, server-side rendering only supports the beforCreate and Created hook functions, which results in some external extension libraries requiring special treatment to run in server-side rendering applications. And unlike SPA, a completely static single-page application that can be deployed on any static file server, the server-side rendering application needs to be in the Node.js Server runtime environment;

  • More server load: Rendering a full application in Node.js is obviously more CPU intensive than a server that only serves static files, so if you expect to use it in a high-traffic environment, prepare your server load accordingly and use your caching strategy wisely.

If your project’s SEO and first screen rendering are key metrics to evaluate your project, then your project will need server-side rendering to help you achieve the best initial load performance and SEO. For details on how to achieve this, please refer to another article by the author, “Vue SSR Pit Trip”. If your Vue project only needs to improve the SEO of a few marketing pages (e.g. /, /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 pre-rendering is easier to set up and you can treat your front end as a completely static site, which you can easily add using the prerender-SPA-Plugin.

2. Optimization at the Webpack level

2.1. Webpack compresses images

In the vue project, you can set the limit size in webpack.base.conf. Js url-loader to handle the image, and convert the image smaller than the limit to base64 format. Therefore, for some large image resources, the load will be slow when the resource is requested. We can use image-webpack-loader to compress the image:

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

npm install image-webpack-loader --save-dev
Copy the code

(2) Then, configure it 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}}]}Copy the code

2.2. Reduce the redundant code for converting ES6 to ES5

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

class HelloWebpack extends Component{... }Copy the code

The following two helper functions are required to convert this code into working ES5 code:

babel-runtime/helpers/createClass  // To implement the class syntax
babel-runtime/helpers/inherits  // Is used to implement extends
Copy the code

By default, Babel will embed the dependent helper code in each output file. If multiple source files depend on the helper, the helper code will appear many times, resulting in code redundancy. In order not to let the auxiliary function code is repeated, can rely on them through the require (‘ Babel – the runtime/helpers/createClass) way to import, so you can do to make them appear only once. The babel-plugin-transform-Runtime plug-in does this by replacing the helper functions with import statements, thereby reducing the size of the code compiled by Babel.

(1) Install babel-plugin-transform-Runtime:

npm install babel-plugin-transform-runtime --save-dev
Copy the code

(2) Then change the.babelrc configuration file to:

"plugins": [
    "transform-runtime"
]
Copy the code

For more details on plug-ins, check out the detailed introduction to Babel-plugin-transform-Runtime.

2.3 extract common code

If the project does not extract the third-party libraries and common modules for each page, the project will have the following problems:

  • The same resource is loaded repeatedly, wasting user traffic and server costs.
  • Too many resources are required to load each page. As a result, the first screen of the page is loaded slowly, affecting the user experience.

So we need to extract the common code from multiple pages into a separate file to optimize the above problem. Webpack has a built-in plugin, CommonsChunkPlugin, which is used to extract the common parts of multiple chunks. The configuration of CommonsChunkPlugin in our project is as follows:

// All dependencies in package.json will be packaged into the vendor.js file.
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); }}),// Extract the code module mapping
new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest'.chunks: ['vendor']})Copy the code

For more details on plug-ins, check out the CommonsChunkPlugin for more details.

2.4 template precompile

When using an in-DOM template or a string template within JavaScript, the template is compiled into a rendering function at run time. This process is usually fast enough, but it’s best to avoid it for performance-sensitive applications.

The easiest way to precompile a template is to use a single-file component — the relevant build Settings take care of the precompile automatically, so the built code already contains the compiled render function rather than the original template string.

If you use WebPack and prefer to separate JavaScript and template files, you can use the Viee-template-Loader, which also converts template files into JavaScript rendering functions during the build process.

2.5. Extract the CSS of components

When using a single-file component, the CSS within the component is injected dynamically through JavaScript using the style tag. There is a small runtime overhead, which results in a “unstyled Content flash (FOUC)” if you use server-side rendering. Extracting the CSS from all components into the same file avoids this problem and also makes the CSS much better for compression and caching.

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

  • Webpack + Vue-Loader (the Vue-CLI WebPack template has been pre-configured)
  • Browserify + vueify
  • Rollup + rollup-plugin-vue

Optimize SourceMap

After the project is packaged, we will package multiple file codes in development into one file, and after compression, removing redundant Spaces and Babel compilation, finally the compiled code will be used in online environment, so the processed code will be very different from the source code. When there are bugs, We can only locate the compressed code, not the code in the development environment, and it is not good for development to locate the problem, so sourceMap comes along to solve the problem of bad mode code.

The SourceMap values are as follows (more + signs mean faster speed, more – signs mean slower speed, and o means medium speed)

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

Recommended for production environment: cheap-mod-source-map

Here’s why:

  • Cheap: The column information in the source code is not useful, so we do not want to include column information in the packaged file, only the row information can establish dependencies before and after the packaging. Therefore, in both development and production environments, we want to add a basic type of Cheap to ignore column information before and after packaging;

  • Module: Whether it is a development environment or a formal environment, we want to be able to locate the source code bug specific location, for example, a Vue file reported an error, we want to be able to locate the specific Vue file, so we also need to configure the Module;

  • Soure-map: source-map generates a separate soucemap file for each packaged module, so we need to add the source-map attribute;

  • Eval-source-map: Eval packages code very quickly because it does not generate a map file, but you can use eval-source-map in combination with eval to save the map file as a DataURL in the packaged JS file. Don’t use eval-source-map in a formal environment because it will increase the size of the file, but in a development environment, try it out because they pack quickly.

2.7. Output analysis of construction results

The output of Webpack code is very unreadable and the file is very large, which makes us very uncomfortable. In order to analyze the output more easily and visually, a number of visual analysis tools have emerged in the community. These tools make the results more intuitive in a graphical way, allowing us to quickly understand the problem. Next, we’ll look at the analysis tool we use in our Vue project: Webpack-bundle-Analyzer.

We do this in webpack.prod.conf.js in the project:

if (config.build.bundleAnalyzerReport) {
  var BundleAnalyzerPlugin =   require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}
Copy the code

$NPM run build –report generates the following analysis report:

2.8. Vue project compilation optimization

If your Vue project builds with Webpack and takes you a cup of coffee, you may need to optimize your project’s Webpack configuration to make Webpack builds more efficient. For details on how to optimize The Webpack build of Vue projects, please refer to the author’s another article, “Webpack Optimization Practices for Vue Projects”.

Third, the basic Web technology optimization

3.1. Start GZIP compression

Gzip, short for GNUzip, was originally used to compress files on UNIX systems. Gzip encoding over THE HTTP protocol is a technique used to improve the performance of Web applications; both the Web server and the client (browser) must support GZIP. At present, the mainstream browser, Chrome, Firefox, IE and so on all support this protocol. Common servers such as Apache, Nginx, and IIS also support gZIP compression, which is very efficient, usually up to 70% compression rate, which means that if you have 30K of web pages, it will be 9K after compression

In the following example, we use the familiar express server to open gzip very simple, related steps are as follows:

  • Installation:
npm install compression --save
Copy the code
  • Add code logic:
var compression = require('compression');
var app = express();
app.use(compression())
Copy the code
  • Restart the service and check the Response header in the network panel. If you see the following fields in red circles, gZIP is enabled successfully:

3.2. Browser Cache

In order to improve the speed of user page loading, it is necessary to cache static resources. The HTTP caching rules are classified according to whether the server needs to be resent to the request (force caching, contrast caching). If you do not understand the caching mechanism clearly, See the author’s article on HTTP caching, “Understanding HTTP Caching and how it works”, which will not be covered here.

3.3. Use of CDN

The browser has to connect to the server to download CSS, JS, and images from the server, and most servers have limited bandwidth. If you exceed the limit, the page will not respond for half a day. CDN can load files through different domain names, so that the number of concurrent connections to download files is greatly increased, and CDN has better availability, lower network delay and packet loss rate.

Use Chrome Performance to find Performance bottlenecks

Chrome’s Performance panel can record details and timing of JS execution over a period of time. Here are the steps to analyze page performance using Chrome Developer tools.

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

More information about Performance can be found here.

conclusion

This paper is composed of the following three parts: Vue code level optimization, WebPack configuration level optimization, basic Web technology level optimization; How to optimize the performance of Vue projects. Hope to read this article you have help, have inspiration, if there are shortcomings, welcome to criticize correct communication!

Hard to arrange for a long time, but also hope to manually thumbs up to encourage ~

Github: github.com/fengshi123/… , summarized all the author’s blog, also welcome to follow and star ~