webpack 基础_优化2

233 阅读14分钟

webpack 基础_优化

webpack 优化配置

dll:动态连接库

dll与external类似,指示 webpack哪些库是不参与打包的;可以单独对某些库进行单独打包,将多个库打包成一个 chunk

  • 作用:将第三方库拆开打包成不同的 chunk,更利于性能优化

  • 下载插件

npm i add-asset-html-webpack-plugin -D
  • 修改配置
// webpack.config.js
// nodejs
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 将 css从 js中提取出来,并到单独的文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// css代码压缩
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// pwa
// const WorkboxWebpackPlugin = require('workbox-webpack-plugin')

// dll
const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')

// 定义 nodejs环境变量:决定使用 browserslist的哪个环境
// process.env.NODE_ENV = 'production'
// process.env.NODE_ENV = 'development'

// loader复用
const commonCSSLoader = [
  // 'style-loader',
  // 将 css代码提取到一个指定的文件中
  MiniCssExtractPlugin.loader,
  /**
   * 问题:
   * MiniCssExtractPlugin.loader当前不能将 less文件单独提取出来放到指定的文件中
   */
  // 将 css加载到 js中
  'css-loader',
  /**
   * css兼容性处理:只能针对 css代码来操作
   * postcss ->
   * postcss-loader ->
   * postcss-preset-env:帮助 postcss识别某些环境,加载指定的配置
   */
  {
    /**
     * 需要在 package.json中定义 browserslist里面的配置
     */
    loader: 'postcss-loader',
    options: {
      // 告诉它要做哪些兼容性配置
      ident: 'postcss',
      plugins: () => [
        /**
         * postcss的插件
         * 帮助 postcss找到 package.json中 browserslist里面的配置,
         * 通过配置加载指定的 css兼容性样式
         */
        require('postcss-preset-env')(),
      ],
    },
  },
];

