First thing Fitrst: Never clickbait, unhelpful interviews, unfriendly newbies
Someone looking at the best background:

In early October, we optimized the front-end construction of the company’s products, but some problems remained:

1. Excessively slow hot reloading: the average response time for ten times of hot reloading for a single file change is about 17s, which seriously affects the development experience;

2. The volume of some bundles is too large, which consumes too much time for a single resource request, affecting the browser loading speed.

3. There is no LIINT mechanism to control the grammatical norms in the coding process, and no automatic formatting for code saving, resulting in low code quality, different coding styles of team members and high handover costs.

4. There is still room for optimization of packaging volume and packaging speed.

Overall consideration, I personally judge that the performance of Webpack1 is not enough to provide better support for the construction process of the front-end project, upgrade Webpack, improve the construction experience, and benefit o&M and testing colleagues HHHH

There will be more images this time:

This article will demonstrate the process of upgrading WebPackage v1.13.2 to V4.44.2. The reason for choosing this version is subjective, I think it is the latest minor version of WebPackage 4 (2020.11). Webpackage 4 has been released from the test version for more than two years now. Enough to make the features of this big version sound and stable enough to be trusted. This time I will be removing the original configuration files and upgrading from scratch, so this article can probably serve as a primer for building projects with WebPack.

Json contains devDependencies, which record the dependencies required by the project in the development environment. After backing up the file, I delete node_modules and clear the list of devDependencies. And then NPM I.

Then I started implementing a simplified version, which I installed with the latest 4.x webpack:

NPM [email protected] - I DCopy the code

The Webpack 4.x version requires a command-line tool to run, so we also need to download WebPack-CLI, and I randomly chose a version that is neither new nor old:

NPM [email protected] - I DCopy the code

Then start writing the configuration file, first write a basic version to test the feasibility of the new version of WebPack:

Create a simple entry file called test.js for packaging:

1 import {cloneDeep} from "lodash"
2 const obj = {color:'red'}
3 const copy = cloneDeep(obj)
Copy the code

Create the webpack.config.js file in the project root directory:

const path = require('path');
module.exports = {
    entry: "./src/test.js",
    output: {
        path: path.resolve(__dirname,"dist"),
        filename: 'testbundle.js',
    }
}
Copy the code

When webpack starts, if no file is specified to run, the configuration in webpack.config.js in the root directory will be read automatically.

  "scripts": {
    "build": "webpack"
  },
Copy the code

Run NPM Run build, the Webpack will be packaged as configured, and you’ll see an additional file called testbundle.js in your dist directory.

Also, with my configuration, I will see a warning on the console:

WARNING in configurationThe ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.You can also set it to ‘none’ to disable any default behavior. Learn more: Webpack.js.org/configurati…

This warning indicates that the “mode” property is not currently set in the configuration passed to Webpack, which webpack will treat as mode: The mode attribute in the WebPack4 configuration file needs to be mentioned here. Webpack4 has some common plug-in configurations built in, which saves the developer time and energy to configure Webpack. Using the mode attribute, you can quickly configure these two sets of plug-ins. Mode :”development” and mode:”production” are optimized for both development and production scenarios, such as persistent caching, code compression, etc. If you don’t want to use either of these presets, you can set mode to “None”. As for other details, interested friends can get more information from the documentation.

The general idea is to separate the development environment configuration from the production environment configuration into two files, usually named webpack.dev.conf.js and webpack.prod.conf.js, because these two scenarios have some things in common. So we can extract a common configuration file webpack.base.conf.js. At present, we don’t consider the difference between production environment and development environment, first create a basic configuration webpack.base.js, so that Webpack can correctly parse a Vue file, successfully complete packaging.

Vue – Loader and vue-template-loader of the same version as vue need to be installed. Note that the vue-loader version is 15 or later. The VueLoaderPlugin needs to be imported from the vue-loader directory, and the VueLoaderPlugin will use the other rules you defined in rules to check and process the blocks in the vue file that comply with the rules

const VueLoaderPlugin = require("vue-loader/lib/plugin"); . . . Module: {rules: [{test: "/\.vue$/", loader: "vue-loader"}]}, plugins: [new VueLoaderPlugin()]Copy the code

If you are already further down in this article and have written the build file, or are rewriting the configuration file based on webpack.config.js, you will see a lot of errors from the console when you execute the package command, such as:

There is a style code in the screenshot, and an error is reported indicating that you may need other loaders to process vue-Loader’s parsing results. Therefore, in order to solve the problem in the screenshot, webpack can smoothly process the style code. You need to add the appropriate loader, which will depend on your project usage:

npm i css-loader style-loader url-loader file-loader less less-loader sass node-sass stylus stylus-loader -D
Copy the code

