前端工程化之webpack5常用案例Loader和plugins集合

1,416 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

webpack5

安装文件

npm install webpack webpack-cli --save-dev

配置文件组成

//webpack.config.js
module.exports = {
  //打包的入口文件
  //entry: {},
  entry: "",
  //打包的输出文件
  // output: "",
  output: {},
  //环境配置
  mode: "",
  //loader配置
  module: {},
  //插件
  plugins: [],
  //开发服务器
  DevServer: {},
  //监听文件打包变化
  watch: true,
  //监听文件打包配置选项
  watchOptions: {},
  //解析模块
  resolve: {},
  // 配置开发工具模式
  devtool: "",
  //第三方模块不需要webpack打包
  externals: {},
  //优化性能
  Optimization: {},
};

  • entry

    • 字符串

      module.exports = {
        entry: "打包入口文件名",
      };
      
    • 对象

      module.exports = {
        entry: {
          应用APP1: "打包入口文件名1",
          应用APP2: "打包入口文件名2",
        },
      };
      
  • output

    • 单入口

      module.exports = {
        output: {
          filename: "输出文件名",
          path: "输出指定的路径",
        },
      };
      
    • 多入口

      module.exports = {
        entry: {
          应用APP1: "打包入口文件名1",
          应用APP2: "打包入口文件名2",
        },
        output: {
          filename: "[name].js",
          path: __dirname + "/dist",
        },
      };
      
  • module

    module.exports = {
      module: {
        rules: [
          {
            test: "匹配文件名正则表达式规则",
            use: ["loader1", "loader2"],
          },
          {
            test: /\.js$/,
            //排除所有符合条件的模块
            exclude: 排除目录,
            // 引入符合以下任何条件的模块
            include: 检查指定目录,
            // 优先执行
            enforce: "pre",
            // 延后执行
            // enforce: 'post',
            // 单个loader用loader
            use: "loader1",
            options: {},
          },
        ],
      },
    };
    
    
  • plugins

    module.exports = {
      plugins: [
        new 插件1(插件构造参数), 
        new 插件2(插件构造参数)
      ],
    };
    

配置文件案例

//webpack.config.js
const path = require("path");
module.exports = {
  //监听文件打包变化
  watch: true,
  //监听文件打包配置选项
  watchOptions: {
    //防抖,和函数防抖一样, 改变过程中不重新打包, 只有改变完成指定时间后才打包
    aggregateTimeout: 300,
    // 每隔多少时间检查一次变动
    poll: 1000,
    // 排除一些巨大的文件夹, 不需要监控的文件夹
    ignored: /node_modules/,
  },
  /**
   * 配置开发工具模式
   */
  devtool: "source-map",
  /**
   * 指定打包的模式
   * development: 不会对打包的JS代码进行压缩
   * production: 会对打包的JS代码进行压缩
   */
  mode: "development",
  /**
   * 指定需要打包的文件
   */
  entry: "./src/index.js",
  /**
   * 指定打包之后的文件输出的路径和输出的文件名称
   */
  output: {
    /**
     *  指定打包之后的JS文件的名称
     */
    filename: "bundle.js",
    /**
     * 指定打包之后的文件存储到什么地方
     */
    path: path.join(__dirname, "bundle"),
    // 自动将上次打包目录资源清空
    clean: true,
  },
  resolve: {
    //别名
    // alias: {
    //   // 创建 import 或 require 的别名,来确保模块引入变得更简单
    //   bootstrapcss: "bootstrap/dist/css/bootstrap.css",
    // },
    // 指定模块入口的查找顺序
    // mainFields: ['style', 'main']
    // 指定导入模块查找顺序
    extensions: [".css", ".js", ".joson"],
  },
  /*
    第三方模块不需要webpack打包
    * */
  externals: {
    /*
        通过import导入jquery模块时, 
        它不是通过node_modules中导入jquery,
        而是全局引入jquery导入
        * */
    jquery: "jQuery",
    lodash: "_",
  },
  /**
   * 处理webpack不能够识别的文件
   */
  module: {
    rules: [],
  },
  /**
   * webpack插件
   */
  plugins: [],
};

模式开发

  • 安装命令

    npm install --save-dev webpack-merge
    
  • 配置文件详情

    const { merge } = require("webpack-merge");
    const common = require("./webpack.common.js");
    
    module.exports = merge(common, {
      mode: "mode模式",
    });
    

Loader

介绍

webapck的本质是一个模块打包工具, 所以webpack默认只能处理JS文件,不能处理其他文件,

