webpack配置大全

338 阅读4分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战

魔法注释

// 浏览器空闲时,再加载
import(/* webpackPrefetch: true */ 'LoginModal');

hash、chunkhash、contenthash

  • hash:用hash命名文件
  • chunkhash:多入口,多出口时。只会修改内容变动的文件名称(如果文件内引入了其他文件,则其他文件名称也被修改)
  • contenthash:多入口,多出口时。只会修改内容变动的文件名称(其引入的文件若无变动,则不重命名)

mode生产环境和开发环境

mode: none | production | development

entry入口(三种类型的值)

// 1、设置一个入口
entry: "./src/index.js", 
// 2、把两个文件打包进一个文件
entry: ["./src/index.js", "./src/other.js"], 
// 3、多入口,同时需要多出口
entry: {
  index: "./src/index.js",
  other: "./src/other.js"
},
output: {
  path: path.resolve(__dirname, "./build"),
  filename: "[name]-[hash:6].js" // 利用hash去适应浏览器的缓存机制
} 

output

output: {
  path: path.resolve(__dirname, "./dist"),
  filename: "[name].js",
  publicPath: "./",   // 可以写任意地址,补url前缀(可以配置成cdn),然后把打包后的文件传到cdn上
}

loader

webpack只支持js和json文件,需要loader对其进行编译

module: {
  rules: [
    {
      test: /.css$/,
      // loader执行顺序是从后向前
      // css-loader 言简意赅 把css模块内容加到js模块
      // css in js方式
      // style-loader 从js中提取css的loader在html中创建
      include: path.resolve(__dirname, "./src"), // 设置编译范围,构建更快
      use: ["style-loader", "css-loader"]
    },
    {
      test: /.less$/,
      use: [
        "style-loader",
        {
          loader: "css-loader",
          options: {
            modules: true  // 开启css模块化
          }
        },
        {
          loader: "postcss-loader",  // 用来添加前缀,做css的浏览器兼容
        },
        "less-loader"   // 解析less
      ]  
    },
    {
      test: /.(png|jpe?g|gif)$/,
      use:{
        loader: "file-loader",
        options: {
          name: "[name]_[hash:6].[ext]",  // 打包后文件名————原名_6位hash.原后缀
          outputPath: "images/"           // 设置一个文件夹路径
        }
      }
    },
         //   推荐用url-loader   因为它支持limit
    {   //    url-loader有file-loader中所有的功能,完全可替代它,还增加了自己的功能
      test: /.(png|jpe?g|gif)$/,
      use:{
        loader: "url-loader",
        options: {
          name: "[name]_[hash:6].[ext]",  // 打包后文件名————原名_6位hash.原后缀
          outputPath: "images/",           // 设置一个文件夹路径
          limit: 2 * 1024,               // 小于2k的图片被转成Base64格式
        }
      }
    },
    {
      test: /.js$/,
      exclude: /node_modules/,
      use: {
        loader: "babel-loader",           // 解析es6
        options: {
          // 语法转换插件 preset-env
          presets: [
            ["@babel/preset-env", {
              targets: {            // 兼容浏览器版本
                edge: "17",
                firefox: "60",
                chrome: "67",
                safari: "11.1"
              },
              corejs: 2,
              useBuiltIns: "usage"
            }]
          ]
        }
      }
    }
  ]
}

添加postcss-loader(用来添加前缀,做css的浏览器兼容)

// 可以单独建立一个文件postcss.config.js
const autoprefixer = require("autoprefixer");
module.exports = {
  plugins: [
    autoprefixer({
      // 兼容最近两个版本
      // 全球浏览器的市场份额大约1%的浏览器
      overrideBrowserslist: ["last 2 versions", ">1%"]
    })
  ]
}  

babel可以单独文件配置babelrc(项目启动首先找自己的配置文件,找不到再去webpack配置中找)

{
  "presets": [
    [
      "@babel/preset-env",
      {
        targets: {            // 兼容浏览器版本
          edge: "17",
          firefox: "60",
          chrome: "67",
          safari: "11.1"
        },
        corejs: 2,
        useBuiltIns: "usage"
      }
    ],
    "@babel/preset-react"
  ]
}

Polyfill是把es6用到的东西(如:promise),挂在window上,就可以用了。会造成全局污染

optimization js摇树

只在mode是production才会生效,development的tree shaking是不生效的,因为webpack为了方便你的调试,从打包后的代码注释可分辨是否生效

