preface

The official version of WebPackage 5 has been released for 2 months, I believe everyone is eager to upgrade and experience the function of “true fragrance”. The main building tool of our team has been upgraded to the latest WebPackage 5.x+babel7.x, after a month, we stepped on many holes. After hands-on use, webpack5 has improved its package size, continuous compilation speed and compatibility with webpack4. Module Federation also provides a solution for how to use microfront-end applications in projects.

Important upgrade points

  • 1, Improve build performance with Persistent Caching
  • Improve Long Term Caching with better algorithms and defaults
  • Improve bundle size with better Tree Shaking and Code Generation
  • 4, Clean up internal structures that were left in a weird state while implementing features in V4 without introducing any Breaking Changes (Implement functionality in V4 while cleaning up internal structures that are in an abnormal state without introducing any major changes. For example, Polyfills are no longer automatically referenced for Node.js modules.)
  • 5, Improve compatibility with the Web platform
  • 6. Module Federation
  • 7. Click to view more webPack5 upgrade logs

Before upgrading, it is recommended that you read the preparations for upgrading from V4 to V5. The latest official documentation requires node to be at least version 10.13.0

To upgrade the Babel

If your team is still using Babel6, please read this article and focus on polyfill solutions. There are two types of polyfill solutions:

  • Scheme one is global entry file header introductionimport "@babel/polyfill";Or in the Webpack entryentry: ["@babel/polyfill", "./src/index.js"]
  • Scheme 2 is@babel/plugin-transform-runtime@babel/runtimeWe use this scheme

@babel/polyfill will pollute global variables, so it is suitable for application development. @babel/ Runtime will not pollute global variables, so it is suitable for library development. There’s a big difference in how it’s implemented now. @babel/ Polyfill was deprecated after 7.4..babelrc file configuration, or you can configure it to webapck.congig.js under babel-loader:

{ "presets": [ [ "@babel/preset-env", { "targets": { "browsers": [" > 5% ", "Internet explorer 10", "7" iOS, "Firefox" > 20]}, "useBuiltIns" : "usage", / / on-demand loaded polyfill "corejs" : 3}]], "plugins" : [ ["@babel/plugin-transform-runtime"] ] }Copy the code

Dependencies in package.json

"Dependencies" : {" @ Babel/runtime ":" ^ 7.12.1 ", "core - js" : "^" 3.8.1,}, "devDependencies" : {" @ Babel/core ": "^ 7.12.10 @", "Babel/plugin - transform - arrow - functions provides" : "^ 7.12.1", "@ Babel/plugin - transform - runtime" : "^ 7.12.1 @", "Babel/preset - env" : "^ 7.12.1", "@ Babel/runtime - corejs3" : "^ 7.12.1", "Babel - loader" : "^ 8.1.0"}Copy the code

Upgrade webpack

npm i weback@latest webpack-cli@latest -D
Copy the code

Other common apis: Entry, output, and Module are almost unchanged. Here are a few points of webPack5 update:

Target to upgrade

Previously webpack4 only supported two types of crude:target: 'web'.target: 'node'Now webpack5 defaults to['web', 'es6'], that is, es6 output will be packaged by default, as shown below:This header function will directly report errors in incompatible browsers (IE), of course, this is a good thing for businesses that do not consider compatible with older versions of the browser, you can directly use the smaller and better performance of ES6 code, after all, IE is gradually becoming obsolete. Webpackage 5 supports multiple configurations and supports the version number after the character stringTarget: 'node12.18', you can also pass an array, such as:target: ['web', 'es5']Can solve the above problem, but also can passbrowserslistAnd moreTo view the document

More thoroughly tree-shaking

  • Webpack5 now handles tree shaking for nested modules. Here’s an example:
// a.js export const name = 'JavaScript'; Export const age = "Brendan Eich created me in 1995, I'm 25 years old "; // b.js import * as a from './a'; export { a }; // index.js import * as b from './b'; console.log(b.a.name);Copy the code