// commonjs
module.exports = {
  // entry: ['./src/js/index.js', './src/index.html'],
  entry: './src/js/index.js',
  output: {
    /**
     * 给文件添加 hash值,防止缓存:[hash:8]
     */
    filename: 'js/built.[contenthash:8].js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      {
        /**
         * 只检查源代码,忽略第三发库
         * eslint-loader
         * eslint
         * 在 package.json的 eslintConfig中设置检查规则,推荐 airbnb
         */
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行此 loader
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          // 自动修复 eslint错误
          fix: true,
        },
      },
      {
        /**
         * oneOf:提升打包构建速度,阻止每种类型的文件都被以下 loader判断一次
         * 在 oneOf中的 loader只会匹配一个
         * 注意:在 oneOf中不能有两个及以上的配置处理同一种类型的文件
         *    如果有,需要拿到 oneOf外边处理
         */
        oneOf: [
          // css代码处理
          {
            test: /\.css$/,
            use: [
              ...commonCSSLoader,
            ],
          },
          {
            test: /\.less$/,
            use: [
              ...commonCSSLoader,
              // MiniCssExtractPlugin.loader,
              // 'css-loader',
              'less-loader',
            ],
          },
          /**
           * 正常来讲,一个文件只能被一个 loader处理;
           * 当一个文件要被多个 loader处理,那一定要指定 loader执行的先后顺序
           * 如下面的 .js文件:应该先执行 eslint再执行 bable,通过 enforce属性指定
           */
          // js语法检查:规范项目代码,检查常见语法错误

          {
            // js兼容性处理:babel-loader、@babel/preset-env、@babel/core
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              /**
               * 开启多进程打包:
               */
              {
                loader: 'thread-loader',
                options: {
                  workers: 2 // 规定只开启两个进程,默认是 cpu核数 - 1
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  // 预设:指示 babel做怎样的兼容处理
                  presets: [
                    [
                      // 只能作简单的语法兼容处理
                      '@babel/preset-env',
                      // corejs可以作复杂的语法兼容处理
                      {
                        // 按需加载
                        useBuiltIns: 'usage',
                        // 指定 core-js版本
                        corejs: {
                          version: 3,
                        },
                        // 指定兼容性做到哪个版本的浏览器
                        targets: {
                          chrome: '60',
                          firefox: '50',
                          safari: '10',
                          edge: '17',
                          ie: '9',
                        },
                      },
                    ],
                  ],
                  /**
                   * 开启 babel缓存
                   * 第二次构建时,会读取缓存,提升构建速度
                   */
                  cacheDirectory: true
                },
              }
            ],
          },

          // 处理图片资源
          {
            test: /\.(png|gif|jpg|jpeg)$/,
            loader: 'url-loader',
            options: {
              // 对于图片小于 8-12kb的可以进行 base64处理
              limit: 8 * 1024,
              // 关闭 ES6解析,使用 commonjs解析
              esModule: false,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
            },
          },
          // 处理 HTML中的 img资源
          {
            test: /\.html$/,
            // 处理 HTML中的 img资源
            loader: 'html-loader',
          },
          // 处理其它资源
          {
            exclude: /\.(html|js|css|less|png|gif|jpg|jpeg)/,
            // 将其它资源原封不动的输出
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]',
              outputPath: 'media',
            },
          },
        ],
      }
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // html代码压缩
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      // 对提取出的 css代码放到统一的文件中,并命名
      filename: 'css/built.[contenthash:8].css',
    }),
    // css代码压缩
    new OptimizeCssAssetsWebpackPlugin(),
    /**
     * dll:
     * webpack.DllReferencePlugin:告诉 webpack哪些库不参与打包,
     *  同时使用时的名称也要变
     */
    new webpack.DllReferencePlugin({
      manifest: resolve(__dirname, 'dll/manifest.json')
    }),
    /**
     * dll的支持插件
     * 将某个打包的文件引入,并在 html中自动引入该资源
     */
    new AddAssetHtmlWebpackPlugin({
      filepath: resolve(__dirname, 'dll/jquery.js')
    })
    /**
     * pwa:
     * 1.帮助 serviceworker快速启动
     * 2.删除旧的 serviceworker
     * 最终:生成一个 serviceworker配置文件
     * 然后在入口文件中注册 serviceworker
     */
    // new WorkboxWebpackPlugin.GenerateSW({
    //   clientsClaim: true,
    //   skipWaiting: true
    // })
  ],
  mode: 'production',
  // mode: 'development',
  /**
   * externals:
   * 添加不被打包的第三方库
   */
  // externals: {
  //   // 忽略的库名 -> npm包名
  //   jquery: 'jQuery'
  // },
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3030,
    open: true,
    // 开启 HRM功能
    hot: true,
  },
  /**
   * source-map的设置:
   * [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
   * source-map:内联
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * inline-source-map:内联
   *  1.将生成的 .map文件放在 .js文件中
   *  2.只生成一个内联的 source-map文件
   *  错误提示:错误代码准确信息和 源代码的错误位置
   * hidden-source-map(防止源代码泄露):外联
   *  1.将生成的一个 source-map文件放在单独的 .map文件中
   *  错误提示:错误代码错误原因,但是没有错误位置,
   *  不能追踪源代码错误,只能提示到构建后代码的错误位置
   * eval-source-map:内联
   *  1.针对每一个文件都生成一个单独的 source-map文件,放在 .js文件的 eval函数中
   *  错误提示:错误代码准确信息 和 源代码的错误位置
   * nosource-source-map(防止源代码泄露):外联
   *  错误提示:错误代码准确信息, 但是没有任何源代码信息(可以防止源代码泄露)
   * cheap-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,只能精确到行
   * cheap-module-source-map:外联
   *  错误提示:错误代码准确信息 和 源代码的错误位置,
   *  module会将 loader的 source-map加入
   * 
   */
  // 开启构建后代码与源码的映射
  devtool: 'eval-source-map' // development
  // devtool: 'source-map' // production
};

  • 添加配置文件
// webpack.dll.js
const { resolve } = require("path");
const webpack = require("webpack");

/**
 * 需要添加一个配置文件
 * 使用 dll技术,对指定的库(第三方库:jquery、react、vue...)进行单独打包
 * 当运行 webapck指令时,默认查找的是 webpack.config.js配置文件
 * 需求:需要运行 webpack.dll.js配置文件
 *  -> webpack --config webpack.dll.js
 */