optimization: {
  useExports: true, // 哪些导出的模块被使用了,再做打包
  splitChunks: {    // 代码分割
    // 所有配置如下图
  },
  concatenateModules: true   // 分析插件使用的地方,尽量插件和使用的地方放在一起
} 

plugins插件

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MinCssExtractPlugin = require("min-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const webpack = require("webpack");

plugin: [ 
  new CleanWebpackPlugin(),  // 清除之前打包生产的文件
  new MinCssExtractPlugin({  // 把css提取成独立的css文件,同时上面的style-loader要换成MinCssExtractPlugin.loader
    filename: "[name].css"
  }),
  new HtmlWebpackPlugin({
    // 常用配置
    title: "首页",   // 还需要在title标签中设置<title><%= htmlWebpackPlugin.options.title%></title>
    template: "./src/index.html",   
    filename: "index.html",
    // 压缩HTML文件
    minify: {
      removeComments: true,     // 移除HTML中的注释  
      collapseWhitespace: true, // 删除空白符和换行
      minisyCSS: true           // 压缩内联css
    }
  }),
  // 压缩css,利用cssnano
  new OptimizeCSSAssetsPlugin({
    cssProcessor: require("cssnano"),
    cssProcessorOptions: {
      discardComments: {
        removeAll: true
      }
    }
  }),
  // 需要注意:热更新要用style-loader,不能用MinCssExtractPlugin.loader
  // 只针对css的修改,js部分vue-loader这些框架会帮我们解决
  new webpack.HotModuleReplacementPlugin(),   // 帮助热更新的插件
], 

HtmlWebpackPlugin所有的配置

devtool

// 用于帮我们快速定位问题。开发环境默认开启,生成环境不开启。设置问none则关闭
// 浏览器的console能直接定位到问题文件代码位置
// 关闭后,浏览器报错会定位到打包后的文件。
devtool: "source-map" 
 

推荐:开发环境用 cheap-module-eval-source-map 打包更快。生产用 cheap-module-source-map 防止代码泄露

WebpackDevServer

devServer: {
  contentBase: path.resolve(__dirname, "./dist"),  // 指定打开的路径
  open: true,  // 自动打开浏览器
  hot: true,   // 自动更新浏览器
  hotOnly: true,   // 更新改动,但不刷新浏览器
  proxy: {      // 跨域代理
    "/api": {
      target: "http://localhost:9092"
    }
  },
  before(app, server) {            // 使用mock数据
    app.get("/api/mock.json", (req, res) => {
      res.json({
        hello: "express",
      })
    })
  },
  port: 8080,
}

resolve查找第三方依赖

resolve: {
  // 查找第三方依赖
  modules: [path.resolve(__dirname, "./node_modules")],
  alias: {
    // 减少查找过程
    // 起别名
    "@": path.resolve(__dirname, "./src/css"),
    react: "./node_module... ...",
    "react-dom": "./node_module... ...",
  },
  // 设置在加载模块时,可以不写后缀,按下面的顺序依次查找
  // 会消耗一些性能,推荐写代码直接写上后缀名
  ectensions: [".js", ".json", ".jsx"]
}

externals打包时排除一些包

// 场景:如果我们有自己的cdn,放在了index.html中。开发的时候为了方便又引入的插件
// 写法上保持一致
// 打包的时候就可以把包排除在外,避免引入两次(比较大的包用两次加载慢很多)
externals: {
  "jquery": "jQuery"
}

sideEffects处理摇树副作用

sideEffects: ["*.css", "@babel/ployfill"] 
//有些模块只需要被引入,不会调用(如:css、less)这样就不会被摇掉

多个配置文件

公共配置 webpack.config.base.js

const path = require("path");
module.exports = {
  entry: "./src/index.js",
  module: {
    rules: [
      {
        test: /.css$/,
        // loader执行顺序是从后向前
        // css-loader 言简意赅 把css模块内容加到js模块
        // css in js方式
        // style-loader 从js中提取css的loader在html中创建
        include: path.resolve(__dirname, "./src"), // 设置编译范围,构建更快
        use: ["style-loader", "css-loader"]
      },
      {
        test: /.less$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: true  // 开启css模块化
            }
          },
          {
            loader: "postcss-loader",  // 用来添加前缀,做css的浏览器兼容
          },
          "less-loader"   // 解析less
        ]  
      },
      {
        test: /.(png|jpe?g|gif)$/,
        use:{
          loader: "file-loader",
          options: {
            name: "[name]_[hash:6].[ext]",  // 打包后文件名————原名_6位hash.原后缀
            outputPath: "images/"           // 设置一个文件夹路径
          }
        }
      },
           //   推荐用url-loader   因为它支持limit
      {   //    url-loader有file-loader中所有的功能,完全可替代它,还增加了自己的功能
        test: /.(png|jpe?g|gif)$/,
        use:{
          loader: "url-loader",
          options: {
            name: "[name]_[hash:6].[ext]",  // 打包后文件名————原名_6位hash.原后缀
            outputPath: "images/",           // 设置一个文件夹路径
            limit: 2 * 1024,               // 小于2k的图片被转成Base64格式
          }
        }
      },
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",           // 解析es6
          options: {
            // 语法转换插件 preset-env
            presets: [
              ["@babel/preset-env", {
                targets: {            // 兼容浏览器版本
                  edge: "17",
                  firefox: "60",
                  chrome: "67",
                  safari: "11.1"
                },
                corejs: 2,
                useBuiltIns: "usage"
              }]
            ]
          }
        }
      }
    ]
  },
  resolve: {
    // 查找第三方依赖
    modules: [path.resolve(__dirname, "./node_modules")],
    alias: {
      // 减少查找过程
      // 起别名
      "@": path.resolve(__dirname, "./src/css"),
      react: "./node_module... ...",
      "react-dom": "./node_module... ...",
    },
    // 设置在加载模块时,可以不写后缀,按下面的顺序依次查找
    // 会消耗一些性能,推荐写代码直接写上后缀名
    extensions: [".js", ".json", ".jsx"]
  },
  plugins: [new CleanWebpackPlugin()]
} 

