At this point the Webpack version is V4.42.0.

introduce

Gitee has been uploaded to demo during learning, click the jump address

Why use

Front-end web features are rich, especially with the popularity of SPA technology, packaging: JavaScript has increased in complexity and requires a lot of dependency packages

Conversion compilation: Sass, LESS, ES6/ES7, etc

Optimization: When pages are complex, performance can be a problem, and WebPack can be optimized as well

The principle of

The installation

NPM install -g webpackCopy the code
// Initialize NPM initCopy the code
// install webpack NPM install --save-dev webpack webpack-dev-server webpack-cliCopy the code

use

Single file entry

// create webpack.base.js in the root directory
const path = require('path');

module.exports = {
	entry: './src/index.js'.output: {
		path: path.resolve(__dirname, 'dist'),
		filename: 'bundle.js',}};Copy the code
// Package the NPM run buildCopy the code

Multifile entry

const path = require('path');

module.exports = {
  entry: {
    entry: './src/index.js'.second: './src/second.js',},output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'.publicPath: 'static',},// chunkFilename: '',
};
Copy the code
Dist /entry.js dist/second.jsCopy the code

Configuration items

mode

Tell Webpack to use the built-in optimizations for the corresponding pattern.

Production: Pay attention to module size (default) and compress scripts

Development: debugging, hot update

Webpack generates a process.env.node_env variable based on mode, which is a global variable defined in webpack.definedPlugin and allows different code to be executed depending on the environment, for example:

if (process.env.NODE_ENV==='development') {
// Development environment execution
} else {
// Production environment execution
}
Copy the code

In the production environment,uglify will automatically delete unreachable code when packaging the code, that is to say, the final code compressed in the production environment is:

// Production environment executionCopy the code

Hot update HMR

In a development environment, we need to debug the code quickly, so we need the local server to access static files packed with Webpack

Advantages:

  • Preserve application state that is lost when the page is fully reloaded
  • Only update changes to save time
  • Adjusting styles is faster, almost as fast as changing styles in the browser debugger

After listening, hot updates do not generate the actual package file, just live in memory, no disk IO, and faster

Webpack-dev-server is an official tool provided by Webpack. When mode is in development, you can enable hot update and preview the modified code in real time.

First we have webpack-dev-server installed, next we need to configure scripts in package.json,

scripts: {
	dev: 'webpack-dev-server --mode development --config webpack.base.js',}Copy the code
// webpack.base.js
const path = require('path');
const distSrc = path.resolve(__dirname, '.. /dist');