因为其他文件中没有模块的概念, 但是在企业开发中我们除了需要对JS进行打包以外,

还有可能需要对图片CSS等进行打包, 所以为了能够让webpack能够对其它的文件类型进行打包,

在打包之前就必须将其它类型文件转换为webpack能够识别处理的模块,

用于将其它类型文件转换为webpack能够识别处理模块的工具我们就称之为loader

特点

  • 单一原则, 一个loader只做一件事情

  • 多个loader会按照从右至左, 从下至上的顺序执行

    • 从右至左

      • ["style-loader", "css-loader"];
        
      • 先执行css-loader解析css文件关系拿到所有内容
      • 再执行style-loader将内容插入到HTML的HEAD代码中
    • 从下至上

      • [
          {
            loader: "style-loader",
          },
          {
            loader: "css-loader",
          },
        ];
        
      • 先执行css-loader解析css文件关系拿到所有内容
      • 再执行style-loader将内容插入到HTML的HEAD代码中

样式

css-loader

  • 安装命令

    npm install css-loader  --save-dev
    npm install style-loader --save-dev
    
  • 配置文件详情

    module.exports = {
      module: {
        rules: [
          //打包CSS规则
          {
            test: /.css$/i,
            /**
             * css-loader:   解析css文件中的@import依赖关系
             * style-loader: 将webpack处理之后的内容插入到html的head代码中
             * */
            // use: ["style-loader", "css-loader"],
            use: [
              {
                loader: "style-loader",
              },
              {
                loader: "css-loader",
                options: {
                  // 开启CSS模块化
                  modules: true,
                },
              },
            ],
          },
        ],
      },
    };
    

less-loader

  • 安装命令

    npm install css-loader  --save-dev
    npm install style-loader --save-dev
    npm install less-loader --save-dev
    
  • 配置文件详情

    module.exports = {
      module: {
        rules: [
          // 打包less规则
          {
            test: /.less$/i,
            use: [
              {
                loader: "style-loader",
              },
              {
                loader: "css-loader",
              },
              {
                loader: "less-loader",
              },
            ],
          },
        ],
      },
    };
    
    

scss-loader

  • 安装命令

    npm install css-loader  --save-dev
    npm install style-loader --save-dev
    npm install node-sass   --save-dev 
    npm install sass-loader --save-dev
    
  • 配置文件详情

    module.exports = {
      module: {
        rules: [
          // 打包SCSS规则
          {
            test: /.scss$/,
            use: [
              // 将 JS 字符串生成为 style 节点
              {
                loader: "style-loader",
              },
              //将 CSS 转化成 CommonJS 模块
              {
                loader: "css-loader",
              },
              // 将 Sass 编译成 CSS
              {
                loader: "sass-loader",
              },
            ],
          },
        ],
      },
    };
    

PostCSS

自动补全浏览器前缀及兼容游览器

  • 安装命令

    npm install css-loader  --save-dev
    npm install style-loader --save-dev
    npm install node-sass   --save-dev 
    npm install sass-loader --save-dev
    npm install less-loader --save-dev
    npm i -D postcss-loader
    npm i -D autoprefixer
    npm install postcss-pxtorem -D
    
  • 配置文件详情

    module.exports = {
      module: {
        rules: [
          // 打包postcss规则
          {
            test: /.s[ac]ss$/i,
            use: [
              // 将 JS 字符串生成为 style 节点
              {
                loader: "style-loader",
              },
              // 将 CSS 转化成 CommonJS 模块
              {
                loader: "css-loader",
                options: {
                  modules: true,
                },
              },
              // 将 Sass 编译成 CSS
              {
                loader: "sass-loader",
              },
              // 将 postcss 编译成 CSS
              {
                loader: "postcss-loader",
                options: {
                  postcssOptions: {
                    plugins: [
                      [
                        "autoprefixer",
                        {
                          // 选项
                          overrideBrowserslist: [
                            "ie >= 8", // 兼容IE7以上浏览器
                            "Firefox >= 3.5", // 兼容火狐版本号大于3.5浏览器
                            "chrome  >= 35", // 兼容谷歌版本号大于35浏览器,
                            "opera >= 11.5", // 兼容欧朋版本号大于11.5浏览器,
                          ],
                        },
                      ],
                      [
                        "postcss-pxtorem",
                        {
                          // 根元素字体大小
                          rootValue: 100,
                          // 可以从px更改到rem的属性
                          propList: ["*"],
                          // propList: ["height"]
                          unitPrecision: 5,
                          // 将哪些html元素排除在外,我这里添加了一个vant的
                          selectorBlackList: ["vant-"],
                          replace: true,
                          mediaQuery: false,
                          minPixelValue: 0,
                          exclude: /node_modules/i,
                        },
                      ],
                    ],
                  },
                },
              },
            ],
          },
        ],
      },
    };
    