module.exports = {
  entry: {
    /**
     * jquery -> 最终打包生成的名字
     * ['jquery'] -> 被打包的库是 jquery
     */
    jquery: ['jquery']
  },
  output: {
    // filename: '[name].[contenthash:8].js'
    filename: '[name].js',
    path: resolve(__dirname, 'dll'),
    // library:指定打包的库里面向外暴露出去的内容叫什么名字
    // 如:jquery_325221b0
    library: '[name]_[hash:8]'
  },
  plugins: [
    // webpack.DllPlugin:打包生成一个 manifest.json -> 提供和 jquery的映射关系
    new webpack.DllPlugin({
      // name:映射库暴露的内容名称,要与 library的一致
      name: '[name]_[hash:8]',
      // 指定输出文件路径
      path: resolve(__dirname, 'dll/manifest.json')
    })
  ],
  mode: 'production'
}
  • 运行命令:
npx webpack --config webpack.dll.js
npx webpack

webpack 性能优化总结

开发环境性能优化

主要考虑打包速度和代码调试方面

优化打包构建速度:HMR 热模块替换

当构建代码时,只会构建修改的模块,其它模块使用缓存,提升代码构建速度,使得开发体验更好

  • css:经过 style-loader处理后就会有 HMR功能
  • js:需要开发者自己写
  • html:不需要此功能

优化代码调试:source-map

在开发环境下使得调试更加友好,要搞清楚每个值的含义,并使用推荐的写法

  • 开发环境下:
  1. eval-source-map(推荐):速度快
  2. eval-cheap-module-source-map:打包信息完整
  • 生产环境下:
  1. source-map(推荐):比较完整的映射关系
  2. cheap-module-source-map:速度快,信息完整

生产环境性能优化

优化打包构建速度:

oneOf:

减少 loader的的遍历,在 oneOf中,一种文件类型被对应的 loader处理后,不会被其它 loader遍历

babel缓存:

优化打包构建速度,将第一次 babel构建的结果缓存起来,在之后的打包中直接使用缓存,提升打包构架速度

多进程打包:

打包默认是单继承的,开启多进程打包可以提升打包速度;当程序较大时可以使用,因为每个进程开启大概需要 600ms

externals:

使得某些库不打包,额外使用 cdn等在 html文件中引入不打包的库

dll:

先将某些库在本地打包好,在使用的时候直接引入。

可以考虑将 dll和 code split结合,将 node_modules中的库分别打包,减少文件体积,提升速度

优化代码运行的性能