module.exports = {
  entry: {
    entry: path.resolve(__dirname, '.. /src/index.js'),
    second: path.resolve(__dirname, '.. /src/second.js'),},output: {
    path: distSrc,
    filename: '[name].js',},devServer: {
    contentBase: distSrc,
    port: '9091'.// Local access port number
    compress: true.// Whether to enable compression
    hot: true.// Enable hot update}};Copy the code

devServer

Common parameters:

host - default:'localhost'

Hot – Starts hot updates

HotOnly – Hot updates are enabled only for modules that contain HMR

How does HMR work?

In the application,

In the editor,

In a module, if the HMR interface is implemented in the module and updates are received to the HMR, the update replaces the old one. However, not all modules require an HMR interface, and when a module does not implement an HMR interface, its updates bubble, meaning that a simple handler can update the entire module tree.

In such modules, a single module updates and the entire dependency block is reloaded.

In the HMR Runtime

loader

Loader is used to convert source code. You can convert different languages to JavaScript. You can preprocess imports or preprocess files while loading modules. Loader can convert inline images to data urls and even import CSS files directly from JavaScript,

Loader parameters:

Test: the extension of the matching processing file

Use: Loader name, the name of the module to be used

Include /exclude: Adds mandatory files or excludes unnecessary files

Query: Provides additional setup options for loaders.

css-loader: The css-loader interprets @import and url() like import/require() and will resolve them.

The main purpose is to handle dependencies in CSS, such as @import and URL () declarations that refer to external files

style-loader: Inject CSS into the DOM.

The result of csS-loader parsing is converted into JS code, and the runtime inserts style tags dynamically to make the CSS code work.

Url-loader /file-loader: used to process JPG, PNG, and GIF files

Sass-loader node-sass parses SCSS files

module: {
  rules: [{test: /\.(css|less)$/.use: [{loader: 'style-loader'
        },
        {
          loader: 'css-loader'.options: {
            importLoaders: 1,}}, {loader: 'postcss-loader'.options: {
          ident: 'postcss'.plugins: (loader) = > [
              require('postcss-import') ({root: loader.resourcePath }),
              require('postcss-cssnext') (the),require('autoprefixer') (the),require('cssnano')(),],},}, {loader: 'less-loader'.options: {
            importLoaders: 1,},},],}, {test: /\.(png|jpg|gif)/.use: {
        loader: 'url-loader'.options: {
          limit: 1024.fallback: {
            loader: 'file-loader'.options: {
              name: 'img/[name].[hash:8].[ext]',},},},},},},Copy the code

or

rules: [
	{ test: ' '.use: ' '}].Copy the code

babel

The current version of the browser does not support es6/ ES7 syntax, you need to switch to ES5 syntax.

After packing:

NPM install -d babel-loader @babel/ core-babel /preset-env NPM install -d babel-loader @babel/ core-babel /preset-envCopy the code

Babel-polyfill is introduced and needs to be used in production environments

npm intall --save babel-polyfill
Copy the code

Not the introduction of Babel – polyfill

After the introduction of

Entry.js is obviously a lot bigger because babel-Polyfill pollutes the global environment by adding promises as global variables. The plugin-transform-Runtime is required

Advantages:

Does not pollute the global variable multiple use will only be packaged once dependency unified introduction on demand, no repeated introduction, no redundant introductionCopy the code
{
   test: /\.js$/.use: {
     loader: 'babel-loader'.options: {
       presets: ['@babel/preset-env'].plugins: ['@babel/plugin-transform-runtime'],}}},Copy the code

// error
Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
Copy the code

When you use @babel/ plugin-transform-Runtime in a commonJS file, Babel assumes that the file is an ES6 file and imports the plugin using import. This led to the error of mixing import and module.exports. The solution is to configure unambiguous Settings in babel.config.js so that Babel, like WebPack, strictly differentiates commonJS files from ES6 files.

{
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-transform-runtime'].sourceType: 'unambiguous',}}},Copy the code

plugin

In the webpack build process, plugin users handle more other build tasks, loader is used to convert languages, better compatibility.

Block, compress, optimize

Since webPack was upgraded to 4, it boasts zero configuration. The code will automatically split, compress, and optimize, and Webpack will automatically Scope and tree-shaking you as well.

When multiple bundles share the same dependencies, these dependencies need to be extracted into the shared bundle to avoid repeated packaging. CommonsChunkPlugin in webpack4.0 were removed, replaced by optimization. The splitChunks and optimization. The runtimeChunk

Built-in code separation strategy:

Whether the new chunk is shared or from the node_modules module

Whether the new chunk size is greater than 30KB before compression

The number of concurrent requests for loading chunk on demand is less than or equal to 5

The number of concurrent requests during initial page loading is less than or equal to 3

Chunk analysis plug-in

npm install --save-dev webpack-bundle-analyzer
Copy the code
// vue.config.js or webpack.base.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

plugins: [
    new BundleAnalyzerPlugin({
        analyzerMode: 'server'.// analyzerHost: '127.0.0.1',
        analyzerPort: 7777.reportFilename: 'index.html'.defaultSizes: 'parsed'.openAnalyzer: true.generateStatsFile: false.statsFilename: 'stats.json'.statsOptions: null.logLevel: 'info',})],Copy the code
// package.json
scripts: {
	"analyze": "NODE_ENV=production npm_config_report=true npm run build"
},
Copy the code

DefinePlugin

Allows you to create a global variable that can be used at compile time.

The separation of CSS

mini-css-extract-plugin

npm install --save-dev mini-css-extract-plugin
Copy the code
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

output: {
    filename: '[name].js'.// fix img path in HTML
    publicPath: 'http://localhost:9091',},plugins: [
	new MiniCssExtractPlugin({
		filename: 'css/[name].js'.chunkFilename: '[id].css',})],module: {
	rules: [{test: /\.(css|less)$/.use: [
				MiniCssExtractPlugin.loader,
				css-loader,
				less-loader,
			],
		}
	],
}
Copy the code

Images introduced in HTML:

Images introduced in CSS:

Automatically adds CSS property prefixes

Prefixes the CSS with autoprefixer. Previously, when writing to csS-Loader, you added the PostCSS-Loader for CSS, which includes the autoprefixer plug-in

rules: [
	{
		test: /\.(css|less)$/.use: [
			MiniCssExtractPlugin.loader,
			'css-loader'.'post-loader',]}],Copy the code
// Add the file postcss.config.js to the root directory
module.exports = {
	plugins: {
		autoprefixer: {},}};Copy the code
// Add the file.browserslistrc to the root directory (recommended)
> 1%
last 2 versions
not ie <= 8

// Add it to package.json (not recommended)
 "browserslist": [
    "last 1 version"."1%" >."IE 10"
  ]

Copy the code

Ignore module packaging

IgnorePlugin: Ignores specific modules so that WebPack does not pack them.

module.exports = {
  // ...
  plugins: [
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
  ]
}
Copy the code

The IgnorePlugin configuration takes two parameters. The first is a regular expression that matches the path of the incoming module, and the second is the name of the directory in which the module resides.

Ignore the package module configuration in vue-CLI:

module.exports = {
	chainWebpack: (config) = > {
		config.externals({
			'element-ui': 'ElEMENT'.'moment': 'moment',})}};Copy the code

These ignored unpackaged files can be imported as script.

<script src="< % = BASE_URL % > [email protected]"></script>
<script src="< % = BASE_URL % > [email protected]"></script>
Copy the code

Split code file

In order to reduce the size of packaged code and use caching to speed up static resource access, it is necessary to separate different and non-affecting code blocks. Plugin says that mini-CSS-extract-plugin can be used to separate CSS files. In addition, It is recommended that publicly used third-party class libraries be explicitly configured as public parts. Because in the actual development of third-party libraries, there is little change, which can avoid cache invalidation due to frequent changes in the public chunk.

Hash: All files with the same hash value will be regenerated regardless of whether the file is modified

Chunkhash: resolves dependent files according to different entry files, constructs corresponding chunks, generates corresponding hash, and does not need to rebuild without changing the code.

Contenthash: Since CSS and JS use the same chunkhash, CSS will be regenerated when only JS is changed. So CSS uses contenthash

module.exports = {
  entry: {
    vendor: ["react"."lodash"."angular". ] .// Specify a third-party library for public use
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks: "initial".test: "vendor".name: "vendor".// Use the vendor entry as the public part
          enforce: true,},},},},/ /... Other configuration
}

/ / or
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /react|angluar|lodash/.// Use test directly to do path matching
          chunks: "initial".name: "vendor".enforce: true,},},},},}/ / or
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks: "initial".test: path.resolve(__dirname, "node_modules") // paths in node_modules are public parts
          name: "vendor".// Use the vendor entry as the public part
          enforce: true,},},},},}...Copy the code

