Home  >  Article  >  Web Front-end  >  About webpack front-end performance optimization (detailed tutorial)

About webpack front-end performance optimization (detailed tutorial)

亚连
亚连Original
2018-06-22 17:08:132213browse

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.

Preface

Once upon a time, we introduced JS resources as shown above. I believe it is very popular now. Seen less often. 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. Many benefits (clear logic, the program focuses on the separation of data and performance, strong readability, and 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. Make it support multiple standard 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, all 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')
}

echarts is very large, so the packaged vendor.js is about 1.2MB (after gzip compression), and echarts is not used on the homepage, so I later used externals to add the first The third-party library is introduced 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'
 },

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' //日志级别。可以是'信息','警告','错误'或'沉默'。
 })

We will see that without externals and after using externals, there will be no similar echarts and lodash in all js The library appears (even if you import it 10,000 times, it will not package it once, it’s amazing~~).

Two more points about externals -

1.The key in externals is used in import

import lodash from "_";
import echarts from "echarts";

2.The value in externals is called under window

Then let’s talk about why 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 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: Generate different hash values ​​based on each file. When the file changes, the hash will change and only the corresponding file will be changed

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

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

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

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

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

但这样的话虽然可以提取公共代码,但我们会把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.

这里面一共有三个参数,第一个是我当前文件的trunkid,它是唯一标识符,就是指main的trunkid,第二个就是打包的所有文件的模块函数,第三个是我要立即执行的trunkid模块函数。

ok,介绍这些就足够了。

然后我们再回过头来看看为什么我们所谓的commonschunkPlugin会变。刚才说过,有几个js就有几个trunkid。

So when we add a new js and introduce it to the main entry, webpack packages it again, and my main file will have one more module function. As I just said, the trunkid increases in sequence and will not be repeated. Therefore, the corresponding vendor 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 meaning 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, let me 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, but the official explanation is to make CDN changes by modifying the config file. If you do this, all my output will go to CDN, and then all ajax requests will be cross-domain. Yeah.

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 is what I compiled for everyone. I hope it will be helpful to everyone in the future.

Related articles:

How to use the video component to play videos in the WeChat mini program

How to use the audio component in the WeChat mini program

How to implement axios secondary encapsulation in vue

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

Statement:
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