文件缓存(hash、chunkhash、contenthash
  • hash:webpack打包时,生成新的唯一 hash值
  • chunkhash:webpack打包时,如果打包时是同一个入口,那就同属于一个 chunk,则 chunkhash值相同;因为 css文件和 js文件同属于一个 chunk,当 js变化后,css的 chunkhash也会变化
  • contenthash:根据文件的内容生成 contenthash值;可以保证再次打包时如果文件内容不变,则 contenthash值也不变
tree shaking:

去除程序中引用了但没有使用的代码文件,减少代码体积

  • 必须开启 ES6模块化
  • webapck生产环境默认启动
  • sideEffect[...]:声明不能删除的文件,如样式文件、babel文件...
code split:代码分割
  • 单入口:引入为一个 chunk,输出为一个 bundle
  1. 将 bundle中的一个 总的js文件拆分为多个小的js文件
  2. 使用 optimization处理 node_modules文件
  3. 通过 import引入的文件会自动分割代码
  • 多入口:多入口会引入多个 chunk,输出多个 bundle
  1. optimization
  2. import
懒加载/预加载
  • 懒加载:某个 js代码刚开始不加载,需要的时候才加载;需要和 code split配合使用:异步 + code split
  • 预加载(兼容性差):当需要的代码全部加载完成后,才偷偷去加载其它代码
pwa(兼容性):离线可访问技术

在离线情况下,也可以访问网站

  • serviceWorker
  • cach

webpack 配置详解

entry:

string: './src/index.js'

字符串单入口,打包形成一个 chunk,输出一个 bundle文件,如 built.js, 如使用 [name].js,此时 chunk使用默认名称 main.js

array: [...]

数组多入口,所有入口文件最终会形成一个 chunk,最终输出结果只有一个 bundle文件

  • 作用:在 HMR功能中,让 html热更新生效

object:{...}

对象多入口,有几个入口文件就形成几个 chunk,结果就输出对应个数的 bundle文件 此时,chunk的名称就是对应的 key

特殊用法:

将 2和 3搭配使用,分别按照 2、3的规则进行打包;如 dll中的用法

webpack.config.js

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
/**
 * entry: 入口起点
 * 1. string: './src/index.js'
 *  字符串单入口,打包形成一个 chunk,输出一个 bundle文件,如 built.js,
 *  如使用 [name].js,此时 chunk使用默认名称 main.js
 * 2. array: [...]
 *  数组多入口,所有入口文件最终会形成一个 chunk,最终输出结果只有一个 bundle文件
 *  作用:在 HMR功能中,让 html热更新生效
 * 3. object:{...}
 *  对象多入口,有几个入口文件就形成几个 chunk,结果就输出对应个数的 bundle文件
 *  此时,chunk的名称就是对应的 key
 * 
 * 4. 特殊用法:
 *  将 2和 3搭配使用,分别按照 2、3的规则进行打包;如 dll中的用法
 */
module.exports = {
  // entry: './src/index.js',
  // entry: [
  //   './src/index.js',
  //   './src/add.js'
  // ],
  // entry: {
  //   index: './src/index.js',
  //   add: './src/add.js'
  // },
  entry: {
    index: ['./src/index.js', './src/count.js'],
    add: './src/add.js'
  },
  output: {
    filename: '[name].js',
    // filename: 'built.js',
    path: resolve(__dirname, 'built')
  },
  plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development'
}

output:

  • filename: 文件名称(指定名称 + 目录),如 'js/built.js'
  • path: 输出文件目录(将来所有资源输出的公共目录)
  • publicPath: 所有资源引入公共路径前缀,'imgs/a.jpg' -> 打包后 '/imgs/a.js'
  • chunkFilename: 非入口文件的 chunk名称
    1. import异步加载
    2. optimization
  • library: 整个库向外暴露一个全局变量 name,一般结合 dll使用
  • libraryTarget: 指定向外暴露的全局变量挂载的位置
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
/**
 * output:出口
 * filename: 文件名称(指定名称 + 目录)'js/built.js'
 * path: 输出文件目录(将来所有资源输出的公共目录)
 * publicPath: 所有资源引入公共路径前缀,'imgs/a.jpg' -> 打包后 '/imgs/a.js'
 * chunkFilename: 非入口文件的 chunk名称
 *  1. import异步加载
 *  2. optimization
 * library: 整个库向外暴露一个全局变量 name,一般结合 dll使用
 * libraryTarget: 指定向外暴露的全局变量挂载的位置
 */
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/[name].js',
    // filename: 'built.js',
    path: resolve(__dirname, 'built'),
    publicPath: '/',
    chunkFilename: 'js/[name]_chunk.js',
    library: '[name]',
    // browser
    libraryTarget: 'window',
    // node
    // libraryTarget: 'global',
    // 使用 commonjs的语法规则暴露全局变量
    // libraryTarget: 'commonjs'
  },
  plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development'
}

module:

  • test
  • exclude
  • include
  • enforce
  • loader
  • options
  • oneOf
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/[name].js',
    // filename: 'built.js',
    path: resolve(__dirname, 'built'),
    publicPath: '/',
    chunkFilename: 'js/[name]_chunk.js',
    library: '[name]',
    // browser
    libraryTarget: 'window',
    // node
    // libraryTarget: 'global',
    // 使用 commonjs的语法规则暴露全局变量
    // libraryTarget: 'commonjs'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        // 多个 loader使用数组 use
        use: [
          'use-loader',
          'css-loader'
        ]
      },
      {
        test: /\.js$/,
        // 排除 node_modules下的 js文件
        exclude: /node_modules/,
        // 只检查 src下的 js文件
        include: resolve(__dirname, 'src'),
        // pre 优先执行, post 延后执行
        // enforce: 'pre',
        // 单个 loader使用 loader
        loader: 'eslint-loader',
        // options指定 loader的配置
        // options: {}
      },
      {
        // oneOf中,同种类型的 loader,只会执行一个
        oneOf: []
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development'
}

