同步 Loader

  1. // 1. return 源码
  2. module.exports = function (content, map, data) {
  3. return `
  4. // 增加备注
  5. ${content}
  6. `
  7. }
  8. // 2. return undefined + this.callback(源码)
  9. module.exports = function (content, map, data) {
  10. this.callback(null, `
  11. // 增加备注
  12. ${content}
  13. `, map, data);
  14. return;
  15. }

异步 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);
  })
}

最佳实践:

  1. 建议尽量使用异步 loader
  2. loader 不要使用箭头函数,因为需要调用 this 上的方法

    Raw Loader

    默认情况下,loader 接收的源文件内容为 String UTF-8 格式,也可以使用 Buffer 格式,不过需要设置 raw 为 true
    module.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

就是所有 loader!源文件 拼起来的字符串

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

参数:

  1. Error 或者 null
  2. 第二个参数代表源内容,String 或者 Buffer
  3. soucemap
  4. 元数据 meta

同步或者异步

this.async

告诉 loader-runner 这个 loader 将会异步地回调,返回 this.callback

const callback = this.async();
callback(err, content, map, meta);

this.data

在 pitch 阶段和正常阶段之间共享的 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

当前 loader 在 loader 数组中的索引

this.resource

request 中的资源部分,资源路径 (包括 query 参数) 👇
/Users/wholee/Desktop/webpack-css-system/require-loader/index.js?name=Simon

this.resourcePath

资源路径,不包括 query 参数

this.resourceQuery

资源路径中的 query 参数 => ?name=Simon

this.target

编译目标:web | node,从配置选项中传递过来的

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

像 require 表达式一样解析一个 request.

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 种类

  1. 前置 loader
  2. 普通 loader
  3. 行内 loader
  4. 后置 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 的重复执行

  1. ! 忽略普通 loader 执行
  2. -! 忽略普通 loader 和前置 loader 执行
  3. !! 忽略所有普通,前置,后置 loader 执行

可以查看 DEMO:
loader-types.zip