语法转换

bable-loader

  • 安装命令

    npm install --save-dev babel-loader @babel/core
    npm install @babel/preset-env --save-dev
    
  • 配置文件详情

    module.exports = {
      module: {
        rules: [
          //打包JS规则
          {
            test: /.m?js$/,
            // 告诉webpack不处理哪一个文件夹
            exclude: /node_modules/,
            use: {
              loader: "babel-loader",
              options: {
                presets: ["@babel/preset-env"],
              },
            },
          },
        ],
      },
    };
    

图片文件

file-loader

  • 安装命令

    npm install --save-dev file-loader
    
  • 配置文件详情

    module.exports = {
      module: {
        rules: [
          // 打包字体图标规则
          {
            test: /.(eot|json|svg|ttf|woff|woff2)$/,
            use: [
              {
                loader: "file-loader",
                options: {
                  // 指定打包后文件名称
                  name: "[name].[ext]",
                  // 指定打包后文件存放目录
                  outputPath: "font/",
                },
              },
            ],
          },
          //打包图片规则
          {
            test: /.(png|jpe?g|gif)$/i,
            use: [
              {
                loader: "file-loader",
                options: {
                  // 指定打包后文件名称
                  name: "[path][name].[ext]",
                  // 指定打包后文件存放目录
                  outputPath: "images",
                  // 指定托管服务器地址(统一替换图片地址)
                  publicPath: "assets",
                },
              },
            ],
          },
        ],
      },
    };
    
    

url-loader

  • 安装命令

    npm install --save-dev url-loader
    
  • 配置文件详情

    module.exports = {
      module: {
        rules: [
          //打包图片规则
          {
            test: /.(png|jpe?g|gif)$/i,
            use: [
              {
                loader: "url-loader",
                options: {
                  /**
                   * limit: 指定图片限制的大小
                   * 如果被打包的图片超过了限制的大小, 就会将图片保存为一个文件
                   * 如果被打包的图片没有超过限制的大小, 就会将图片转换成base64的字符串
                   * 注意点:
                   * 对于比较小的图片, 我们将图片转换成base64的字符串之后, 可以提升网页的性能(因为减少了请求的次数)
                   * 对于比较大的图片, 哪怕我们将图片转换成了base64的字符串之后, 也不会提升网页的性能, 还有可能降低网页的性能
                   * (因为图片如果比较大, 那么转换之后的字符串也会比较多, 那么网页的体积就会表达, 那么访问的速度就会变慢)
                   * */
                  limit: 1024 * 100,
                  // 指定打包后文件名称
                  name: "[path][name].[ext]",
                  // 指定打包后文件存放目录
                  outputPath: "images",
                },
              },
            ],
          },
        ],
      },
    };
    

asset

module.exports = {
  module: {
    rules: [
      {
        //处理图片的
        test: /.(png|jpe?g|gif|webp)$/,
        type: "asset",
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
          },
        },
        generator: {
          // 将图片文件输出到 static/imgs 目录中
          // 将图片文件命名 [hash:8][ext][query]
          // [hash:8]: hash值取8位
          // [ext]: 使用之前的文件扩展名
          // [query]: 添加之前的query参数
          filename: "static/imgs/[hash:8][ext][query]",
        },
      },
      {
        //处理字体
        test: /.(ttf|woff2?)$/,
        type: "asset/resource",
        generator: {
          filename: "static/media/[hash:8][ext][query]",
        },
      },
      {
        //处理视频和音频
        test: /.(ttf|woff2?|map4|map3|avi)$/,
        type: "asset/resource",
        generator: {
          filename: "static/media/[hash:8][ext][query]",
        },
      },
    ],
  },
};

image-webpack-loader

  • 安装命令

    npm install image-webpack-loader --save-dev
    
  • 配置文件详情

    module.exports = {
      module: {
        rules: [
          //打包图片规则
          {
            test: /.(png|jpe?g|gif)$/i,
            use: [
              {
                loader: "file-loader",
                options: {
                  // 指定打包后文件名称
                  name: "[path][name].[ext]",
                  // 指定打包后文件存放目录
                  outputPath: "images",
                  // 指定托管服务器地址(统一替换图片地址)
                  // publicPath: "assets",
                },
              },
              {
                loader: "image-webpack-loader",
                options: {
                  mozjpeg: {
                    progressive: true,
                  },
                  // optipng.enabled: false will disable optipng
                  optipng: {
                    enabled: false,
                  },
                  pngquant: {
                    quality: [0.65, 0.9],
                    speed: 4,
                  },
                  gifsicle: {
                    interlaced: false,
                  },
                  // the webp option will enable WEBP
                  webp: {
                    quality: 75,
                  },
                },
              },
            ],
          },
        ],
      },
    };
    