The webpack4 package looks like this:

! function(e){var n={}; function t(r){if(n[r])return n[r].exports; var o=n[r]={i:r,l:! 1,exports:{}}; return e[r].call(o.exports,o,o.exports,t),o.l=! 0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:! 0,get:r})},t.r=function(e){"undefined"! =typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esMo dule",{value:! 0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e; if(4&n&&"object"==typeof e&&e&&e.__esModule)return e; var r=Object.create(null); if(t.r(r),Object.defineProperty(r,"default",{enumerable:! 0,value:e}),2&n&&"string"! =typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o)); return r},t.n=function(e){var n=e&&e.__esModule? function(){return e.default}:function(){return e}; return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=0)}([function(e,n,t){"use strict";t.r(n);var r={};t.r(r),t.d(r,"name",(function(){return o})),t.d(r,"age",(function(){return u}));const o="JavaScript",u="Brendan Eich created me in 1995, I'm 25 years old ";console.log(r.name)}]);Copy the code

Webapck5 packaged:

(()=>{"use strict"; console.log("JavaScript")})();Copy the code
  • 2. Webpack is now able to analyze relationships between multiple modules
import { something } from './something';

function usingSomething() {
  return something;
}

export function test() {
  return usingSomething();
}
Copy the code

When “sideEffects”: false is set, if the test method is not used, not only test will be removed, but also “./something” will be removed.

  • 3. At one time, WebPack did not support export usage analysis for CommonJs and require() calls. Webpack 5 now adds support for a number of CommonJs constructs, allowing the elimination of unused CommonJs exports and tracking of referenced export names from require() calls, with the following constructs supported:
Exports | this | module. Exports. XXX =... Exports | this | module exports = the require ("..." ) (reexport) exports | this | module. Exports. XXX = the require ("..." ). XXX (reexport) Object. DefineProperty (exports | this | module. Exports, "XXX",...). XXX require(" ABC ").xxx() import from ESM require() an ESM module is flagged for export types (with special treatment for non-strict ESM imports) more constructs are supported in future plansCopy the code

Persistent cache

Before Webpack5, you could use cache-loader, hard-source-webpack-plugin, and babel-loader, Set option.cacheDirectory to write babel-loader compilation results to disk. In webpack5, caching is enabled by default, and caching is in memory by default. It is recommended to start in the development environment and experience the flying packaging speed after the second modification of the file. You can also set the cache:

module.export={ cache { type:'filesystem', // 'memory' | 'filesystem' name: 'umd-cache', named separately and friendly for multiple packets cacheDirectry: 'node_modules/. Cache /webpack', // default cache stored in node_modules/. Cache /webpack // cache dependency, when the cache dependency changes, Config: [__filename]}}Copy the code

The new Asset Modules

Webapck5 already has file-loader, url-loader, and raw-loader built in, but it needs to be replaced with asset

  • asset/resource emits a separate file and exports the URL. Previously achievable by using file-loader.
  • asset/inline exports a data URI of the asset. Previously achievable by using url-loader
  • asset/source exports the source code of the asset. Previously achievable by using raw-loader
{test: /\. SVG /, type: 'asset/source', use: [{loader: 'svgo-loader', // compress SVG options: {plugins: [ {removeTitle: true}, {convertColors: {shorthex: false}}, {convertPathData: false} ] } } ] }Copy the code

The import alert

Webpack will issue a warning if this is introduced directly into the code

import { version } from '.. /package.json';Copy the code



Change the value to default

import packageInfo from '.. /package.json'; const { version } = packageInfo;Copy the code

SplitChunk and module size

Modules are now able to represent sizes in a better way than displaying single numbers and different types of sizes. By default, only javascript sizes can be handled, but you can now pass multiple values to manage them:

optimization{
  splitChunks{
  minSize: {
        javascript: 30000,
        style: 50000,
    }
  }
}

Copy the code

ModuleIds & chunkIds optimization

Prior to webpackage 5, chunk files that were not packaged from entry would start at 1,2,3… File naming method output. (The hash value after the file name is generated with chunkhash)

Js ->1.js, 3.js->2.js, and this will invalidate the cache of 2.js requests that were originally online. Before WebPack5, you could also use webpackChunkName to solve the naming problem

. <Switch> <Route key='/' exact path='/' component={ Loadable({ loader: () => import(/* webpackChunkName: "home" */ './home'), loading: (<div>loadding</div>) }) }/> <Route key='/page1' exact path='/page1' component={ Loadable({ loader: () => import(/* webpackChunkName: "page1" */'./page1'), loading: () => (<div>loadding</div>) }) } /> <Route key='/page2' exact path='/page2' component={ Loadable({ loader: () => import(/* webpackChunkName: "page2" */'./page2'), loading: () => (<div>loadding</div>) }) } /> </Switch> ....Copy the code
  • This seems to solve the cache invalidation problem, but when we open the compiled home.js, chunkId is still there. If the home menu is deleted, the chunkId of page1 and Page2 package will still change:

  • Page1.js is a packaged file
  • File page1.js packed after removing home.js



    Even though page1.js has not been modified, the chunkhashi of page1.js will still change due to the change in chunkId caused by the deletion of home.js, and the problem of cache invalidity still exists.
  • What does webpack5 do

With the new algorithm, in production mode, these functions are enabled by default. ChunkIds: “Deterministic “, moduleIds:” Deterministic “. This algorithm assigns short numeric ids (3 or 4 characters) to Modules and chunks in a deterministic manner. This is a compromise between bundle size and long-term caching.

Optimization. moduleIds: Optional: 1.false tells WebPack not to use any built-in algorithms and provides custom algorithms through plug-ins 2. Natural numeric ids in order of use. 4. Deterministic generates short hash values based on the module name 5. Size Specifies the number ID generated based on the module size optimization.chunkids: Optional: 1. False tells WebPack not to use any built-in algorithms and provides custom algorithms through plug-ins. 2. 4. deterministic generates a short hash value based on the module name 5. size The id calculated based on the size of the requested initial resource 6. total-size: the ID calculated based on the size of the requested resolved resourceCopy the code

FAQ

  • 1. Start webpack-dev-server
webpack serve --config
Copy the code
  • 2. Webpack-upload is incompatible
  • 3. Less-loader option
options: { lessOptions? , additionalData? , sourceMap? , webpackImporter? }Copy the code
  • 4. Postcss-loader option
options: {
	postcssOptions: {
    	plugins: []
    }
}
Copy the code

The last

. I don’t have enough space to talk about these upgrades, federated modules, more friendly Node support, etc. More also need you to query the documentation, continuous trial and error, below post a simple version of webpack.config.js

/** * @file webpack */ const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const path = require('path'); const isProd = process.env.NODE_ENV === 'production'; const client = { entry: './src/index.js', devtool: isProd ? false : 'cheap-module-source-map', mode: isProd ? 'production' : 'development', target: ['web', 'es5'], output: { path: `${__dirname}/browser`, filename: 'index.js', library: 'Player', libraryTarget: 'window', libraryExport: 'default'}, cache: {// 'filesystem' / / 'memory' | 'filesystem buildDependencies: {/ / when configuration changes, cache invalidation config: [__filename]}, name: 'client-cache'}, module: {rules: [{test: /\.js$/, // exclude: '/node_modules/', }, { test: /\.scss$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader', 'sass-loader' ] }, { test: /\.svg/, type: 'asset/source', use: [{ loader: 'svgo-loader', options: { plugins: [{ removeTitle: true }, { convertColors: { shorthex: false } }, { convertPathData: false } ] } }] }] }, plugins: [ // new BundleAnalyzerPlugin({ // defaultSizes: 'parsed' // }) ], optimization: { minimize: isProd, } } module.exports = client;Copy the code