Skip to content
On this page

Webpack 加载器(Loaders)

加载器(Loaders)是 Webpack 的核心特性之一,它允许 Webpack 处理其他类型的文件(不仅仅是 JavaScript),并将它们转换为有效的模块。

加载器基础概念

加载器本质上是一个函数,它接受源文件作为参数,并返回转换后的结果。加载器可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。

加载器配置结构

javascript
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,        // 用于匹配文件的正则表达式
        use: [                // 使用的加载器(从右到左执行)
          'style-loader',     // 最后执行
          'css-loader'        // 首先执行
        ]
      }
    ]
  }
};

常用加载器

1. CSS 相关加载器

javascript
module.exports = {
  module: {
    rules: [
      // 基础 CSS 加载器
      {
        test: /\.css$/,
        use: [
          'style-loader',    // 将 CSS 注入到 DOM 中
          'css-loader'       // 解析 CSS 文件中的 @import 和 url()
        ]
      },
      
      // 带有 PostCSS 的 CSS 加载器
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,      // 启用 CSS 模块
              importLoaders: 1    // 在 css-loader 前应用的 loader 数量
            }
          },
          'postcss-loader'       // PostCSS 处理
        ]
      },
      
      // SCSS/SASS 加载器
      {
        test: /\.(scss|sass)$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'          // 将 Sass 编译为 CSS
        ]
      },
      
      // LESS 加载器
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'less-loader'          // 将 Less 编译为 CSS
        ]
      }
    ]
  }
};

2. JavaScript 相关加载器

javascript
module.exports = {
  module: {
    rules: [
      // Babel 加载器
      {
        test: /\.js$/,
        exclude: /node_modules/,    // 排除 node_modules 目录
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', {
                targets: {
                  browsers: ['last 2 versions']
                }
              }]
            ],
            plugins: [
              '@babel/plugin-proposal-class-properties',
              '@babel/plugin-proposal-object-rest-spread'
            ]
          }
        }
      },
      
      // TypeScript 加载器
      {
        test: /\.tsx?$/,
        use: 'ts-loader',           // 或者使用 babel-loader
        exclude: /node_modules/
      },
      
      // CoffeeScript 加载器
      {
        test: /\.coffee$/,
        use: 'coffee-loader'
      }
    ]
  }
};

3. 资源加载器

javascript
module.exports = {
  module: {
    rules: [
      // 图片资源加载器 (Webpack 5)
      {
        test: /\.(png|jpe?g|gif|svg|webp)$/i,
        type: 'asset/resource',     // 输出单独文件
        generator: {
          filename: 'images/[name].[hash][ext]'
        }
      },
      
      // 小图片转 Base64 (Webpack 5)
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        type: 'asset/inline',       // 转换为 data URI
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024       // 8kb 以下的文件转为 base64
          }
        }
      },
      
      // 字体加载器
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'fonts/[name].[hash][ext]'
        }
      },
      
      // 音频视频加载器
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'media/[name].[hash][ext]'
        }
      }
    ]
  }
};

4. 文件加载器(Webpack 4 兼容)

javascript
module.exports = {
  module: {
    rules: [
      // 文件加载器 - 保留原始文件名
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[hash].[ext]',
              outputPath: 'images/'
            }
          }
        ]
      },
      
      // URL 加载器 - 小文件转 Base64
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,          // 8kb 以下转为 base64
              name: '[name].[hash].[ext]',
              outputPath: 'images/'
            }
          }
        ]
      }
    ]
  }
};

高级加载器配置

1. 自定义加载器

javascript
// 自定义加载器示例
function myLoader(source) {
  // source 是模块的原始源代码
  const newSource = doSomeTransformation(source);
  return `export default ${JSON.stringify(newSource)}`;
}

// 异步加载器
function asyncLoader(source) {
  const callback = this.async();
  
  someAsyncOperation(source, (err, result) => {
    if (err) return callback(err);
    callback(null, result);
  });
}

module.exports = {
  module: {
    rules: [
      {
        test: /\.custom$/,
        use: path.resolve(__dirname, 'my-loader.js')
      }
    ]
  }
};

2. 加载器链配置

javascript
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'thread-loader',  // 在 worker 池中运行
            options: {
              workers: 2
            }
          },
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env']
            }
          }
        ]
      }
    ]
  }
};

3. 条件加载器

javascript
module.exports = {
  module: {
    rules: [
      // 开发环境下使用 CSS 模块
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: process.env.NODE_ENV === 'development'
            }
          }
        ]
      },
      
      // 生产环境下提取 CSS
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,  // 生产环境下使用 MiniCssExtractPlugin
          'css-loader'
        ]
      }
    ]
  }
};

常见加载器详解

1. CSS 加载器

javascript
// 完整的 CSS 配置
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          process.env.NODE_ENV === 'production'
            ? MiniCssExtractPlugin.loader  // 生产环境提取 CSS
            : 'style-loader',             // 开发环境注入 CSS
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[local]_[hash:base64:5]'  // CSS 模块类名
              },
              sourceMap: true,            // 启用源映射
              importLoaders: 2            // import 的资源经过后续 2 个 loader 处理
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  ['autoprefixer', {}],
                  ['postcss-preset-env', {}]
                ]
              }
            }
          }
        ]
      }
    ]
  }
};

2. 图片优化加载器

javascript
module.exports = {
  module: {
    rules: [
      // 响应式图片生成
      {
        test: /\.(jpe?g|png|webp)$/i,
        use: [
          {
            loader: 'responsive-loader',
            options: {
              adapter: require('responsive-loader/sharp'),
              sizes: [300, 600, 1000],
              placeholder: true,
              placeholderSize: 50
            }
          }
        ]
      },
      
      // SVG 优化
      {
        test: /\.svg$/,
        use: [
          {
            loader: 'svg-inline-loader',  // 内联 SVG
            options: {
              removeTags: true,
              removingTags: ['desc', 'defs', 'title']
            }
          }
        ]
      }
    ]
  }
};

3. 模板加载器

javascript
module.exports = {
  module: {
    rules: [
      // Handlebars 模板
      {
        test: /\.hbs$/,
        use: 'handlebars-loader'
      },
      
      // Pug 模板
      {
        test: /\.pug$/,
        use: 'pug-loader'
      },
      
      // HTML 模板
      {
        test: /\.html$/,
        use: [
          {
            loader: 'html-loader',
            options: {
              minimize: true,
              sources: {
                list: [
                  {
                    tag: 'img',
                    attribute: 'src',
                    type: 'src'
                  }
                ]
              }
            }
          }
        ]
      }
    ]
  }
};

加载器性能优化

1. 排除不必要的目录

javascript
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: path.resolve(__dirname, 'src'),    // 只处理 src 目录
        exclude: /node_modules/                    // 排除 node_modules
      }
    ]
  }
};

2. 使用缓存

javascript
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,    // 启用缓存
              cacheCompression: false  // 禁用缓存压缩
            }
          }
        ]
      }
    ]
  }
};

3. 并行处理

javascript
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'thread-loader',
            options: {
              workers: require('os').cpus().length - 1
            }
          },
          'babel-loader'
        ]
      }
    ]
  },
  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: true    // 并行压缩
      })
    ]
  }
};

小结

加载器是 Webpack 的重要组成部分,它们扩展了 Webpack 处理不同文件类型的能力。通过合理配置加载器,我们可以处理 CSS、图片、字体、模板等各种资源,并将它们转换为 JavaScript 模块。理解加载器的工作原理和配置方法,可以帮助我们更好地优化构建流程。