plugins

html插件

  • 安装文件

    npm install --save-dev html-webpack-plugin
    
  • 配置文件详情

    //打包结束之后自动创建一个index.html, 并将打包好的JS自动引入到这个文件中
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    module.exports = {
      plugins: [
        new HtmlWebpackPlugin({
          //指定打包的模板, 如果不指定会自动生成一个空的
          template: "./src/index.html",
          minify: {
            // htmlPlugin打包之后的html文件需要压缩
            collapseWhitespace: true,
          },
        }),
      ],
    };
    
    

压缩css文件

  • 安装文件

    npm install --save-dev mini-css-extract-plugin
    npm install --save-dev css-minimizer-webpack-plugin
    
  • 配置文件详情

    //创建独立一个css文件
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    //压缩css文件
    const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
    module.exports = {
      optimization: {
        //开发环境进行压缩css
        minimize: true,
        minimizer: [new CssMinimizerPlugin()],
      },
      plugins: [
        new MiniCssExtractPlugin({
          // 类似于 webpackOptions.output 中的选项
          // 所有选项都是可选的
          filename: "[name].css",
          chunkFilename: "[id].css",
        }),
      ],
    };
    
    

按需打包css文件

  • 安装文件

    npm install --save-dev purgecss-webpack-plugin
    
  • 配置文件详情

    const path = require("path");
    const glob = require("glob");
    // 按需打包css
    const PurgecssPlugin = require("purgecss-webpack-plugin");
    
    module.exports = {
      plugins: [
        new PurgecssPlugin({
          paths: glob.sync(`${path.join(__dirname, "src")}/**/*`, { nodir: true }),
        }),
      ],
    };
    
    

热更新

  • css配置文件

    const Webpack = require("webpack");
    module.exports = {
      devServer: {
        // 开启热更新, 只要开启了热更新就不会自动刷新网页了
        hot: true,
        // 哪怕不支持热更新也不要刷新网页
        hotOnly: true,
      },
      plugins: [new Webpack.HotModuleReplacementPlugin()],
    };
    
  • js配置

    // 判断当前有没有开启热更新
    if (module.hot) {
      // 告诉热更新需要监听哪一个JS模块的变化
      module.hot.accept("js文件路径", function () {
        console.log("js更新了");
      });
    }
    

清空打包文件

  • 安装文件

    npm install --save-dev clean-webpack-plugin
    
  • 配置文件详情

    //应用场景每次打包前将指定(dist/build)目录清空, 然后再存放新打包的内容, 避免新老混淆问题
    const { CleanWebpackPlugin } = require("clean-webpack-plugin");
    module.exports = {
      plugins: [
        //清理每次打包之前文件
        new CleanWebpackPlugin(),
      ],
    };
    
    

拷贝文件

  • 安装文件

    npm install --save-dev copy-webpack-plugin
    
  • 配置文件详情

    //文档内容是固定不变的, 我们只需要将对应的文件拷贝到打包目录中即可
    const CopyPlugin = require("copy-webpack-plugin");
    module.exports = {
      plugins: [
        //拷贝
        new CopyPlugin({
          patterns: [
            {
              from: "./doc",
              to: "doc",
            },
          ],
        }),
      ],
    };
    

EsLint

  • 安装文件

    npm install eslint-webpack-plugin --save-dev
    npm install eslint --save-dev
    npm install eslint-plugin-promise eslint-plugin-node eslint-plugin-import eslint-plugin-standard eslint-config-standard --save-dev
    
  • 配置文件

    const ESLintPlugin = require('eslint-webpack-plugin');
    
    module.exports = {
      // ...
      plugins: [new ESLintPlugin(options)],
      // ...
    };
    

代码分割

