Webpack Loader 深度解析

Loader 是 Webpack 生态中最核心的概念之一,理解 Loader 的原理和使用,是掌握前端工程化的关键一步。

目录


1. Loader 是什么

1.1 定义

Loader 是文件转换器,它的作用是将非 JavaScript 文件转换成 Webpack 能够理解和处理的模块。

1.2 为什么需要 Loader

Webpack 本身只认识 JavaScript 文件。但在前端开发中,我们需要处理各种类型的文件:

文件类型 需要 Loader 转换
.vue vue-loader
.scss/.sass sass-loader
.ts ts-loader
.png/.jpg file-loader / url-loader
.svg svg-loader
.md markdown-loader

1.3 Loader 的作用

┌─────────────────────────────────────────────────────┐
│              源文件                              │
│  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐  │
│  │.vue   │ │ .scss  │ │ .ts   │ │ .png  │  │
│  └────────┘ └────────┘ └────────┘ └────────┘  │
└─────────────────────────────────────────────────────┘
                    ↓ Loader 转换
┌─────────────────────────────────────────────────────┐
│           Webpack 可处理的模块                   │
│  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐  │
│  │  JS    │ │  CSS   │ │  JS    │ │  JS   │  │
│  └────────┘ └────────┘ └────────┘ └────────┘  │
└─────────────────────────────────────────────────────┘

2. Loader 的核心原理

2.1 执行顺序

Loader 的执行顺序是从右到左,从下到上

{ test: /\.scss$/, use: [ 'style-loader', // 3. 最后执行 'css-loader', // 2. 中间执行 'sass-loader' // 1. 最先执行 ] }

执行流程:

main.scss
    ↓
sass-loader  (SCSS → CSS)
    ↓
main.css
    ↓
css-loader  (处理 @import、url())
    ↓
处理后的 CSS
    ↓
style-loader  (插入到 style 标签)
    ↓
注入到页面

2.2 Loader 函数签名

每个 Loader 本质上是一个函数:

/** * @param {string|Buffer} source - 文件内容 * @returns {string|Buffer} - 转换后的内容 */ module.exports = function(source) { // 转换逻辑 return transformedSource }

2.3 更完整的 Loader 结构

module.exports = function(source, sourceMap, meta) { // source: 文件内容 // sourceMap: 源映射 // meta: 元数据

// 可以使用 this 访问上下文
const callback = this.async()
const options = this.getOptions()

// 异步处理
someAsyncProcess(source, options, (error, result) => {
callback(error, result, sourceMap, meta)
})
}


3. Loader 的基础用法

3.1 基本配置

module.exports = { module: { rules: [ { test: /\.js$/, // 匹配文件 use: 'babel-loader' // 使用的 Loader(字符串形式) } ] } }

3.2 数组形式(链式调用)

{ test: /\.scss$/, use: [ 'style-loader', 'css-loader', 'sass-loader' ] }

3.3 对象形式(传递选项)

{ test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'], cacheDirectory: true } } }

3.4 混合形式

{ test: /\.scss$/, use: [ // 字符串简写 'style-loader', // 对象带选项 { loader: 'css-loader', options: { modules: true, importLoaders: 1 } }, // 字符串简写 'sass-loader' ] }

3.5 条件匹配

{ test: /\.js$/, include: path.resolve(__dirname, 'src'), // 只处理 src 目录 exclude: /node_modules/ // 排除 node_modules use: 'babel-loader' }

4. 常用 Loader 详解

4.1 babel-loader - ES6+ 转换

作用: 将 ES6+ 代码转换成 ES5,兼容旧浏览器。

安装:

npm install -D babel-loader @babel/core @babel/preset-env

配置:

{ test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: [ ['@babel/preset-env', { targets: { browsers: ['> 1%', 'last 2 versions'] } }] ], cacheDirectory: true // 开启缓存 } } }

效果:

// 源代码 const greeting = () => 'Hello'

// 转换后
var greeting = function greeting() {
return ‘Hello’;
}

4.2 sass-loader - SCSS 编译

作用: 将 SCSS/Sass 文件编译成 CSS。

安装:

npm install -D sass-loader sass

配置:

{ test: /\.(scss|sass)$/, use: [ 'style-loader', 'css-loader', 'sass-loader' ] }

效果:

// 源代码 $primary: #42b983;

.button {
background: $primary;
}

// 编译后
.button {
background: #42b983;
}

4.3 css-loader - CSS 处理

作用: 处理 CSS 中的 @importurl(),将其转换成 requireimport

安装:

npm install -D css-loader

配置:

{ loader: 'css-loader', options: { modules: true, // 启用 CSS Modules importLoaders: 1 // 在 @import 之前应用几个 loader } }

效果:

/* 源代码 */ @import 'variables.scss';

/* 转换后 */
require(‘variables.scss’);

4.4 style-loader - CSS 注入

作用: 将 CSS 注入到 DOM 的 <style> 标签中。

安装:

npm install -D style-loader

配置:

{ loader: 'style-loader', options: { injectType: 'styleTag', // 注入方式 attributes: { id: 'app-styles' // 添加自定义属性 } } }

4.5 file-loader - 文件处理

作用: 将文件输出到输出目录,返回文件 URL。

安装:

npm install -D file-loader

配置:

{ test: /\.(png|jpg|gif|svg)$/, use: { loader: 'file-loader', options: { name: '[name].[hash].[ext]', // 文件名 outputPath: 'images/' // 输出目录 publicPath: '/images/' // 公共路径 } } }

效果:

// 源代码 import logo from './logo.png'

// 转换后
import logo from ‘/images/logo.abc123.png’

4.6 url-loader - Base64 处理

作用: 小文件转成 Base64,减少 HTTP 请求。

安装:

npm install -D url-loader

配置:

{ test: /\.(png|jpg|gif)$/, use: { loader: 'url-loader', options: { limit: 8192, // 小于 8KB 转 Base64 fallback: 'file-loader' // 大于限制使用 file-loader } } }

效果:

// 小于 8KB import logo from './small.png' // 转换成 import logo from 'data:image/png;base64,iVBORw0KG...'

// 大于 8KB
import logo from ‘./large.png’
// 保持原样,输出到文件

4.7 vue-loader - Vue 单文件组件

作用: 解析 .vue 单文件组件。

安装:

npm install -D vue-loader vue-template-compiler

配置:

{ test: /\.vue$/, use: 'vue-loader' }

效果:

<!-- 源代码 -->
<template>
  <div>{{ message }}</div>
</template>

<script>
export default {
data() {
return { message: ‘Hello’ }
}
}
</script>

<style scoped>
div { color: red; }
</style>

<!-- 转换成可执行的 JS 模块 -->


5. 自定义 Loader

5.1 同步 Loader

场景: 给所有 JS 文件添加版权注释。

// copyright-loader.js module.exports = function(source) { const copyright = `/** * Copyright © 2025 My Company */\n` return copyright + source }

使用:

{ test: /\.js$/, use: './copyright-loader' }

5.2 异步 Loader

场景: 调用异步 API 处理文件。

// translate-loader.js module.exports = function(source) { const callback = this.async()

// 模拟异步操作
setTimeout(() => {
const translated = source.replace(/Hello/g, ‘你好’)
callback(null, translated)
}, 1000)
}

5.3 带选项的 Loader

场景: 根据 options 决定行为。

// banner-loader.js module.exports = function(source) { const options = this.getOptions() || {} const banner = options.prefix || '// Custom Banner\n' return banner + source }

使用:

{ loader: './banner-loader', options: { prefix: '// My Project\n' } }

5.4 获取文件信息

// info-loader.js module.exports = function(source) { const filepath = this.resourcePath // 文件完整路径 const query = this.resourceQuery // 查询参数 const context = this.context // 上下文目录

console.log(‘处理文件:’, filepath)
console.log(‘上下文:’, context)

return source
}


6. Loader 链式调用

6.1 基本链式

{ test: /\.scss$/, use: [ 'style-loader', // 输出:注入到 style 'css-loader', // 输入:CSS 'sass-loader' // 输出:CSS ] }

6.2 多次使用同一 Loader

{ test: /\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { import: false } }, { loader: 'postcss-loader', options: { plugins: [...] } } ] }

6.3 内联 Loader(不推荐)

// 在 import 语句中直接指定 import styles from 'style-loader!css-loader!sass-loader!./main.scss'

7. 常见使用场景

场景一:Vue + TypeScript + SCSS

module.exports = { module: { rules: [ // Vue 单文件组件 { test: /\.vue$/, use: 'vue-loader' }, // TypeScript { test: /\.ts$/, exclude: /node_modules/, use: 'ts-loader' }, // SCSS { test: /\.scss$/, use: [ 'style-loader', 'css-loader', 'sass-loader' ] } ] } }

场景二:图片优化

{ test: /\.(png|jpg|gif)$/, use: [ { loader: 'image-webpack-loader', options: { mozjpeg: { progressive: true }, gifsicle: { interlaced: false } } }, 'url-loader' ] }

场景三:处理字体文件

{ test: /\.(woff|woff2|eot|ttf)$/, use: { loader: 'file-loader', options: { name: '[name].[hash].[ext]', outputPath: 'fonts/' } } }

场景四:CSS Modules

{ test: /\.module\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { modules: { localIdentName: '[name]__[local]--[hash:base64:5]' } } } ] }

场景五:处理 Markdown

{ test: /\.md$/, use: [ 'html-loader', { loader: 'markdown-loader', options: { gfm: true, tables: true } } ] }

8. 最佳实践

8.1 使用缓存

{ loader: 'babel-loader', options: { cacheDirectory: true, // 开启缓存 cacheCompression: false } }

8.2 排除不必要的文件

{ test: /\.js$/, exclude: [ /node_modules/, // 排除第三方库 /vendor/, // 排除供应商文件 /\.test\.js$/ // 排除测试文件 ], use: 'babel-loader' }

8.3 合理配置 include

{ test: /\.js$/, include: path.resolve(__dirname, 'src'), // 只处理 src 目录 use: 'babel-loader' }

8.4 使用 oneOf 优化

{ test: /\.js$/, oneOf: [ // 特殊文件特殊处理 { test: /\.worker\.js$/, use: 'worker-loader' }, // 普通文件 { use: 'babel-loader' } ] }

9. 常见问题

Q1: Loader 执行顺序搞反了?

A: Loader 是从右到左从下到上执行的。

// 假设 use 是数组 use: [ 'style-loader', // 3. 最后 'css-loader', // 2. 中间 'sass-loader' // 1. 最先 ]

Q2: Loader 找不到?

A: 确保已安装:

npm install -D css-loader

Q3: 如何调试 Loader?

A:

// my-loader.js module.exports = function(source) { console.log('处理文件:', this.resourcePath) console.log('内容:', source) return source }

Q4: Loader 性能慢?

A:

  • 开启缓存
  • 减少不必要的 Loader
  • 使用 include/exclude 缩小范围
  • 使用 thread-loader 多线程处理

Q5: 如何获取 Loader 版本?

A:

npm view css-loader version

总结

概念 说明
Loader 文件转换器,将非 JS 文件转换成 Webpack 可处理的模块
执行顺序 从右到左,从下到上
同步/异步 都支持,用 this.async() 处理异步
链式调用 多个 Loader 组合,输出作为下一个输入
最佳实践 缓存、合理 exclude/include、性能优化

Loader 是 Webpack 生态的核心,掌握 Loader 的原理和使用,能够让你更灵活地处理各种前端资源,提升开发效率。

希望这篇文章对你有帮助!有任何问题欢迎交流。