We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
loader 的作用是将不同类型的文件转换为 webpack 可识别的模块。任何非 js 文件都必须被预先处理转换为 js 代码,才可以参与打包,loader 就是这样一个代码转换器。
loader
webpack
loader 本质就是导出一个函数的 node 模块,loader runner 会调用这个函数,然后把上一个 loader产生的结果或者资源传进去。函数的 this 上下文是由 webpack 提供的。
this
function Loader(source, sourceMap?, data?) { // source 为 loader 的输入,可能是文件内容,也可能是上一个 loader 处理结果 return source; }; module.exports = Loader
可以在处理某种文件的时候配置多个 loader。
以处理 less 为例
module.exports = { module: { rules: [ { test: /\.less/, // less 文件的处理顺序为先 less-loader 再 css-loader 再 style-loader use: [ 'style-loader', { loader:'css-loader', // 给 css-loader 传入配置项 options:{ minimize:true, } }, 'less-loader'], }, ] }, };
可以看出来这是一个链式的调用。但是他们会以相反的顺序执行,从下到上或者从右到左
在 Webpack 中,loader 可以被分为 4 类:pre 前置、post 后置、normal 普通和 inline 行内。其中 pre 和 post loader 可以通过 rule 对象的 enforce 属性来指定
Webpack
rule
enforce
// 使用 ! 将资源中的 loader 分开。 import Styles from 'style-loader!css-loader?modules!./styles.css';
通过为内联 import 语句添加前缀,可以覆盖配置中的所有 loader
import
!
normal loader
import Styles from '!style-loader!css-loader?modules!./styles.css';
!!
import Styles from '!!style-loader!css-loader?modules!./styles.css';
-!
preLoader
postLoaders
import Styles from '-!style-loader!css-loader?modules!./styles.css';
可以给 inline loader 传参,?key=value&foo=bar 或者 ?{"key":"value","foo":"bar"},上面的例子中的 modules 就是参数
inline loader
?key=value&foo=bar
?{"key":"value","foo":"bar"}
顾名思义,指定了 enforce 属性的 loader 在执行时不是默认的从下到上(或从右到左)的顺序。
// webpack.config.js // 这里就是先执行 a-loader 再执行 b-loader 最后执行 c-loader const path = require("path"); module.exports = { module: { rules: [ { test: /\.txt$/i, use: ["a-loader"], enforce: "pre", // pre loader }, { test: /\.txt$/i, use: ["b-loader"], // normal loader }, { test: /\.txt$/i, use: ["c-loader"], enforce: "post", // post loader }, ], }, };
content 可以是 string 或者 Buffer,在我们处理图片,音频,视频等这些文件的时候,我们就需要用到 Buffer,这时我们需要将 row 设为 true。
content
string
Buffer
row
true
module.exports = function (content) { assert(content instanceof Buffer); return someSyncOperation(content); }; module.exports.raw = true
首先我们的 loader 通常是到处一个函数
同时我们可以在 loader 函数上添加一个 pitch 属性,它的值也是一个函数。它会比 loader 更早执行
pitch
/** * @remainingRequest 剩余请求 * @precedingRequest 前置请求 * @data 数据对象 */ Loader.pitch = function (remainingRequest, precedingRequest, data) { // };
可以通过 data 参数来进行数据传递,在 Normal loader 中就可以通过 this.data 的方式读取数据。
data
this.data
loader 执行链的执行过程分为三个阶段,pitch、解析资源、loader执行。
// a-loader module.exports = function(content) { return content; }; module.exports.pitch = function(remainingRequest) { // };
// b-loader module.exports = function(content) { return content; }; module.exports.pitch = function(remainingRequest) { // };
// c-loader module.exports = function(content) { return content; }; module.exports.pitch = function(remainingRequest) { // };
module: { rules: [ { test: /\.txt$/i, use: ["a-loader","b-loader", "c-loader"], }, ], },
上面的例子的 loader 的执行顺序是
而 pitch 的执行顺序是和 loader 执行顺序相反,可以看成是顺序入栈倒序出栈
同时 pitch 还有一个重要功能:阻断。
还是上面的例子,只不过现在我们 b-loader 的 pitch 函数返回一个。
// b-loader module.exports = function(content) { return content; }; module.exports.pitch = function(remainingRequest) { return '123'; };
现在的执行顺序就是
当 pitch 有返回(非 undefined),就会跳过后续 pitch 的执行,接着只执行之前执行了 pitch 相关联的 loader,而且 b-loader 的返回值也是 pitch 返回的值
undefined
b-loader
可以看出 pitch 提供了一个可以阻断loader链执行的方式。style-loader 就用到了 pitch,在 style-loader 中,是没有loader内容的,只有 loader.pitch
style-loade
style-loader
loader.pitch
// style-loader const loaderAPI = () => {}; loaderAPI.pitch = function loader(request) { // return someCode }
这里就很奇怪了,为什么style-loader阻断了loader(pitch 有返回值)执行,但是我们写的样式依旧可以生效!
这是因为在 style-loader 的返回值里面返回了 inline loader。然后执行 inline loader返回的css字符串直接写进的style标签,然后再插入 dom 中
css
style
dom
// style-loader 返回内容中很重要的一段 // !! 禁用所有已配置的 loader import content, * as namedExport from "!!css-loader-path!less-loader-path!./index.less";
所以我们使用 less 时,实际的loader执行顺序是: 第一次执行
less
第二次是inline loader执行
并且第一次执行的时候是在webpack编译阶段,但是第二次执行则是在浏览器在加载 js 的时候执行。
事实上 style-loader 这么设计是有原因的: style-loader 会去操作 dom 元素(会给dom添加 style 标签)。但是在webpack编译阶段执行 loader 的过程中,是在 nodejs 环境,没有 dom 对象。所以才通过返回 inline loader执行,执行结果在通过 style-loader 处理添加到 dom 中。
nodejs
通过在 rule 对象设置 path.resolve 指向这个本地 loader
path.resolve
{ test: /\.js$/ use: [ { loader: path.resolve('path/to/loader.js'), options: {/* ... */} } ] }
匹配多个 loaders, 可以使用 resolveLoader.modules 配置
loaders
resolveLoader.modules
resolveLoader: { modules: [ 'node_modules', // 本地 loaders 目录 path.resolve(__dirname, 'loaders') ] }
或者通过 npm link 来关联到需要测试的项目
npm link
this.getOptions
从 webpack 5 开始,getOptions可以在loader上下文中使用。替代loader-utils 中的 getOptions。
webpack 5
getOptions
loader-utils
获取loader参数
this.getOptions()
与 pitch 共享的数据
loader 是可以通过 return 来返回处理结果,但是如果需要返回多个结果时,需要使用到 callback API。
return
callback API
this.callback( err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any // webpack 会忽略,loader 之间可以传输信息 );
异步loader
async function Loader(source) { // 1. 获取异步回调函数 const callback = this.async(); let res; try { // 2. 调用less 将模块内容转译为 css res = await SomeAsync(); } catch (error) { // ... } callback(null, res); }
默认情况下,loader的结果都是被缓存的,传递 false 会让 loader 结果不缓存。
false
this.cacheable(flag = true: boolean)
添加依赖来监听,依赖项变化时,会重新编译生成缓存
this.addDependency(file: string)
更多
// txt-loader.js module.exports = function loader(source) { return `module.exports = '${source}'`; }
// index.js import Data from "./data.txt" const msgElement = document.querySelector("#message"); msgElement.innerText = Data;
// simple-babel-loader.js const core = require('@babel/core'); module.exports = function loader(source) { const options = this.getOptions() || {}; const callback = this.async(); core.transform(source, options, function (err, result) { if (err) { callback(err); } else { callback(null, result.code); } }); }
当 loader 用来解析非 js 文件到 js 文件时,最后返回的代码是需要添加module.exports 或者 export default字符串,这样外界在导入模块的时候就可以接收到这个HTML字符串。
module.exports
export default
return `module.exports = ${JSON.stringify(someString)}` // or // 有 callback 就不需要 return this.callback(null, `module.exports = ${JSON.stringify(someString)}`)
The text was updated successfully, but these errors were encountered:
No branches or pull requests
什么是 loader
loader
的作用是将不同类型的文件转换为webpack
可识别的模块。任何非 js 文件都必须被预先处理转换为 js 代码,才可以参与打包,loader
就是这样一个代码转换器。loader
本质就是导出一个函数的 node 模块,loader runner 会调用这个函数,然后把上一个loader
产生的结果或者资源传进去。函数的this
上下文是由webpack
提供的。loader 链式调用
可以在处理某种文件的时候配置多个
loader
。以处理 less 为例
可以看出来这是一个链式的调用。但是他们会以相反的顺序执行,从下到上或者从右到左
loader 使用
在
Webpack
中,loader
可以被分为 4 类:pre 前置、post 后置、normal 普通和 inline 行内。其中 pre 和 post loader 可以通过rule
对象的enforce
属性来指定inline loader
通过为内联
import
语句添加前缀,可以覆盖配置中的所有loader
!
前缀,将禁用所有已配置的normal loader
!!
前缀,将禁用所有已配置的loader
(preLoader, normal loader, postLoader)-!
前缀,将禁用所有已配置的preLoader
和loader
,但是不禁用postLoaders
可以给
inline loader
传参,?key=value&foo=bar
或者?{"key":"value","foo":"bar"}
,上面的例子中的 modules 就是参数pre Loader, normal loader, post Loader
顾名思义,指定了
enforce
属性的loader
在执行时不是默认的从下到上(或从右到左)的顺序。raw模式
content
可以是string
或者Buffer
,在我们处理图片,音频,视频等这些文件的时候,我们就需要用到Buffer
,这时我们需要将row
设为true
。pitch
首先我们的
loader
通常是到处一个函数同时我们可以在
loader
函数上添加一个pitch
属性,它的值也是一个函数。它会比loader
更早执行可以通过
data
参数来进行数据传递,在 Normal loader 中就可以通过this.data
的方式读取数据。loader
执行链的执行过程分为三个阶段,pitch
、解析资源、loader
执行。上面的例子的
loader
的执行顺序是而
pitch
的执行顺序是和loader
执行顺序相反,可以看成是顺序入栈倒序出栈同时
pitch
还有一个重要功能:阻断。还是上面的例子,只不过现在我们 b-loader 的 pitch 函数返回一个。
现在的执行顺序就是
当
pitch
有返回(非undefined
),就会跳过后续pitch
的执行,接着只执行之前执行了pitch
相关联的loader
,而且b-loader
的返回值也是pitch
返回的值可以看出
pitch
提供了一个可以阻断loader
链执行的方式。style-loade
r 就用到了pitch
,在style-loader
中,是没有loader
内容的,只有loader.pitch
这里就很奇怪了,为什么
style-loader
阻断了loader
(pitch
有返回值)执行,但是我们写的样式依旧可以生效!这是因为在
style-loader
的返回值里面返回了inline loader
。然后执行inline loader
返回的css
字符串直接写进的style
标签,然后再插入dom
中所以我们使用
less
时,实际的loader
执行顺序是:第一次执行
第二次是
inline loader
执行并且第一次执行的时候是在
webpack
编译阶段,但是第二次执行则是在浏览器在加载 js 的时候执行。为什么 style-loader 要这么设计
事实上
style-loader
这么设计是有原因的:style-loader
会去操作dom
元素(会给dom
添加style
标签)。但是在webpack编译阶段执行loader
的过程中,是在nodejs
环境,没有dom
对象。所以才通过返回inline loader
执行,执行结果在通过style-loader
处理添加到dom
中。loader开发
本地开发调试
通过在
rule
对象设置path.resolve
指向这个本地loader
匹配多个
loaders
, 可以使用resolveLoader.modules
配置或者通过
npm link
来关联到需要测试的项目loader API
this.getOptions
获取
loader
参数this.data
与
pitch
共享的数据this.callback
loader
是可以通过return
来返回处理结果,但是如果需要返回多个结果时,需要使用到callback API
。this.async
异步
loader
this.cachable
默认情况下,
loader
的结果都是被缓存的,传递false
会让loader
结果不缓存。this.addDependency
添加依赖来监听,依赖项变化时,会重新编译生成缓存
更多
实现一个 txt-loader
实现极简 babel-loader
工具
注意事项
当 loader 用来解析非 js 文件到 js 文件时,最后返回的代码是需要添加
module.exports
或者export default
字符串,这样外界在导入模块的时候就可以接收到这个HTML字符串。代码实现
参考
The text was updated successfully, but these errors were encountered: