Home > Web Front-end > JS Tutorial > Webpack learning tutorial front-end performance optimization summary

Webpack learning tutorial front-end performance optimization summary

小云云
Release: 2017-12-06 11:32:20
Original
2262 people have browsed it

Webpack is the most popular module loader and packaging tool recently. It can use various resources, such as JS (including JSX), coffee, styles (including less/sass), images, etc. as modules. deal with. This article mainly summarizes and introduces relevant information about front-end performance optimization of webpack learning tutorial. Friends in need can refer to it.

We introduced JS resources as shown above. I believe we rarely encounter them now. In recent years, the field of Web front-end development has evolved towards standardized development. This is reflected in the following two points:

1. MVC R&D architecture. There are many benefits (the logic is clear, the program focuses on the separation of data and performance, and it is highly readable, which is helpful for avoiding and troubleshooting problems...)

2. There are endless construction tools. Many benefits (improving team collaboration, engineering operation and maintenance, and avoiding manual processing of trivial and repetitive work)

  • Modular development

  • Integrate the front-end Implementation of performance optimization theory, code compression, merging, cache control, extraction of public code, etc.

  • Others include, for example, you can use ES 6 or CoffeeScript to write source code, and then build browser support ES5

So, the front end is so fun, if there are still projects that do not separate the front and back ends, it is really too conservative.

Mainstream build tools

There are many build tools on the market, including Grunt, Gulp, browserify, etc. These and WebPack are all packaging tools. But WebPack also has the following characteristics:

Compared with Grunt, WebPack not only has a rich set of plug-ins, but also has a loading (Loader) system. It supports multiple standardized loading methods, including ES6, CommonJS, AMD, etc., which Grunt and Gulp do not have.

From the perspective of code obfuscation, WebPack is more extreme

The code is fragmented into processing units (rather than files), making file fragmentation more flexible.

P.S. This is just a simple comparison, no matter which one is better or worse. In fact, tools can meet the needs. The key is how to use them. Behind the use of tools is the understanding of front-end performance optimization.

Introduction

Recently I am using webpack to optimize the first screen loading performance. After using several plug-ins, our speed before and after going online has doubled. Let me simply share it here. First, I will post a comparison picture of the first screen rendering before and after optimization.

You can see that the total download time is reduced from 3800ms to 1600ms.

When we use webpack, we usually choose multiple entry files in order to separate our own source code from the third-party library code. This is the previous code.


entry: {
 entry: './src/main.js',
 vendor: ['vue', 'vue-router', 'vuex', 'element-ui','echarts']
},
output: {
 path: config.build.assetsRoot,
 filename: utils.assetsPath('js/[name].[chunkhash].js'),
 chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
}
Copy after login


##echarts is very large, so the packaged vendor.js is about 1.2MB (after gzip compression after), and echarts was not used on the homepage, so I later used externals to introduce the third-party library through cdn. The following is the optimized code


entry: {
 entry: './src/main.js',
 vendor: ['vue', 'vue-router', 'vuex', 'element-ui']
 },
 // 这里的output为base中的output,不是生产的output
 output: {
 path: config.build.assetsRoot,
 filename: '[name].js',
 libraryTarget: "umd",
 publicPath: process.env.NODE_ENV === 'production' ?
  config.build.assetsPublicPath : config.dev.assetsPublicPath
 },
 externals: {
 echarts: 'echarts',
 _: 'lodash'
 },
Copy after login


This is the comparison before and after optimization.

Then we need to go to the html to quote the cdn in externals in the form of a script tag. After that, you can import it in the corresponding file. Its advantage is that no matter how many times you reference it in many vue files, it will not be packaged into all trunks (trunk' here refers to loading on demand, which will be detailed later. Description), this is the effect shown using the webpack-bundle-analyzer plug-in.


var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
new BundleAnalyzerPlugin({
  // 可以是`server`,`static`或`disabled`。
  // 在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
  // 在“静态”模式下,会生成带有报告的单个HTML文件。
  // 在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
  analyzerMode: 'server',
  // 将在“服务器”模式下使用的主机启动HTTP服务器。
  analyzerHost: '127.0.0.1',
  // 将在“服务器”模式下使用的端口启动HTTP服务器。
  analyzerPort: 8888, 
  // 路径捆绑,将在`static`模式下生成的报告文件。
  // 相对于捆绑输出目录。
  reportFilename: 'report.html',
  // 模块大小默认显示在报告中。
  // 应该是`stat`,`parsed`或者`gzip`中的一个。
  // 有关更多信息,请参见“定义”一节。
  defaultSizes: 'parsed',
  // 在默认浏览器中自动打开报告
  openAnalyzer: true,
  // 如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
  generateStatsFile: false, 
  // 如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
  // 相对于捆绑输出目录。
  statsFilename: 'stats.json',
  // stats.toJson()方法的选项。
  // 例如,您可以使用`source:false`选项排除统计文件中模块的来源。
  // 在这里查看更多选项:https: //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
  statsOptions: null,
  logLevel: 'info' //日志级别。可以是'信息','警告','错误'或'沉默'。
 })
Copy after login


We'll see, no use After externals and externals are used, libraries like echarts and lodash will not appear in all js (even if you import 10,000 times, it will not be packaged once, it is amazing~~).

Two more points about externals -

1. The key in externals is the one used in import


import lodash from "_";
import echarts from "echarts";
Copy after login


2. The value in externals is called under window

Then let’s talk about why the output uses trunkhash instead of trunk. This is for persistent caching. Let’s briefly talk about the difference between the two -

trunk: The version after each build means that the hash values ​​of all the files after the build are the same. For example, if I only change one file, all the file hashes will be the same in the end. Change, so that all files will not go to the cache, so the cache will lose its meaning.

trunkhash:根据每个文件生成不同的hash值,当文件变化时hash会改变且只会改变相应的文件

然后我们肯定是要用到CommonsChunkPlugin,这个插件是用来抽取公共代码的,基本上99%的配置都是长这样子或者类似这样子用两个不同的commonschunkPlugin,但这从某方面来说并没有实现真正意义上的持久化缓存,这个一会我会通过webpack打包原理来详细解释其中的原因。。。。。。


new webpack.optimize.CommonsChunkPlugin({
 names: ['vendor','manifest']
})
Copy after login


在没用这个插件之前,我们的main.js和vendor.js会是这样子。。。

大家会看到我们这两个文件会有公共的部分,比如vue和element-ui,所以我们要抽取公共代码到vendor中,所以我们可以先这样配置


new webpack.optimize.CommonsChunkPlugin({
 name: 'vendor',
}),
Copy after login


但这样的话虽然可以提取公共代码,但我们会把runtime(webpack运行时的代码,一会在打包原理中会再次提到)也放到vendor中,这里面会维护一个trunk的文件列表,类似于这样,就是说我们改任意的代码,这个table里面的hash会变,所以vendor的hash也会变

,所以这没有实现真正的持久化缓存。这个hash table是按需缓存的打包出来的trunk包,一般都是通过require.ensure(就是vue-router中配置的page对应页面,按需加载)


所以我们就把name改为names,就是上面那个配置。因为使用这个插件,我们会把公共代码抽到第一个name中,把runtime放到最后一个name中,也就是我们所谓的“manifest”文件。

并且这个文件会比较小,通常都是2kb左右,所以build后会生成一个script标签,但这样的话就多了一个http请求,所以我们可以用另外一个插件(InlineManifestWebpackPlugin)将manifest.js内联进去。就会长这样子

再回到我们的CommonsChunkPlugin,现在我们随便改任何已存在的文件,vendor.js的hash都不会变,是的,貌似这就实现了持久化缓存。但是当我们新增一个模块,并且在入口文件中import一下,我们的vendor就会跟main一起变。很奇怪对吧,我们明明已经做了自己的源码跟第三方库分离,为什么vendor还会变(到现在应该没有任何一篇博客对此进行详细的说明)。下面我就详细的给大家解释下我的看法,如果大家发现有不对的地方还请指正。

再解释为什么之前,我们先简单了解下webpack的打包规则。

webpack一个entry对应一个bundle,这个bundle包括入口文件和其依赖的模块。其他按需加载的则打包成其他的bundle。还有一个比较重要的文件时manifest,它是最先加载的,负责打包其他的bundle并按需加载和执行。

manifest是一个自执行函数,熟悉angular的同学看第一行应该很了解,因为anguar1.3版本的源码中启动就是angular.bootstrap,对,这里也是一样。里面的modules变量就是对应模块函数,它是webpack处理的基本单位,就是说对应打包前的一个文件

这是js源文件,

这是打包后的文件,

所有的模块函数索引都是连续的(每个js文件生成一个trunkid!!!!!),像这种 /* 4 */ 对应的就是js文件,他通过打包就变成了一个个trunkid,仔细看会看到咱们打包前js文件里的export和require依赖都会统一转换成webpack模块。咱们说的webpackJsonp就是除manifest之外打包其他的文件的函数体。

简单说下main吧,这个图的trunkid是连续的,为了在一张图上显示,我截掉了trunk3-7.

There are three parameters here. The first one is the trunkid of my current file. It is a unique identifier, which refers to the trunkid of main. The second one is the module function of all the files packaged. The third one is my The trunkid module function to be executed immediately.

ok, that’s enough to introduce these.

Then let’s go back and see why our so-called commonschunkPlugin changed. As I said just now, there are as many trunkids as there are js.

So when we add a new js and introduce it to the main entrance, webpack packages it again, and my main file will have one more module function. As I just said, the trunkid increases sequentially and will not be repeated. Therefore, the corresponding vendor's ID will be +1. It is such a subtle change that causes the hash to change.

Look carefully, both vendors have 10272 lines. The only difference is I want to self-execute this vendor library. I quoted jquery here, so this file only has jquery. Self-execution must have module functions, trunkid+1, so the hash will change. Let's recall it carefully. In fact, this also illustrates the significance of this plug-in. I just want to extract the public library. OK, this plug-in does it, but because of the webpack packaging mechanism, different files generate different turnkids, so this is a fly in the ointment. Recall that we generally do not modify main.js casually, so from another perspective, this is the implementation of persistent caching. But what if I just want to keep the vendor’s hash unchanged?

This code can be implemented. Yes, if you know vue-cli well, this is the official demo of vue-cli. As for why it is possible, I will discuss this later. Explain to everyone (I really can’t write anymore...).

Finally, I would like to introduce to you a super useful thing, which is CDN. Our current demand is to let pictures go to CDN and js to go online. However, the official explanation is to make CDN changes by modifying the config file. If this is done, all my output will go through CDN, and all ajax requests will be cross-domain. That’s it.

My solution at the beginning was to replace the source files one by one, which would be slower. More importantly, the cdn image also has a hash value. When I When you replace the image in the future, you have to change the corresponding hash again. Is there any way to allow him to automatically obtain the hash?

Yes, we need to configure the cdn separately in the url-loader so that js can access the online path and static resources use the cdn. The two do not affect each other.

A brief reminder, url-loader cannot detect the background in js, so any address we reference in js must first import this image, url -loader will parse and package it.

The above content is a summary of the front-end performance optimization of the webpack learning tutorial. I hope it can help everyone.

Related recommendations:

Webpack server-side code packaging

Detailed introduction to examples of webpack processing CSS

Detailed explanation of webpack’s configuration file

The above is the detailed content of Webpack learning tutorial front-end performance optimization summary. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template