resolve:

  • alias: 配置解析模块路径别名
  • extensions: 配置省略文件路径的后缀名
  • modules: 告诉 webpack解析模块是去找哪个目录
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
/**
 * resolve: 解析模块的规则
 * 1. alias: 配置解析模块路径别名
 * 2. extentions: 配置省略文件路径的后缀名
 * 3. modules: 告诉 webpack解析模块是去找哪个目录
 */
module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].js',
    // filename: 'built.js',
    path: resolve(__dirname, 'built'),
    // publicPath: '/',
    chunkFilename: 'js/[name]_chunk.js',
    // library: '[name]',
    // browser
    // libraryTarget: 'window',
    // node
    // libraryTarget: 'global',
    // 使用 commonjs的语法规则暴露全局变量
    // libraryTarget: 'commonjs'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        // 多个 loader使用数组 use
        use: [
          // 在程序运行时,可以在浏览器的控制台中看到 style
          'style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development',
  resolve: {
    // 配置解析模块路径别名
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    // 配置省略文件路径的后缀名
    extensions: [
      '.js',
      '.json'
    ],
    // 告诉 webpack解析模块是去找哪个目录
    modules: [
      resolve(__dirname, '../../node_modules'),
      'node_modules'
    ]
  }
}

optimization:

  • splitChunks: 提取公共代码 node_modules成单独的 chunk进行单独打包
  • runtimeChunk: 主要解决 splitChunks的 hash值问题
  • minimizer: 配置生产环境的压缩方案:js和 css
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
const Terser = require('terser-webpack-plugin')
/**
 * optimization: 只在 生产环境中执行
 */