Vue – in cli vue. Config. Js

module.exports = {
chainWebpack: (config) = > {
	config.optimization.splitChunks(Object.assign({}, splitOptions, {
      name: false.cacheGroups: {
        default: false.vendors: {
          name: 'chunk-vendors'.test: /[\\/]node_modules|plat-utils[\\/]/.minChunks: 2.priority: 11.chunks: 'all'.reuseExistingChunk: true,},betterScroll: {
          test: /[\\/]node_modules[\\/]better-scroll[\\/]/.name: 'better-scroll'.priority: 12.chunks: 'all'.reuseExistingChunk: true.enforce: true,},vueRouter: {
          test: /[\\/]node_modules[\\/]vue-router[\\/]/.name: 'vue-router'.enforce: true.priority: 12.chunks: 'all'.reuseExistingChunk: true,},vueLazyload: {
          test: /[\\/]node_modules[\\/]vue-lazyload[\\/]/.name: 'vueLazyload'.enforce: true.priority: 12.chunks: 'all'.reuseExistingChunk: true,}}})); }};Copy the code

resolve.alias

module.exports = {
	resolve: {
		alias: {
			utils: path.resolve(__dirname, 'src/utils'),}}};Copy the code

References:

// The original reference
import cookie from './utils/cookie';
/ / alias
import cookit form 'utils/cookie';
Copy the code

Vue – in cli vue. Config. Js

module.exports = {
  configureWebpack: {
    resolve: {
      alias: {
        'utils': path.resolve('utils'), // Configure the alias}},},chainWebpack: (config) = > {
  	config.resolve.alias.set('utils', path.resolve(__dirname, 'src/components')); }};Copy the code

Reference:

Webpackage 4.0 from Scratch

Webpack3.X: The Path to God (24 episodes)

Webpack Chinese Document

Webpack official website

The notes are recorded before, and I am reviewing them recently. By the way, I will send them below. Welcome to correct them if there is any wrong understanding.

Optimization problem

To be updated