"use strict";
module.exports = {
  /*
    告诉webpack需要对代码进行分割
    * */
  optimization: {
    splitChunks: {
      chunks: "all", // 对那些代码进行分割 async(只分割异步加载模块)、all(所有导入模块)
      minSize: 20000, // 表示被分割的代码体积至少有多大才分割(单位是字节)
      minChunks: 1, // 表示至少被引用多少次数才分割,默认为1
      maxAsyncRequests: 30, // 异步加载并发最大请求数(保持默认即可)
      maxInitialRequests: 30, // 最大的初始请求数(保持默认即可)
      automaticNameDelimiter: "+", // 指定被分割出来的文件名称的连接符
      name: false, // 拆分出来块的名字使用0/1/2...(false) 还是指定名称(true)
      /*
      cacheGroups: 缓存组
      缓存组的作用: 将当前文件中导入的所有模块都缓存起来统一处理
            * */
      cacheGroups: {
        /*
        vendors: 专门用于处理从node_modules中导入的模块
        会将所有从node_modules中导入的模块写入到一个文件中去
                * */
        vendors: {
          test: /[\/]node_modules[\/]/,
          priority: -10, // 抽取公共代码的优先级,数字越大,优先级越高
          reuseExistingChunk: true, // 是否复用分割的代码
        },
        /*
          default: 专门用于处理从任意位置导入的模块
           会将所有从任意位置导入的模块写入到一个文件中去
                * */
        /*
           注意点: 如果我们导入的模块同时满足了两个条件, 那么就会按照优先级来写入
           例如: 我们导入了jQuery, jQuery存放在了node_modules目录中
           所以满足vendors的条件, 也满足default条件, 但是vendors的条件的优先级高于default的优先级
           所以就只会执行vendors规则, 只会写入到vendors对应的文件中去
                * */
        default: {
          minChunks: 1, // 表示至少被引用多少次数才分割,默认为1
          priority: -20,
          reuseExistingChunk: true, // 是否复用分割的代码
        },
      },
    },
  },
};

全局导入

"use strict";
const Webpack = require("webpack");

module.exports = {
  plugins: [
    // 全局导入
    new Webpack.ProvidePlugin({
      $: "jquery",
    }),
  ],
};

忽略第三方包部分目录导入

'use strict';
const Webpack = require('webpack');
module.exports = {
    plugins: [
        /*
        在打包moment该库的时候, 将整个locale目录都忽略掉
        * */
        new Webpack.IgnorePlugin(/^./locale$/, /moment$/)
    ]
};

动态dll库

新建一个配置文件, 专门用于打包不会变化的第三方库

"use strict";
const path = require("path");
const Webpack = require("webpack");
module.exports = {
  entry: {
    jquery: ["jquery"],
    lodash: ["lodash"],
  },
  /**
   * 指定打包之后的文件输出的路径和输出的文件名称
   */
  output: {
    /**
     *  指定打包之后的JS文件的名称
     */
    filename: "[name].dll.js",
    /**
     * 指定打包之后的文件存储到什么地方
     */
    path: path.join(__dirname, "dll"),
    library: "[name]",
  },
  /**
   * 指定打包的模式
   * development: 不会对打包的JS代码进行压缩
   * production: 会对打包的JS代码进行压缩
   */
  mode: "development",
  /**
   * webpack插件
   */
  plugins: [
    /*
        DllPlugin作用:
        在打包第三方库的时候生成一个清单文件
        * */
    new Webpack.DllPlugin({
      name: "[name]", // 注意这个名称必须和library名称一致
      path: path.resolve(__dirname, "dll/[name].manifest.json"),
    }),
  ],
};

安装插件把打包好的库插入到html中

npm i add-asset-html-webpack-plugin -D

在专门打包第三方的配置文件中添加生成清单配置

"use strict";
const path = require("path");
const fs = require("fs");
const Webpack = require("webpack");
const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin");
const plugins = [];
const dllPath = path.resolve(__dirname, "dll");
const files = fs.readdirSync(dllPath);
files.forEach(function (file) {
  if (file.endsWith(".js")) {
    plugins.push(
      new AddAssetHtmlPlugin({
        filepath: path.resolve(__dirname, "dll", file),
      })
    );
  } else if (file.endsWith(".json")) {
    plugins.push(
      new Webpack.DllReferencePlugin({
        manifest: path.resolve(__dirname, "dll", file),
      })
    );
  }
});
module.exports = {
  plugins: plugins,
};

加载模块

  • 异步加载模块

    import("加载模块").then(() => {
      //代码....
    });
    
  • 懒加载模块

    //webpackPreload 是否懒加载
    //webpackChunkName 定义懒加载名称
    import(
      /* webpackPreload: true */ /* webpackChunkName: "jquery" */ "加载模块"
    ).then(() => {
      //代码...
    });