同步 Loader
// 1. return 源码module.exports = function (content, map, data) {return `// 增加备注${content}`}// 2. return undefined + this.callback(源码)module.exports = function (content, map, data) {this.callback(null, `// 增加备注${content}`, map, data);return;}
异步 Loader
module.exports = function (content, map, meta) {
const callback = this.async();
somAsyncOperation(content, (err, result) => {
if (err) return callback(err);
callback(null, result, map, meta);
})
}
最佳实践:
- 建议尽量使用异步 loader
- loader 不要使用箭头函数,因为需要调用 this 上的方法
Raw Loader
默认情况下,loader 接收的源文件内容为 String UTF-8 格式,也可以使用 Buffer 格式,不过需要设置 raw 为 truemodule.exports = function (content) { return content; } module.exports.raw = true;Pitching Loader
loader 是 洋葱模型 ,首先从左往右执行 loader 上的 pitch 方法,然后从右往左执行 loader 方法pitch 捕获并共享信息
module.exports = function (content, map, meta) { conosle.log('姓名:', this.data.name); } module.exports.pitch = function (remainingRequest, precedingRequest, data) { data.name = 'Alex'; }pitch 返回结果,则跳过剩下 loader
``javascript module.exprots.pitch = function (remainingRequest, precedingRequest, data) { return
` }module.exports = require(${JSON.stringify('-!' + remainingRequest)})
这样就只会执行这个 loader 之前的 loader 集合以及此 loader 的 pitch
<a name="APmNz"></a>
# Loader 上下文
<a name="6Akxj"></a>
#### 使用 require 调用 加载器 加载 源码
require('style-loader!css-loader?module?./index.css')
<a name="2L02l"></a>
#### this.version
使用版本号判断版本,做断层版本的兼容
<a name="Uvaew"></a>
#### this.context
模块所在目录<br />`/Users/wholee/Desktop/webpack-css-system/require-loader`
<a name="ZlNpJ"></a>
#### this.request
解析出来的 request 字符串
```javascript
/Users/wholee/Desktop/webpack-css-system/require-loader/test_pre.js!/Users/wholee/Desktop/webpack-css-system/require-loader/test.js!/Users/wholee/Desktop/webpack-css-system/require-loader/index.js
this.query
取的 options 或者 require 中的 ? 开头字符串
// 场景1:
{
loader: path.resolve(__dirname, './test.js'),
options: {
name: 'Simon'
}
}
=> { name: Simon }
// 场景2:
require('./test_pre?name=Simon111!./index1.js')
=> ?name=Simon111
this.callback
参数:
- Error 或者 null
- 第二个参数代表源内容,String 或者 Buffer
- soucemap
- 元数据 meta
this.async
告诉 loader-runner 这个 loader 将会异步地回调,返回 this.callback
const callback = this.async();
callback(err, content, map, meta);
this.data
this.cacheable
设置当前 loader 结果是否可被缓存,在输入和相关依赖没有变化时,必须返回相同的结果。
this.cacheable(false) 调用
this.loaders
获取加载当前资源用到的所有 loaders
[
{
path: '/Users/wholee/Desktop/webpack-css-system/require-loader/test_pre.js',
query: '',
options: { name: 'Simon' },
pitch: undefined
},
{
path: '/Users/wholee/Desktop/webpack-css-system/require-loader/test.js',
query: '',
options: undefined,
pitch: [Function (anonymous)]
}
]
this.loaderIndex
this.resource
request 中的资源部分,资源路径 (包括 query 参数) 👇/Users/wholee/Desktop/webpack-css-system/require-loader/index.js?name=Simon
this.resourcePath
this.resourceQuery
资源路径中的 query 参数 => ?name=Simon
this.target
this.loadModule
解析一个给定的 request 到一个模块
this.loadModule(remainingRequest, function (err, source, map, meta) {
console.log(err, source, map, meta);
})
比如:
css-loader!./index.css
loadModule 调用加载之后,source 为:
// Imports
import ___CSS_LOADER_API_IMPORT___ from "../node_modules/css-loader/dist/runtime/api.js";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function(i){return i[1]});
// Module
___CSS_LOADER_EXPORT___.push([module.id, "body {\n background: #ff0000;\n}", ""]);
// Exports
export default ___CSS_LOADER_EXPORT___;
this.resolve
this.addDependency
加入一个文件作为产生 loader 结果的依赖,监听文件变化重新打包
module.exports = function (source) {
this.addDependency(path.resolve(__dirname, './index1.js'))
return source;
}
chunk 中没有引用 index1.js,正常 index1.js 内容改变不会重新打包,addDependency 之后会监听 index1 内容变化并重新打包
this.addContextDependency
把文件夹作为 loader 结果的依赖加入,文件夹下内容变化则重新打包
⚠️ 需要注意的是:不要监听打包产出文件,不然会死循环
this.emitFile
发射文件,产出结果 dist 中会多一个 1.js
this.emitFile('1.js', 'console.log(1111)')
this.fs
Loader 种类
- 前置 loader
- 普通 loader
- 行内 loader
- 后置 loader ```javascript // 行内 loader 书写方式 import name from ‘./InlineLoader!./name.css’
// 其他三种 loader 通过 webpack.config.js module 配置 module: { rules: [ { test: /.css$/, enforce: ‘pre’, loader: path.resolve(dirname, ‘./PreLoader’) }, { test: /.css$/, loader: path.resolve(dirname, ‘./NormalLoader’) }, { test: /.css$/, enforce: ‘post’, loader: path.resolve(__dirname, ‘./PostLoader’) } ] }
// name.css const name = ‘Alex’ export default name;
这里需要注意的是:为何要拿 css 后缀来测试,不要拿 js 后缀测试,因为 loader 中可能会注入 js,会导致输出结果乱七八糟的,所以 css 更为准确<br />执行结果为:
```javascript
PostLoader pitch
InlineLoader pitch
NormalLoader pitch
PreLoader pitch
PreLoader
NormalLoader
InlineLoader
PostLoader
正因为有这么多的 loader,就会导致行内 loader 有个问题:行内 loader 引入的文件后缀也是 .css,也会走其他种类的 loader,就导致了多次加载
使用前缀忽略 loader
在使用 inline loader 时,可以使用 ! -! !! 来忽略其他 loader 的重复执行
- ! 忽略普通 loader 执行
- -! 忽略普通 loader 和前置 loader 执行
- !! 忽略所有普通,前置,后置 loader 执行
可以查看 DEMO:
loader-types.zip