Style-loader generates style tags and mounts the CSS to the page structure. File-loader reads the reference path of static resources and generates a file that meets the rules in the output directory for the compiled code. Url-loader On the basis of file-loader, url-loader converts files whose volume is smaller than the specified value into base64 strings to reduce the number of resource requests. Other files other Loaders and related dependencies are not described.

I used the loading method of CSS related loader (copied from somewhere else anyway) and added a few changes:

Exports.cssloaders = function () {// the style-loader can be used as an export.cssloaders. It also implements hot reloading of style level without page refresh, but style-loader has been completed for a long time, so you can choose to use the latest version of style-Loader with CSS-Loader. Both loaders now default to True for the ESModule property. const vueStyleLoader = { loader: "vue-style-loader" } const cssLoader = { loader: "Css-loader ", // If you are using vue-style-loader and the VERSION of CSS-Loader is V4.0.0 or later, this property must be added. Options: {esModule: False}} // If generateLoaders did not receive the parameters, the basic loader configuration would be returned: Use csS-loader and vue-style-loader function generateLoaders (loader) {const outputLoaders = [vueStyleLoader,cssLoader] if (loader) { const targetloader = {loader:loader+"-loader"} outputLoaders.push(targetloader) } return outputLoaders } return { css: generateLoaders(), less: generateLoaders("less"), sass: generateLoaders("sass"), scss: generateLoaders("sass"), stylus: generateLoaders("stylus"), styl: generateLoaders("stylus") } } exports.styleLoaders = function () { var output = [] var loaders = exports.cssLoaders() for (let extension in loaders) { var loader = loaders[extension] console.log(loader) output.push({ test: new RegExp('\\.' + extension + '$'), use: loader }) } return output }Copy the code

The hack’s styleLoaders output an array of rules for processing style files, which you can mount into rules using the extension operator. So next we create a build.js file that calls up the WebPack API for packaging. I prefer to use the command line to call Node to execute a build file and run WebPack in this file. This makes it slightly easier to handle scenarios with different packaging configurations, such as SIT, UAT, PROD, etc. There are different global configurations (such as interface baseurl, request encryption and decryption, local packaging, etc.). In this case, process.argv can be used to obtain command line parameters and refine the configuration. Another benefit for me is that it is easy to add old_space to expand memory and avoid compilation failures in larger projects (64-bit Windows allocates 1.4GB of memory for Node).

Gai version of the build. Js

process.env.NODE_ENV = 'production'

var webpack = require('webpack')
var webpackConfig = require('./webpack.prod.conf')


webpack(webpackConfig, function (err, stats) {
  // spinner.stop()
  if (err) throw err
  process.stdout.write(stats.toString({
    colors: true,
    modules: false,
    children: false,
    chunks: false,
    chunkModules: false
  }) + '\n')
})
Copy the code

Then rewrite the build command as:

"build": "node --max_old_space_size=2077 build/build.js"
Copy the code

Now run NPM run build and see what happens:

There is no error in the process, a basic packaging process, by now actually finished, the package is not actually usable, but I think it is still a meaningful step, I think this version will continue to enrich the features. The following content is not continuous, there are no pre-requirements, you can choose to read:

  1. clean-webpack-plugin

  2. html-webpack-plugin

  3. babel & babel-loader

  4. postcss

  5. webpack-dev-server

  6. mini-css-extract-plugin

  7. terser-webbpck-plugin & optimize-css-assets-webpack-plugin

  8. image-webpack-loader

  9. thread-loader

  10. cache-loader

  11. eslint & eslint-loader

Before we begin:

As mentioned before, mode attribute of Webpack4 has two types: “development” and “production”, corresponding to development environment and production environment. Since the configuration of the two environments is often different, it is not suitable for the following application scenarios to use a single configuration. Therefore, before additional configuration, You may need to split up the corresponding configuration files and load different packaged configurations in different scenarios, which is a normal operation of WebPack.

For general projects, it would be enough to add two new configuration files to the webpack.base.conf file, named webpack.dev.conf.js and webpack.prod.conf.js, according to my convention.

Next I will introduce webpack-merge. Webpack-merge is used to merge two configuration files.

const merge = require("webpack-merge").merge const webapckBaseConfig = require("./webpack.base.conf") module.exports = merge(webapckBaseConfig,{ ... your configuration })Copy the code

You can also use this method as an object. assign method to merge a common Object, which is a bit of an add-replace method for loader rules, especially for loader options.

html-webpack-plugin

The dist directory structure, described as JSON at the end of the previous paragraph, looks like this:

"Dist" : [" static ": [{" img" : [...]}, {" font ": [...]}, {" js" : [...]}]]Copy the code

This form of package also have no way to applied to the practice, the reason is the lack of a used to mount the js script and CSS files of the “real”, if there is no an HTML file as an entrance to mount these resources, browsers have no chance to parse the style/code/script tags and associated resources, so nature is can’t open the page. Solving this problem requires creating an HTML file to introduce packaged resources, and the html-webpack-plugin helps simplify this process (I mean, not recommended, But you can make your front-end package usable by manually importing resources from the static directory in an HTML file.

I’m going to add the plugin to the WebPack configuration, and I’m going to pick a few properties to explain what they do. See the rest

const HtmlWebpackPlugin = require("html-webpack-plugin") ... . plugins: [... new HtmlWebpackPlugin({// Select a local HTML file as the template instead of having the plugin automatically generate template:" XXX/XXX /index.html", // Inject This property selects where to inject packed resources in the HTML file. True is the default value and injects script to the bottom of the body tag. The remaining optional values are: "head"/"body"/false inject: Filename :"index.html", // small icon favicon:"xxxx.png", // Break the cache, Add the hash as the request parameter in the form of? XXXXXX when importing each resource, It remains to be seen whether the effect is consistent with adding [hash] to filename in output Other plugins that create new files during execution, such as copy-webpack-plugin and mini-css-extract-plugin, have the option of adding the hash suffix to the generated file name: "True"}) // ps: new HtmlWebpackPlugin() = new HtmlWebpackPlugin()Copy the code

The dist directory now has an extra index.html:

Details:

This concludes the introduction of this plugin, which is used by almost every project that uses WebPack, but I actually don’t think it’s that important, probably because I don’t need any more advanced properties on my end.

babel-loader & babel

Babel is used to convert ES6 and more advanced syntaxes into ES5 syntaxes so that code can run in lower-release environments. Using Babel to translate to lower-level syntaxes is a common process in most projects. If your project is built from scratch rather than from an older version, You may spend a lot less time in the configuration process than the latter way.

Without further ado, go to the code.

The project used [email protected] and [email protected] before, I backed up the backup, and then began to reconfigure.

The basic dependencies need to be downloaded first

npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill
npm install babel-loader -D
Copy the code

The latest version of babel-loader is 8.2.2 at the time of writing this article, so I installed it directly. After installing it, I started to append the webpack configuration and use babel-loader to process js files:

Rules: [... {test: /\.js$/, loader: "babel-loader", // exclude: /node_modules/,},]Copy the code

The Babel configuration file is named in two types, one is named babel.config.xx (the extension is usually json or js, or you can use CJS or MJS depending on your environment), and the other is named.babelrc(json) or.babelrc.js, Babelrc has a higher priority than babel.config. Create this file in the root directory of the project, and babel-loader will process the js files that conform to the rules according to this configuration.

{// Preset is a set of plug-ins, and some common configurations of these plug-ins can be added after preset // And preset-env is a set of several presets, which are loaded from ES5 to the latest preset (ES2020) by default, It's not recommended to use Babel in user 0 configuration to get around this problem, but it's not recommended to use Babel in user 0 configuration. [["@babel/preset-env",{"targets": presets": [["@babel/preset-env",{"targets": { "browsers": [ "> 1%", "last 2 versions", "not ie <= 10" ] } } ]], "plugins": [// transform-vue-jsx syntax "transform-vue-jsx" may appear in script modules, // elementui used by the project imports as needed [" Component ", {"libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ], "comments": false }Copy the code

postcss

Postcss is a platform type tool for parsing and converting CSS code. I call it a platform because it implements various processing of CSS code through platform-loaded applications, such as lint mechanism and client-side compatibility. Here I only show the use of autoprefixer, the client-compatible tool I used during the build process, and I may use the demo to explain the details of the process later.

Postcss-autoprefixer in Webpack needs to work with postCSs-Loader, so as usual, the first step needs to install dependencies;

npm i postcss postcss-loader autoprefixer -D

In the process of building, postcss – loader will try to read the root directory called postcss. Config. Js postcss configuration, so you need to create a new postcss. Config. Js files:

module.exports = {
    plugins: {
      "autoprefixer": {
      }
    }
}
Copy the code

Autoprefixer needs to declare a list of supported clients. If you don’t want to use the default configuration, you can create a. Browserslistrc file in the root directory to configure the range of compatible client versions. You can also add a browsersList property to package.json, such as:

. "browserslist": [// Browsers used by more than 1% of people worldwide... The larger the number of compatible clients is, the smaller the range of compatible clients is "> 1%". The last 2 versions of the latest version released by the browser manufacturer are "not IE <= 10"].Copy the code

Sometimes you are not sure if you need to use postCSS, in which case it is a good choice to set a switch. The process of adding postCSs-Loader to handle CSS code needs to be done before csS-Loader handles it, because the loader that handles the styles has been encapsulated in a file called utils. With the new switches, you need to extend the styleLoaders method in utils.js:

/** * * @param {{usePostcss:Boolean}} options */ exports.cssLoaders = function (options) { const vueStyleLoader = { loader: "vue-style-loader" } const cssLoader = { loader: "css-loader", options: {// If you are using vue-style-loader and the version of CSS-loader is v4.0.0 or above, the following property must be set to false. Specific reasons see https://www.cnblogs.com/byur/p/14194672.html esModule: false, }} const postcssLoader = {loader:'postcss-loader'} const baseLoaders = [vueStyleLoader,cssLoader] function generateLoaders (loader) { const outputLoaders = [vueStyleLoader,cssLoader] if (options.usePostcss){ outputLoaders.push(postcssLoader) } if (loader) { const targetloader = {loader:loader+"-loader"} outputLoaders.push(targetloader) } return outputLoaders } return { css: generateLoaders(), less: generateLoaders("less"), sass: generateLoaders("sass"), scss: generateLoaders("sass"), stylus: generateLoaders("stylus"), styl: GenerateLoaders ("stylus")}} // add parameters options exports. StyleLoaders = function (options) {var output = [] // pass through options var loaders = exports.cssLoaders(options) for (let extension in loaders) { var loader = loaders[extension] console.log(loader) output.push({ test: new RegExp('\\.' + extension + '$'), use: loader }) } return output }Copy the code

Turn it on when necessary:

const merge = require("webpack-merge").merge const webapckBaseConfig = require("./webpack.base.conf") const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const config = require(".. /config") const utils = require('./utils') module.exports = merge(webapckBaseConfig,{ mode:"production", module: {/ / need to config. Build will usePostcss is set to true rules: utils. StyleLoaders ({usePostcss: config. Build. UsePostcss}),}, plugins: [ new CleanWebpackPlugin(), ] })Copy the code

And you’re done.

Postscss is special in that it doesn’t exist at all. (Laughter) First, many projects actually use autoprefixer, but it’s actually a little difficult to adjust it according to your own needs after accurately understanding the meaning of configuration. So a lot of times you just copy a configuration to the new project and it’s done; The second point is that autoprefixer, from a developer’s point of view (and I’m speaking only for myself), is often hard to detect the difference between autoprefixer working and autopreFixer not working. You just know that the configuration you’re doing is working during the build process, but what the client looks like when it’s not working. This is something I rarely encounter, at least when using mainstream attributes (placeholder, box-shadow, etc.), so I sometimes doubt its necessity.

webpack-dev-server

Webpack’s watch mode, Webpack-dev-server, and Webpack-dev-Middleware are three different development modes. However, the latter two are mostly used in the actual application (because the first one does not automatically refresh the browser). The company project originally used middleware, and I will use Webpack-dev-server as the platform in development mode this time.

So the new webpack.dev.conf.js file now comes into play. In addition to inherits the general configuration, this file will also be used to host some personalized configuration in development mode. One important property of this file is devServer. In the webpack.config.js configuration file that WebPack looks for and executes by default, devServer also needs to be configured in it, including vue-cli, which is wrapped on top of WebPack. You can also find devServer properties in its configuration file. Now let’s configure this property in the webpack.dev.conf.js file.

webpack.dev.conf.js

const merge = require("webpack-merge").merge const Webpack = require("webpack") const webapckBaseConfig = require("./webpack.base.conf") const config = require(".. /config") const utils = require('./utils') const path = require("path") module.exports = merge(webapckBaseConfig,{ mode:"development", module: { rules: utils.styleLoaders({ usePostcss:config.dev.usePostcss }), }, plugins: [new Webpack HotModuleReplacementPlugin ()], devtool: config. Dev. Devtool, devServer: {/ / in the console display build schedule progress: True, // In inline mode, when hot replacement occurs, relevant build information is refreshed in the console, false is displayed in the browser, true is recommended. Inline: true, // Log level clientLogLevel: "Warning ", // In silent mode, errors and warnings during webpack compilation will not be printed on the console, and will not be prompted after the build/hot reload is complete. If there is no other tool for output assistance, it is not recommended to set false quiet: false, historyApiFallback: { rewrites: [ { from: /.*/, to: Path. The posix. Join (config. Dev. AssetsPublicPath, "index.html"),},],}, / / open the hot, If there is no added in plug-in HotModuleReplacementPlugin (HMR), build at the beginning of the dev - server will automatically help you to catch up on, but still suggest manually add HHH hot: true, / / open gzip compress: True, host: config.dev. Host, port: config.dev. Port, // Automatically open browser open: The config. Dev. AutoOpenBrowser, / / compile failed in full-screen browser display error overlay: config. Dev. ErrorOverlay? {warnings: false, errors: True,} : false, publicPath: config. Dev., / / proxy agent: assetsPublicPath config. Dev. ProxyTable,}})Copy the code

Written in accordance with the conventional NPM command line write webpack – dev – server build/webpack. Dev. Conf., js is enough, but the previously mentioned, so sometimes the memory allocated enough, I grope for the solution to create a js script, Execute this script via Node with expansion parameters and invoke the Webpack-dev-server API.

Create dev-server.js /** * * https://github.com/webpack/webpack-dev-server/blob/master/examples/api/simple/server.js node tuning up the dev - server * / const Webpack = require('webpack'); const WebpackDevServer = require('webpack-dev-server'); const webpackConfig = require('./webpack.dev.conf'); const compiler = Webpack(webpackConfig); Const devServerOptions = webPackConfig. devServer // Instead of manually setting up the webPack startup project, DevServer in webpackConfig is ignored, so const server = new WebpackDevServer(Compiler, devServerOptions) needs to be added in the second parameter; server.listen(devServerOptions.port, devServerOptions.host, () => { console.log(`Starting server on http://${devServerOptions.host}:${devServerOptions.port}`); }); / / package. Json... "scripts": { "build": "node --max_old_space_size=4096 build/build.js", "dev": "Node --max_old_space_size=4096 build/dev-server.js"},......Copy the code

Here you have to write a basic function is complete configuration (used to amuse ourselves should suffice), can be packaged to deployed on the server, and can also run local development and debugging, you need to do is to both experience and performance, optimize the existing configuration, the content and writing of hope I won’t let you feel boring.

Now, it’s all about data

Next, two plug-ins, Webpack-bundle-Analyzer and Speed-Measure -webpack-plugin, are used to analyze the performance of the existing project construction process. One thing that needs to be noted in advance is that these two plug-ins also consume part of the performance during the effective period, so there will be some error between the time calculation and the total time calculated by Webpack. Generally speaking, the time in the statistics of Webpack will be less than that of Speed-measure -webpack-plugin. Because the time consumed by Webpack-bundle-Analyzer is counted by speed-measure-webpack-plugin.

I ran it half a dozen times and ran through the results with the closest approximation to the average:

Overall view of the bundle:

Some accompanying statistics:

(If you look at the stat/Parsed comparison here, you may be aware that this build process may have repackaged dependencies.)

Statistics for time-consuming details:

This near-zero personalization performance is a bit better than the last configuration in this article, which makes me wonder how much more performance and user friendliness webpack4 pairs are compared to previous versions… High time something was done! .jpg)

CSS extraction and code compression

In order to squeeze more time out of loader and plugin, I then did style code extraction and code compression based on mode: Production.

Before WebPack4, I used the extract-text-webpack-plugin for CSS extraction. After Webpack4, the extract-text-webpack-plugin is no longer applicable. The official recommendation is to use the mini-CSs-extract-plugin instead.

The mini-CSs-extract-plugin needs to be used with its built-in loader, so you need to configure it in utils.js as you don’t need to extract/compress code in Development mode. Add logic to determine the scene:

(Why can’t you fold code like blogosphere? !!)

// utils.js const path = require('path') const config = require('.. / config ') / / introduction of the mini - CSS - extract - the plugin's built-in loader const MiniCssExtractPluginLoader = require("mini-css-extract-plugin").loader exports.assetsPath = function (_path) { var assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory; return path.posix.join(assetsSubDirectory, _path) } /** * * @param {{usePostcss:Boolean}} options */ exports.cssLoaders = function (options) { const vueStyleLoader  = { loader: "vue-style-loader" } const cssLoader = { loader: "css-loader", options: {// If you are using vue-style-loader and the version of CSS-loader is v4.0.0 or above, the following property must be set to false. Specific reasons see https://www.cnblogs.com/byur/p/14194672.html esModule: false, }} const postcssLoader = {loader:'postcss-loader'} const baseLoaders = [vueStyleLoader,cssLoader] Function generateLoaders (loader) {const outputLoaders = [vueStyleLoader,cssLoader] Insert MiniCssExtractPluginLoader if (options. ExtractCss) {outputLoaders. Splice (1, 0, {loader: MiniCssExtractPluginLoader, options: { publicPath: ".. /.. / "}, }) } if (options.usePostcss){ outputLoaders.push(postcssLoader) } if (loader) { const targetloader = {loader:loader+"-loader"} outputLoaders.push(targetloader) } return outputLoaders } return { css: generateLoaders(), less: generateLoaders("less"), sass: generateLoaders("sass"), scss: generateLoaders("sass"), stylus: generateLoaders("stylus"), styl: GenerateLoaders ("stylus")}} exports. StyleLoaders = function (options) {var output = [] // options var loaders = exports.cssLoaders(options) for (let extension in loaders) { var loader = loaders[extension] console.log(loader) output.push({ test: new RegExp('\\.' + extension + '$'), use: loader }) } return output } // webpack.prod.js const MiniCssExtractPlugin = require("mini-css-extract-plugin"); ... Plugins: [new MiniCssExtractPlugin({filename: "static/styles/[name][contenthash:7].css" ignoreOrder: True,})]...Copy the code

At this point, when you build, the CSS code is extracted and stored as a CSS file in the dist/static/styles path. The CSS code is then compressed with the JS code, which is done by configuring the optimization property. For some of the configuration, I wrote some personal understanding in the comments, there is wrong place, please correct.

// const OptimizeCSSPlugin = require("optimize- CSS-assets-webpack-plugin "); const TerserPlugin = require("terser-webpack-plugin") optimization: { flagIncludedChunks:true, occurrenceOrder:true, concatenateModules:true, usedExports: // Mode: Minimize the minimize attribute in production is set to true. If the minimizer is not configured, minimize the minimize attribute in production. Terser-webpack-plugin is used to compress and optimize JS code. Minimize: true, // Js compression is automatically enabled under mode: Production, and I add a few additional configurations to suit my needs. To do this you need to specify minimizer to configure plug-ins that handle CSS and JS code separately. Minimizer :[// I use terser-webpack-plugin as the js code compression tool, as well as other third-party plugins. New terser-webpack-plugin ({cache: {cache: : {cache: : : {cache: : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : True, parallel: true, sourceMap: true, // Not separate extractComments: false, terserOptions: {sourceMap: // Compress: {drop_console: true, drop_debugger: True, // pure_funcs takes a list of functions and removes the return values of those functions at compile time (if they are not used). Console. log will have the same effect as drop_console. // pure_funcs: ["console.log"]}, // Do not keep comments format: {comments: False,},}}), // Compression CSS new OptimizeCSSPlugin({cssProcessorOptions: {discardComments: {removeAll: true, }, canPrint: true } }), ], runtimeChunk: True, // SplitChunksPlugin replaces CommonsChunkPlugin in WebPack3 and earlier versions. splitChunks: { hidePathInfo: true, cacheGroups: { vendor: { name: "vendor", chunks: "Initial ", // priority defaults to 0, which determines the priority for handling bundles. A larger value indicates a higher priority. If priorities are not properly assigned, the configuration may not work particularly well. Priority: 0, // Reuse modules already included in main. reuseExistingChunk: true, test: /node_modules\/(.*)\.js/, }, commons: { name: "commons", chunks: "async", priority: -10, reuseExistingChunk: true,}, // Appends the style cache group in addition to the JS subcontracting configuration. styles: { test: /\.css$/, chunks: "all", reuseExistingChunk: true, enforce: true, priority: 10, }, }, maxSize: 1000000,}}Copy the code

Add this configuration to the build performance:

CSS code compression effect:

Image compression

I use image-webpack-loader in this process. This loader mainly relies on a third-party library called Imagemin, and some external libraries are used to compress images in the Node environment, so these libraries will be automatically downloaded after downloading loader. Is it necessary to use this loader? I think it is useful to pursue the ultimate package volume and image loading speed according to the specific needs of the project. However, it has a great impact on the construction time, and it is troublesome to rely on image-webpack-Loader for domestic installation. It is a bit of a joke that all tools seem to be made by the same team. Why are the configuration parameters of each tool different?

Let’s go straight to the code:

wbepack.base.conf.js

/ / pumped from the module of processing image loader const imageLoaders = {test: / \. (cur | PNG |, jpe." g|gif|svg)(\? . *)? $/, use: [ { loader: 'url-loader', query: { esModule: false, limit: 10, name: utils.assetsPath('img/[name].[ext]') } }, ] }; if (process.env.NODE_ENV === "production" && config.build.imageCompress) { imageLoaders.use.push({ loader: "Image-webpack-loader ", options: {// handle jpeg mozjpeg: {quality: 95, progressive: False creates baseline JPEG file. I'm not a graphics guy, I don't know what baseline means, so I chose the default value true. }, // GIF GIFsicle: {interlaced: true,}, // Composes JPG and PNG images to WEBP. My images here are almost all in PNG format, so I have not configured tools specifically for PNG images to use WEBP for laced: true. Webp: {quality: 85, // image quality method: 5, // 0-6 Module: [... // add imageLoaders in module]Copy the code

Take a look at the comparison:

Of course, this compression process is actually time-consuming, so I don’t enable it in local development.

Attached: a list of configuration parameters for several configurable items of image-webpack-loader:

Github.com/imagemin/im…

Github.com/imagemin/im…

Github.com/imagemin/im…

Github.com/imagemin/im…

Github.com/imagemin/im…

Multithreading (use with caution)

The main purpose of terser-webpack-plugin is to make full use of computing power. As far as I know, the Terser-webpack-plugin is enabled with multi-threading by default. Readers can see this when opening the task manager at build time. In addition, there are other tools that take too long to deal with in the build process. Since happypack has not been updated for a long time, I chose to add Thread-Loader in the build process to try to reduce the build time.

After repeated testing for several times, I determined that this loader was a little bit flawed, and the effect of Loader itself was controversial, which was seldom mentioned in various tutorials or posts in China. I found a blog through Google, in which negative optimization was mentioned, and it would take more time after using Loader. Zhihu on a brother is also tested, zero configuration or negative optimization configuration is wrong, the configuration of the build time also can raise a nothing, switch configuration to run several times after, I be adjust a no negative optimization configuration (positive optimization are slim, of course, the overall time it’s not a big fluctuations). And there is no guarantee that it can be applied to another project. Here is a reference for readers:

Multithreading Babel translation and image compression:

// webpack.prod.conf.js const threadLoader = require("thread-loader") threadLoader.warmup({ poolTimeout: 1000, workerParallelJobs:50, poolParallelJobs:500 },["babel-loader"]) threadLoader.warmup({ poolTimeout: 800, workerParallelJobs:50, poolParallelJobs:500 // workers: 6,}, [" image - webpack - loader "]) / / pumped from the module of processing image loader const imageLoaders = {test: / \. (cur | PNG |, jpe." g|gif|svg)(\? . *)? $/, use: [ { loader: 'url-loader', query: { esModule: false, limit: 10, name: utils.assetsPath('img/[name].[ext]') } }, ] }; if (process.env.NODE_ENV === "production" && config.build.imageCompress) { imageLoaders.use = imageLoaders.use.concat([ { loader: "thread-loader", options: { poolTimeout: 1000, workerParallelJobs:50, poolParallelJobs:500 } }, { loader: "Image-webpack-loader ", options: {// handle jpeg mozjpeg: {quality: 90, progressive: }, // GIF gifsicle: {laced: lace: True,}, // Compress JPG and PNG images into WEBP, my images are mostly in PNG format, so I don't have to configure the tools for processing PNG images, use WEBP together. },},},])} const scriptLoaders = {test: /\.js$/, use: [ { loader: "thread-loader", options: { poolTimeout: 1000, workerParallelJobs:50, poolParallelJobs:500 } }, { loader: "babel-loader", }, ], include: path.resolve(__dirname, ".. /src"), exclude: /node_modules/, }Copy the code

The construction time of my project is from about 110s to about 150s(the number of image files is about 300), and the average construction time is 148s after the addition of multi-threaded configuration. The average value fluctuation before and after is not very large, but you can say that it is wrong that Thread-Loader does not take effect. You can see that the CPU usage for the first minute (which lasts about half a minute while image-Webpack-Loader is compressing files) is significantly improved, but I don’t know why this improvement is not reflected in the build time. It needs to be reminded that, due to multi-threading, the desktop will keep popping up because the Node application that image-webpack-loader relies on is constantly started, affecting normal work. So it is recommended that you do not enable multi-threading for image-webpack-loader when building locally (or lock the screen and take a break)

Use caching wisely

The cache-loader is used to cache the results of partial compiles. again, you should only use it if the performance is heavy. Similar to thread-loader, you can add cache-loader to the loader to be cached.

Use cache-loader in CSS code processing:

/ / utils. Js... ... ... Function generateLoaders (loader) {... if (options.usePostcss){ outputLoaders.push(postcssLoader) } // Insert cacheloader before postCSS-loader and Mini-CSs-extract-plugin loader -laoder if (options.usecsscache) {outputLoaders.push({loader:"cache-loader"})} if (loader) { Const targetLoader = {loader:loader+"-loader"} outputLoaders.push(targetLoader)} return outputLoaders}... ... ... utils.jsCopy the code

Use cache-loader for image compression and Babel translation:

/ / pumped from the module of processing image loader const imageLoaders = {test: / \. (cur | PNG |, jpe." g|gif|svg)(\? . *)? $/, use: [ { loader: 'url-loader', query: { esModule: false, limit: 10, name: utils.assetsPath('img/[name].[ext]') } }, ] }; if (process.env.NODE_ENV === "production" && config.build.imageCompress) { imageLoaders.use = imageLoaders.use.concat([ { loader: "thread-loader", options: { poolTimeout: 1, workerParallelJobs:50, poolParallelJobs:500}}, "Cache-loader ",}, {// image-webpack-loader......},])} const scriptLoaders = {test: /\.js$/, use: [{loader: "thread-loader", options: { poolTimeout: 1, workerParallelJobs:50, poolParallelJobs:500}, // add cache-loader {loader: "cache-loader",}, {loader: "babel-loader", options: {// If you only need to cache babel-loader's results, set cacheDirectory to true. // cacheDirectory: true,},},], include: path.resolve(__dirname, ".. /src"), exclude: /node_modules/, }Copy the code

The cache-loader can be used to cache the results of some loaders that consume a lot of performance. The cache-loader can be used to cache the results of some loaders that consume a lot of performance. The second and subsequent build times have been significantly shortened (with a villainous laugh) :

Before:

After:

Fat out of the fist

The effect of pull

Unfortunately, if the automatic build starts with the node_modules directory uninstalled and dependencies reinstalled, the cache will also…

Lint mechanism

Lint is a mechanism for checking static code to detect explicit problems in code before compilation, and is also used to establish and enforce a code specification. Lint checks can be divided into style checks and quality checks. For example, if the indentation rule is set to two Spaces in a project, if a line of code has four Spaces in it, the indentation problem is a code style problem. If you write a function and write other code on the line after the return, such code that will never execute can be classified as a code quality problem when it is detected by the Lint mechanism. Here I use code style to prettier for control, esLint mainly checks for code quality issues and a few for code style issues.

Eslint (to do this you need to download esLint and eslint-Loader, which fall under devDependencies in package.json) :

The editor I use is vscode, so in this section I’ll just cover some configuration for eslint on vscode and webpack.

**.eslintrc** in the root directory to create a configuration file (json) that describes the tools, plug-ins, and code rules that esLint uses:

Module. exports = {// first you need to specify the scope to check. Root defaults to true, starting from the root directory root: true, // specify the runtime env: {node: ParserOptions: {parser: "babel-eslint", "ecmaVersion": {parser: "babel-eslint", "ecmaVersion": }, // extends can be understood as inheriting a set of configurations. Recommended integration is the core rule of ESLint. This extends is recommended whenever you use ESLint: ["eslint:recommended","plugin:vue/recommended", "@vue/prettier"], // Select a rule, configure it as needed, Detailed see https://cn.eslint.org/docs/rules/ rules: {" no - the console ": the process. The env. NODE_ENV = = =" production "? "error" : "off", "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", // Stylistic Issues "no-multi-spaces":["error", { ignoreEOLComments: false }], // ECMAScript 6 "prefer-const": ['warn',{ "destructuring": "any", "ignoreReadBeforeAssign": false }], "arrow-spacing": ['warn',{ "before": true, "after": true }], }, }Copy the code

You need to download the dependencies mentioned in the configuration file, otherwise these will generate errors or warnings during or after the build.

Set a whitelist.

dist
src/library
node_modules
static
// ...
Copy the code

Add eslint-Loader to webPack configuration (I only use ESLint in local development environment, but can also enable it in Production mode, which takes more time to build. However, I think it is a management problem if the syntax error/warning is not solved in the local environment.

webpack.base.conf.js

Const createLintingRule = () => ({createLintingRule = () => ({createLintingRule = () => ({createLintingRule = (); / \. (js | vue) $/, loader: "eslint - loader", enforce: "pre", / / can include property regulation scope / / include: / SRC/options: {formatter: require("eslint-friendly-formatter"), emitWarning: ! config.dev.showEslintErrorsInOverlay } }); ... ... ... Rules: [// config.dev.useEslint = true...(config.dev.useesLint? [createLintingRule()] : []), …… …… ……]Copy the code

Prettier (download dependency)

See all the options here

.prettierrc(json):

{
  "eslintIntegration": true,
  "singleQuote": false,
  "bracketSpacing": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "semi": true,
  "quoteProps": "as-needed"
}
Copy the code

If you need the editor to tell you what you need to do/change, or to set the save autofix, you can also download the eslint plugin in the vscode extension. Eslint and tip will try to repair to repair the problem (there will be a special icon in the cn.eslint.org/docs/rules/ flagged); Batch fixes can also be made by setting the NPM command “eslint –fix”, although fixes can sometimes produce unexpected results, which may cause other logical errors, so use fix with caution if you are introducing ESlint in the middle of a project.

.vscode/settings.json

{// TAB length "editor.tabSize": 2, // Number of characters in a row "Editor. rulers": [120], // line number "files.eol": "\n", "editor.lineNumbers": "On", / / the code prompt "editor. SnippetSuggestions" : "top", / / save automatically repair "editor. CodeActionsOnSave" : {" source. FixAll ": True,}, / / vetur configuration "vetur. Format. The options. TabSize" : 4, "vetur. Format. ScriptInitialIndent" : false, "vetur.format.styleInitialIndent": true, "vetur.format.defaultFormatter.html": "prettyhtml", "vetur.format.defaultFormatter.js": "prettier", "vetur.format.defaultFormatterOptions": { "js-beautify-html": { "wrap_line_length": 120, "wrap_attributes": "auto" } }, "eslint.validate": [ "javascript", "javascriptreact", "vue", ], "eslint.options": { "extensions": [".js",".vue"] } }Copy the code

Added: ESLint integration portal on various platforms

I would have liked to write some nice improvements like console output beautification and portfinder, but then I realized that there are no barriers to these things and not everyone needs them, so I decided to end this already boring article here.

Not very stable in recent time (or special steadily less), but this blog is really spent a lot of time, the main is lazy, and afraid of being sprayed bad so over and over in order to guarantee the authenticity of the data change configuration and then to run the project, although I myself to this kind of constipation type of update is not ashamed, but whether to write the article well, writing the code also good, I am really not satisfied with my status during this period. In short, I hope I can learn and share more meaningful content in the future.