module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name]_[contenthash:8].js',
    // filename: 'built.js',
    path: resolve(__dirname, 'built'),
    // publicPath: '/',
    chunkFilename: 'js/[name]_[contenthash:8]_chunk.js',
    // library: '[name]',
    // browser
    // libraryTarget: 'window',
    // node
    // libraryTarget: 'global',
    // 使用 commonjs的语法规则暴露全局变量
    // libraryTarget: 'commonjs'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        // 多个 loader使用数组 use
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'production',
  resolve: {
    // 配置解析模块路径别名
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    // 配置省略文件路径的后缀名
    extensions: [
      '.js',
      '.json'
    ],
    // 告诉 webpack解析模块是去找哪个目录
    modules: [
      resolve(__dirname, '../../node_modules'),
      'node_modules'
    ]
  },
  devServer: {
    // 运行打包后代码的目录
    contentBase: resolve(__dirname, 'build'),
    // 监视 contentBase目录下的所有文件,一旦文件变化就会 reload
    watchContentBase: true,
    // 监视配置
    watchOptions: {
      // 忽略文件
      ignored: /node_modules/
    },
    // 启动 gzip压缩,减小代码体积
    compress: true,
    // 端口号
    port: 5000,
    // 域名
    host: 'localhost',
    // 打开默认浏览器
    open: true,
    // 开启 HMR功能
    hot: true,
    // 日志方面的内容
    // 不要显示启动服务器有关的日志信息
    clientLogLevel: 'none',
    // 除了一些基本启动信息外,其它内容都不要显示
    quiet: true,
    // 如果出错,不要全屏提示
    overlay: false,
    /**
     * 服务器代理: 解决开发环境跨域问题
     */
    proxy: {
      /**
       * 一旦 devServer(5000)服务器接收到 /api/xxx的请求,
       * 就会自动将请求转发到另外一个服务器(3000)
       */
      '/api': {
        target: 'http://localhost:3000',
        // 请求路径重写:/api/xxx -> /xxx 去掉 /api
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  },
  optimization: {
    // 代码分割 node_modules
    // 提取公共代码 node_modules成单独的 chunk进行单独打包
    splitChunks: {
      chunks: 'all',
      /**
       * 以下都为默认值:一般不用修改
       */
      // 分割的 chunk最小为 30kb
      minSize: 30 * 1024,
      // 最大没有限制
      maxSize: 0,
      // 要提取的 chunk最少要被引用一次
      minChunks: 1,
      // 按需加载时,并行加载的文件的最大数量
      maxAsyncRequests: 5,
      // 入口 js文件,最大并行请求数量
      maxInitialRequests: 3,
      // 名称连接符
      automaticNameDelimiter: '~',
      // 可以使用命名规则
      name: true,
      cacheGroups: {
        /**
         * 分割 chunk的组:也满足上面个的公共规则
         */
        vendors: {
          // node_modules文件会被打包到 vendors组的 chunk中: vendors~xxx.js
          // 
          test: /[\\/]node_modules[\\/]/,
          // 优先级
          priority: -10
        },
        default: {
          // 要提取的 chunk最少被引用 2次
          minChunks: 2,
          // 优先级
          priority: -20,
          // 如果当前被打包的模块和之前已经被提取的模块是同一个,就会复用,解决重复打包问题
          reuseExistingChunk: true,
        }
      }
    },
    /**
     * runtimeChunk:主要解决 splitChunks的 hash值问题
     * 将当前模块记录的其它模块的 hash值单独打包成一个文件 runtime
     * 作用:防止因打包文件的 hash值改变,造成其它引用此模块的 hash值变化,避免造成缓存失效
     */
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}`
    },
    /**
     * 配置生产环境的压缩方案:js和 css
     */
    minimizer: [
      // 4.26版之后,使用 Terser压缩替换 uglify压缩
      new Terser({
        // 开启缓存
        cache: true,
        // 开启多进程打包
        parallel: true,
        // 启动 source-map
        sourceMap: true
      })
    ]
  }
}

devServer:

具体配置参照下面代码

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { resolve } = require("path");
/**
 * devServer:
 */
module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].js',
    // filename: 'built.js',
    path: resolve(__dirname, 'built'),
    // publicPath: '/',
    chunkFilename: 'js/[name]_chunk.js',
    // library: '[name]',
    // browser
    // libraryTarget: 'window',
    // node
    // libraryTarget: 'global',
    // 使用 commonjs的语法规则暴露全局变量
    // libraryTarget: 'commonjs'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        // 多个 loader使用数组 use
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin()
  ],
  mode: 'development',
  resolve: {
    // 配置解析模块路径别名
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    // 配置省略文件路径的后缀名
    extensions: [
      '.js',
      '.json'
    ],
    // 告诉 webpack解析模块是去找哪个目录
    modules: [
      resolve(__dirname, '../../node_modules'),
      'node_modules'
    ]
  },
  devServer: {
    // 运行打包后代码的目录
    contentBase: resolve(__dirname, 'build'),
    // 监视 contentBase目录下的所有文件,一旦文件变化就会 reload
    watchContentBase: true,
    // 监视配置
    watchOptions: {
      // 忽略文件
      ignored: /node_modules/
    },
    // 启动 gzip压缩,减小代码体积
    compress: true,
    // 端口号
    port: 5000,
    // 域名
    host: 'localhost',
    // 打开默认浏览器
    open: true,
    // 开启 HMR功能
    hot: true,
    // 日志方面的内容
    // 不要显示启动服务器有关的日志信息
    clientLogLevel: 'none',
    // 除了一些基本启动信息外,其它内容都不要显示
    quiet: true,
    // 如果出错,不要全屏提示
    overlay: false,
    /**
     * 服务器代理: 解决开发环境跨域问题
     */
    proxy: {
      /**
       * 一旦 devServer(5000)服务器接收到 /api/xxx的请求,
       * 就会自动将请求转发到另外一个服务器(3000)
       */
      '/api': {
        target: 'http://localhost:3000',
        // 请求路径重写:/api/xxx -> /xxx 去掉 /api
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
}

webpack5:

主要是对 tree shaking的优化

module.exports = {
  // entry: './src/index.js',
  // output: {
  //   filename: '[name].js',
  //   path: resolve(__dirname, 'dist')
  // },
  /**
   * 以上配置是默认的,不用配置
   */
  // mode: 'development'
  mode: 'production'
}