开发配置 webpack.config.pro.js

"prod": "webpack --config ./webpack.config.pro.js", 
const baseConfig = require("./webpack.config.base.js");
const { merge } = require("webpack-merge");
const MinCssExtractPlugin = require("min-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-plugin");

const proConfig = {
  mode: "production",
  output: {
    path: path.resolve(__dirname, "./build"),
    fileaname: "[name].js",
    publicPath: "http://... ..."
  },
  module: {
    rules: [
      {
        test: /.css$/,
        // loader执行顺序是从后向前
        // css-loader 言简意赅 把css模块内容加到js模块
        // css in js方式
        // style-loader 从js中提取css的loader在html中创建
        include: path.resolve(__dirname, "./src"), // 设置编译范围,构建更快
        use: ["style-loader", "css-loader"]
      },
      {
        test: /.less$/,
        use: [
          MinCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              modules: true  // 开启css模块化
            }
          },
          {
            loader: "postcss-loader",  // 用来添加前缀,做css的浏览器兼容
          },
          "less-loader"   // 解析less
        ]  
      },
      {
        test: /.(png|jpe?g|gif)$/,
        use:{
          loader: "file-loader",
          options: {
            name: "[name]_[hash:6].[ext]",  // 打包后文件名————原名_6位hash.原后缀
            outputPath: "images/"           // 设置一个文件夹路径
          }
        }
      },
           //   推荐用url-loader   因为它支持limit
      {   //    url-loader有file-loader中所有的功能,完全可替代它,还增加了自己的功能
        test: /.(png|jpe?g|gif)$/,
        use:{
          loader: "url-loader",
          options: {
            name: "[name]_[hash:6].[ext]",  // 打包后文件名————原名_6位hash.原后缀
            outputPath: "images/",           // 设置一个文件夹路径
            limit: 2 * 1024,               // 小于2k的图片被转成Base64格式
          }
        }
      },
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",           // 解析es6
          options: {
            // 语法转换插件 preset-env
            presets: [
              ["@babel/preset-env", {
                targets: {            // 兼容浏览器版本
                  edge: "17",
                  firefox: "60",
                  chrome: "67",
                  safari: "11.1"
                },
                corejs: 2,
                useBuiltIns: "usage"
              }]
            ]
          }
        }
      }
    ]
  },
  plugins: [
    new MinCssExtractPlugin({  // 把css提取成独立的css文件,同时上面的style-loader要换成MinCssExtractPlugin.loader
      filename: "[name].css"
    }),
    new HtmlWebpackPlugin({
      // 常用配置
      title: "首页",   // 还需要在title标签中设置<title><%= htmlWebpackPlugin.options.title%></title>
      template: "./src/index.html",   
      filename: "index.html",
      // 压缩HTML文件
      minify: {
        removeComments: true,     // 移除HTML中的注释  
        collapseWhitespace: true, // 删除空白符和换行
        minisyCSS: true           // 压缩内联css
      }
    }),
    // 压缩css,利用cssnano
    new OptimizeCSSAssetsPlugin ({
      cssProcessor: require("cssnano"),
      cssProcessorOptions: {
        discardComments: {
          removeAll: true
        }
      }
    }),
  ]
} 
module.exports = merge(baseConfig, proConfig); 

生产配置 webpack.config.dev.js

"dev": "webpack --config ./webpack.config.dev.js",   // 开发环境打包
"dev": "webpack-dev-serve --config ./webpack.config.dev.js"  // 开发环境启动项目
const baseConfig = require("./webpack.config.base.js");
const { merge } = require("webpack-merge");
const HtmlWebpackPlugin = require("html-webpacl-plugin");
const webpack = require("webpack");

const devConfig = {
  mode: "development",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filname: "[name].js",
  },
  devtool: "cheap-inline-source-map",
  module: {
    rules: [
      {
        test: /.css$/,
        // loader执行顺序是从后向前
        // css-loader 言简意赅 把css模块内容加到js模块
        // css in js方式
        // style-loader 从js中提取css的loader在html中创建
        include: path.resolve(__dirname, "./src"), // 设置编译范围,构建更快
        use: ["style-loader", "css-loader"]
      },
      {
        test: /.less$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: true  // 开启css模块化
            }
          },
          {
            loader: "postcss-loader",  // 用来添加前缀,做css的浏览器兼容
          },
          "less-loader"   // 解析less
        ]  
      },
      {
        test: /.(png|jpe?g|gif)$/,
        use:{
          loader: "file-loader",
          options: {
            name: "[name]_[hash:6].[ext]",  // 打包后文件名————原名_6位hash.原后缀
            outputPath: "images/"           // 设置一个文件夹路径
          }
        }
      },
           //   推荐用url-loader   因为它支持limit
      {   //    url-loader有file-loader中所有的功能,完全可替代它,还增加了自己的功能
        test: /.(png|jpe?g|gif)$/,
        use:{
          loader: "url-loader",
          options: {
            name: "[name]_[hash:6].[ext]",  // 打包后文件名————原名_6位hash.原后缀
            outputPath: "images/",           // 设置一个文件夹路径
            limit: 2 * 1024,               // 小于2k的图片被转成Base64格式
          }
        }
      },
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",           // 解析es6
          options: {
            // 语法转换插件 preset-env
            presets: [
              ["@babel/preset-env", {
                targets: {            // 兼容浏览器版本
                  edge: "17",
                  firefox: "60",
                  chrome: "67",
                  safari: "11.1"
                },
                corejs: 2,
                useBuiltIns: "usage"
              }]
            ]
          }
        }
      }
    ]
  },
  devServer: {
    contentBase: path.resolve(__dirname, "./dist"),  // 指定打开的路径
    open: true,  // 自动打开浏览器
    hot: true,   // 自动更新浏览器
    hotOnly: true,   // 更新改动,但不刷新浏览器
    proxy: {      // 跨域代理
      "/api": {
        target: "http://localhost:9092"
      }
    },
    before(app, server) {            // 使用mock数据
      app.get("/api/mock.json", (req, res) => {
        res.json({
          hello: "express",
        })
      })
    },
    port: 8080,
  },
  plugins: [
    new HtmlWebpackPligin({
      // 常用配置
      title: "首页",   // 还需要在title标签中设置<title><%= htmlWebpackPlugin.options.title%></title>
      template: "./src/index.html",   
      filename: "index.html",
      // 压缩HTML文件
      minify: {
        removeComments: true,     // 移除HTML中的注释  
        collapseWhitespace: true, // 删除空白符和换行
        minisyCSS: true           // 压缩内联css
      }
    }),
    new webpack.HotModuleReplacementPlugin(),   // 帮助热更新的插件
  ]
}
module.exports = merge(baseConfig, devConfig);

webpack返回一个函数时

"test:dev": "webpack --env.production --config webpack.config.test.js"
"test:build": "webpack-dev-server --config webpack.config.test.js" 
// 用cross-env方式
"test:build": "cross-env NODE_ENV=test321312312312 --config webpack.config.test.js"

webpack.config.test.js

const baseConfig = require("./webpack.config.base.js");
const devConfig = require("./webpack.config.dev.js");
const proConfig = require("./webpack.config.pro.js");
const merge = require("webpack-merge");

console.log(provess.env.NODE_ENV)  // 用cross-env方式

module.exports = (env) => {
  if(env && env.production) {
    return merger(baseConfig, proConfig);
  } else {
    return merger(baseConfig, devConfig);
  }
} 

plugin

loader和plugin性能

DllPlugin

HardSourceWebpack

优化项目过程

webpack vs vite vs Rollup