Quick learning

  • Yarn init –yes Initializes the project

  • Yarn add webpack webpack-cli –dev Installs the core module and CLI

  • Yarn Webpack –version View the current WebPack version

  • Yarn webpack Packages JS files in a project

The configuration file

  • The default package path is SRC /index.js -> dist/main.js

Modifying a Configuration File

//webpack.config.js

const path = require('path')

module.exports = {
  mode:'development'.// Change the working mode. The default is production and the remaining optional parameters are None
  entry: './src/main.js'.// The path and name of the entry file
  output: {
    filename: 'bundle.js'.// The name of the file to export
    path: path.join(__dirname, 'output')// The exported path must be an absolute path}}Copy the code

Resource module loading

  • Yarn add CSS-loader –dev Packages CSS files
  • Yarn add style-loader –dev Load style

Modifying a Configuration File

//webpack.config.js

module.exports = {
  // Change the entry file to a CSS file
  entry: './src/main.css'.// Add the module field
  module: {
    rules: [{test: /.css$/,
        use: [
          'style-loader'.'css-loader'
          // Loader is executed from back to front}]}}Copy the code

Importing a Resource Module

The style file required to load the current module in a JS file is more in line with webPack’s design philosophy

import './heading.css
Copy the code

File resource loader

  • Yarn add file-loader –dev Installs the resource loader

Modifying a Configuration File

module.exports = {
  output: {
    // You need to change the path if the resource file is not in the root directory of the website
    publicPath: 'dist/'
  },
  module: {
    rules: [{test: /.css$/,
        use: [
          'style-loader'.'css-loader']},// Add a resource loader
      {
        test: /.png$/,
        use: 'file-loader'}}}]Copy the code
import icon from './icon.png'

const img = new Image()
img.src = icon

document.body.append(img)
Copy the code

Webpack URL loader

Data URLs

data:image/png; base64,iVBORw0KGgoAAAANSUhE... SuQmCC/ / agreement
data:

// Media type and encodingimage/png; base64,// File contentsiVBORw0KGgoAAAANSUhE... SuQmCC! ~Copy the code
  • Yarn add url-loader –dev Installs the URL loader

Use Data URLs for small files to reduce the number of requests. Large files are separately extracted and stored to improve loading speed

const path = require('path')

module.exports = {  
  module: {
    rules: [{test: /.png$/,
        use: {
          loader: 'url-loader'.options: {
            limit: 10 * 1024 // Package files under 10 KB as Data URLS}}}]}}Copy the code

Common loader classification

  • Compile conversion classes convert loaded resource modules into JS code, such as CSS Loader
  • The file operation class copies the loaded resource file to the output directory and exports the accessed file path
  • The code inspection class verifies the loaded resource files to improve code quality in a unified style

Webpack and ES2015

  • Yarn add babel-loader @babel/ core@babel /preset-env –dev Installs Babel loader, core files, and preset plug-ins
const path = require('path')

module.exports = {
  
  module: {
    rules: [{test: /.js$/,
        use: {
          loader: 'babel-loader'.options: {
            presets: ['@babel/preset-env'}}}]}}Copy the code

How webpack loads resources

  • Follow the ES Modules standard import declaration
  • Follow the CommonJS standard require function
  • Follow AMD standard define function and require function
  • The @import directive and URL function in the style code
@import url(reset.css);
Copy the code
  • The SRC attribute of the image tag in HTML code

Yarn add html-loader –dev Installs the HTML loader

const path = require('path')

module.exports = {
  
  module: {
    rules: [{test: /.html$/,
        use: {
          loader: 'html-loader'.options: {
            attrs: ['img:src'.'a:href'}}}]}}Copy the code

Webpack commonly used plug-ins

clean-webpack-plugin

Clearing the cache file

yarn add clean-webpack-plugin –dev

//webpack.config.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {  
  plugins: [
    new CleanWebpackPlugin()
  ]
}

Copy the code

html-webpack-plugin

Used to generate HTML files

yarn add html-webpack-plugin –dev

You can generate multiple HTML files with multiple instance objects

//webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {  
  plugins: [
    // Used to generate index.html
    new HtmlWebpackPlugin({
      title: 'Webpack Plugin Sample'.meta: {
        viewport: 'width=device-width'
      },
      template: './src/index.html'
    }),
    // Used to generate about.html
    new HtmlWebpackPlugin({
      filename: 'about.html'}})]Copy the code

Plug-in configuration parameters are also accessible within the page

<! -- index.html -->

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Webpack</title>
</head>
<body>
  <div class="container">
    <h1><%= htmlWebpackPlugin.options.title %></h1>
  </div>
</body>
</html>

Copy the code

copy-webpack-plugin

Copy the file

yarn add copy-webpack-plugin –dev

const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {  
  plugins: [    
    new CopyWebpackPlugin([
      // 'public/**'
      'public'// Specify the directory to copy])]}Copy the code

Enhance the WebPack development experience

Automatic compilation

Webpack monitor mode, automatically repackage content after modification

yarn webpack –watch

Automatically refresh the browser

browser-sync dist –files “**/*”

Webpack Dev Server

Installation:

yarn add webpack-dev-server –dev

Run:

yarn webpack-dev-server

Adding the open parameter after it will wake up the browser and refresh yarn webpack-dev-server –open in real time

Webpack supports the configuration agent API

module.exports = {  
  devServer: {
    // Specify the path to the additional resources to be loaded
    contentBase: './public'.proxy: {
      // Set the path name of the interface
      '/api': {
        // http://localhost:8080/api/users -> https://api.github.com/api/users
        target: 'https://api.github.com'.// http://localhost:8080/api/users -> https://api.github.com/users
        pathRewrite: {
          '^/api': ' '
        },
        // Localhost :8080 cannot be used as the host name for requesting GitHub
        changeOrigin: true}}}}Copy the code

Source Map

SourceMap solves the problem of inconsistency between source code and running code

module.exports = {
  devtool: 'eval'
}
Copy the code
devtool build rebuild production quality
(none) fastest fastest yes bundled code
eval fastest fastest no generated code
cheap-eval-source-map fast fastest no transformed code (lines only)
cheap-module-eval-source-map slow fastest no original source (lines only)
eval-source-map slowest fast no original source
cheap-source-map fast slow yes transformed code (lines only)
cheap-module-source-map slow slower yes original source (lines only)
inline-cheap-source-map fast slow no transformed code (lines only)
inline-cheap-module-source-map slow slower no original source (lines only)
source-map slowest slowest yes original source
inline-source-map slowest slowest no original source
hidden-source-map slowest slowest yes original source
nosources-source-map slowest slowest yes without source content

Advice:

Development mode: cheap-module-eval-source-map

Each line of code does not exceed 80 characters. After the code is converted by loader, the difference is large and the repackaging is relatively fast

Production mode: None/nosource-source-map

Avoid exposing source code

MHR

Hot Module Replacement Hot update of the Module

The command to enable the function is

webpack-dev-server –hot

It can also be enabled through a configuration file

// Load the Webpack module
const webpack = require('webpack')

module.exports = {  
  devServer: {
    hot: true
    // hotOnly: true // Only HMR is used. Fallback to live reloading is not allowed. You are advised to enable this function during debugging
  },  
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
}
Copy the code

HMR API

Custom JS modules need to manually call the MHR API to implement hot updates

module.hot.accept('Dependent module path'.() = >{depend on the path after the update handler})Copy the code
import createEditor from './editor'
import background from './better.png'
import './global.css'

const editor = createEditor()
document.body.appendChild(editor)

const img = new Image()
img.src = background
document.body.appendChild(img)

// ============ the following is used to handle HMR, independent of business code ============

// console.log(createEditor)

if (module.hot) {
  let lastEditor = editor
  module.hot.accept('./editor'.() = > {
    // console.log(' Editor module updated, need to handle hot replacement logic manually here ')
    // console.log(createEditor)

    const value = lastEditor.innerHTML
    document.body.removeChild(lastEditor)
    const newEditor = createEditor()
    newEditor.innerHTML = value
    document.body.appendChild(newEditor)
    lastEditor = newEditor
  })

  module.hot.accept('./better.png'.() = > {
    img.src = background
    console.log(background)
  })
}

Copy the code
  • In the case of HMR code, it is recommended to use hotOnly: True only to find problems in the code
  • Add criteria before writing the HMR code to determine whether the current project has HMR enabled
  • The HMR is not packaged into the final compiled file

Production environment optimization

  • The configuration file is exported based on the environment
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = (env, argv) = > {
  const config = {
    mode: 'development'.entry: './src/main.js'.output: {
      filename: 'js/bundle.js'
    },
    devtool: 'cheap-eval-module-source-map'.devServer: {
      hot: true.contentBase: 'public'
    },
    module: {
      rules: [{test: /\.css$/,
          use: [
            'style-loader'.'css-loader'] {},test: /\.(png|jpe? g|gif)$/,
          use: {
            loader: 'file-loader'.options: {
              outputPath: 'img'.name: '[name].[ext]'}}}]},plugins: [
      new HtmlWebpackPlugin({
        title: 'Webpack Tutorial'.template: './src/index.html'
      }),
      new webpack.HotModuleReplacementPlugin()
    ]
  }

  // If it is a production environment
  if (env === 'production') {
    config.mode = 'production'// Change the mode to production environment
    config.devtool = false // Disable source map
    config.plugins = [ // Add some plug-ins required by the production environment. config.plugins,new CleanWebpackPlugin(),
      new CopyWebpackPlugin(['public'])]}return config
}
Copy the code


  • Each environment corresponds to a configuration file

Extract the common parts, configure the rest in a separate file, and merge the common parts together

Install the required modules yarn add webpack-merge –dev

The configuration file YARN webpack –config webpack.prod.js needs to be specified

Common configuration file webpack.common.js

// webpack.common.js
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/main.js'.output: {
    filename: 'js/bundle.js'
  },
  module: {
    rules: [{test: /\.css$/,
        use: [
          'style-loader'.'css-loader'] {},test: /\.(png|jpe? g|gif)$/,
        use: {
          loader: 'file-loader'.options: {
            outputPath: 'img'.name: '[name].[ext]'}}}]},plugins: [
    new HtmlWebpackPlugin({
      title: 'Webpack Tutorial'.template: './src/index.html'}})]Copy the code


The development environment configuration file webpack.dev.js

//webpack.dev.js
const webpack = require('webpack')
const merge = require('webpack-merge')
const common = require('./webpack.common')

module.exports = merge(common, {
  mode: 'development'.devtool: 'cheap-eval-module-source-map'.devServer: {
    hot: true.contentBase: 'public'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
})
Copy the code





//webpack.prod.js
const merge = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const common = require('./webpack.common')

module.exports = merge(common, {
  mode: 'production'.plugins: [
    new CleanWebpackPlugin(),
    new CopyWebpackPlugin(['public'])]})Copy the code

DefinePlugin

Inject global members into your code

//webpack.config.js
const webpack = require('webpack')

module.exports = {
  mode: 'none'.entry: './src/main.js'.output: {
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.DefinePlugin({
      // The value requires a snippet of code
      API_BASE_URL: JSON.stringify('https://api.example.com')]}})Copy the code

When you write code

console.log(API_BASE_URL)
Copy the code

Use key values automatically after packaging is complete

console.log("https://api.example.com")
Copy the code

Tree Shaking

Remove unused code

//webpack.config.js
module.exports = {
  mode: 'none'.entry: './src/index.js'.output: {
    filename: 'bundle.js'
  },
  optimization: {
    // The module exports only used members
    usedExports: true.// Merge each module into a function if possible
    concatenateModules: true.// Compress the output
    minimize: true}}Copy the code

Tree Shaking & Babel

Tree Shaking is based on ES Modules. Code packaged by WebPack must use ESM. In order to convert new ECMAScript features in code, Babel will probably convert ES Modules to CommonJS

The latest version of Babel does not convert code to CommonJS form

You can select whether to enable ESM conversion in the configuration file

module.exports = {
  mode: 'none'.entry: './src/index.js'.output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [{test: /\.js$/,
        use: {
          loader: 'babel-loader'.options: {
            presets: [
              // If Babel has already transformed ESM when loading the module, Tree Shaking will fail
              // ['@babel/preset-env', { modules: 'commonjs' }]
              // ['@babel/preset-env', { modules: false }]
              // You can also use the default configuration, i.e. Auto, so that babel-Loader automatically shuts down ESM conversion
              ['@babel/preset-env', { modules: 'auto'}]]}}}]},optimization: {
    // The module exports only used members
    usedExports: true.// Merge each module into a function if possible
    // concatenateModules: true,
    // Compress the output
    // minimize: true}}Copy the code

sideEffects

What a module does when it executes other than exporting members is generally used for NPM package flags whether there are side effects When flagged as no side effects, unused modules are not packaged

//webpack.config.js
module.exports = {
  mode: 'none'.entry: './src/index.js'.output: {
    filename: 'bundle.js'
  },
  optimization: {
    // This function is enabled by default in production environments
    sideEffects: true,}}Copy the code
//package.json
{
  "name": "31-side-effects"."version": "0.1.0 from"."main": "index.js"."author": "zce <[email protected]> (https://zce.me)"."license": "MIT"."scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "css-loader": "^ 3.2.0"."style-loader": "^ 1.0.0"."webpack": "^ 4.41.2"."webpack-cli": "^ 3.3.9"
  },
  "sideEffects": [
    // You can specify files that have side effects, and return a false Boolean if there are no side effects
    "./src/extend.js"."*.css"]}Copy the code

The code segment

Multiple entry packing

Suitable for multi-page applications, one page corresponds to a package entry, the common part is extracted separately

const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'none'.entry: {
    // Configure multiple entries in the form of objects
    index: './src/index.js'.album: './src/album.js'
  },
  output: {
    // Dynamically generate file names based on entry
    filename: '[name].bundle.js'
  },
  module: {
    rules: [{test: /\.css$/,
        use: [
          'style-loader'.'css-loader']]}},plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'Multi Entry'.template: './src/index.html'.filename: 'index.html'.// Specify the bundles to inject via the chunks attribute
      chunks: ['index']}),new HtmlWebpackPlugin({
      title: 'Multi Entry'.template: './src/album.html'.filename: 'album.html'.chunks: ['album']]}})Copy the code

Extract common modules

module.exports = {  
  optimization: {
    splitChunks: {
      // Automatically extract all public modules into separate bundles
      chunks: 'all'}}}Copy the code

Dynamic import

When a module is needed, the dynamically imported module will be automatically subcontracted

const render = () = > {
  const hash = window.location.hash || '#posts'
  const mainElement = document.querySelector('.main')
  mainElement.innerHTML = ' '

  if (hash === '#posts') {
    // Import the module's path destruct
    import('./posts/posts').then(({ default: posts }) = > {
      mainElement.appendChild(posts())
    })
  } else if (hash === '#album') {
    // Rename files generated by subcontracting
    import(/* webpackChunkName: 'components' */'./album/album').then(({ default: album }) = > {
      mainElement.appendChild(album())
    })
  }
}

render()

window.addEventListener('hashchange', render)


Copy the code

Extract the CSS

Plug-ins can be used to extract CSS into separate files

MiniCssExtractPlugin

yarn add mini-css-extract-plugin –dev

Compress CSS and JS

OptimizeCssAssetsWebpackPlugin

yarn add optimize-css-assets-webpack-plugin –dev

TerserWebpackPlugin

yarn add terser-webpack-plugin –dev

const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')

module.exports = {
  optimization: {
    minimizer: [
      // Specify the compression plug-in
      new TerserWebpackPlugin(),
      new OptimizeCssAssetsWebpackPlugin()
    ]
  },
  plugins: [
    new MiniCssExtractPlugin()// Export CSS]}Copy the code

Output file name Hash

Webpack supports adding a [hash] field to filename to add a hash value to a file

module.exports = {
  mode: 'none'.entry: {
    main: './src/index.js'
  },
  output: {
    filename: '[name]-[hash].bundle.js'}}Copy the code

Three hash forms are supported

// The hash value of all files will change whenever there is any change in the project
filename: '[name]-[hash].bundle.js'

// The hash value changes only when files in the same chunk change
filename: '[name]-[chunkhash].bundle.js'

// The hash value will change only if the current file changes
filename: '[name]-[contenthash].bundle.js'


// You can set the length of the hash value
filename: '[name]-[contenthash:8].bundle.js'

Copy the code