《Webpack5 核心原理与应用实践》学习笔记-> webpack的loader开发技巧

简介: 《Webpack5 核心原理与应用实践》学习笔记-> webpack的loader开发技巧

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情

loader的作用就是帮助webpack来识别计算机的文件,把webpack本身不识别的文件资源类型转换成webpack可以识别的js处理方式,然后再交由webpack来处理。

loader 是什么?


从计算机的世界来看,文件资源格式实在是太多了,webpack如果将所有文件资源都替你做好那工作量庞大且不说,还有很多开发商喜欢用自定义类型,你可以自定义那webpack就直接把解析也自定义不就ok了,你把你的东西处理成我能识别的javascript格式,其他的你爱咋咋地。


下面就是一个loader函数形式,具体可以参考loaders


/**
 * @param source {string|Buffer} 资源输入,对于第一个执行的 Loader 为资源文件的内容;后续执行的 Loader 则为前一个 Loader 的执行结果,可能是字符串,也可能是代码的 AST 结构;
 * @param sourceMap? {Object} 可选参数,代码的 [sourcemap](https://github.com/mozilla/source-map) 结构
 * @param data? {any} 数据,可以是任何内容
 * @returns {*}
 */
module.exports = (source, sourceMap, data) => {
    return source;
}

注:上面是示例代码写了剪头函数,真实的loader不要使用剪头函数,因为webpack会通过this注入上下文,剪头函数的this指向的是外层的this,会导致注入失败。


Loader 简单示例


上面的loader是啥也没做,所以我们现在就写一个能做点啥的loader,代码目录结构如下


├─ src
│  ├─ loader
│  ├─  └─ index.js
│  └─ main.js
├─ package.json
└─ webpack.config.js

首先src/loader/index.js里面的内容就是上面的那个,main.js里面什么都没有,所以现在只需要配置一下webpack.config.js就好了:


const path = require('path');
module.exports = {
    mode: "development",
    entry: "./src/main.js",
    output: {
        path: path.resolve(__dirname, "./dist"),
        filename: "[name].js",
        clean: true
    },
    module: {
        rules: [{
            test: /.js$/,
            use: ['./src/loader/index.js'], // loader 直接指向本地地址就好
        }]
    }
}

当然现在我们的loader是啥也干不了,那我们就加点功能,就比如说我要在文件头部自动加上一些信息,修改src/loader/index.js代码如下:


module.exports = function(source) {
    return (
`/**
 * @author 田八
 */
${source}
`
    )
}

接下来就执行一下npx webpack来看一下构建的结果吧。

image.png

使用上下文接口


上面的代码是将对应的文件进行转置,其实在loader的运行过程中,webpack会自动主动上下文this,用以使用更加丰富的webpack的能力,上面提供的loader链接文档中有这一部分内容。


我们可以直接通过this关键字来访问webpack的上下文,上面的示例修改如下可以达到同样的效果:


module.exports = function(source, sourceMap, data) {
    this.callback(
        null,
        `/** \n * @author 田八\n */\n${source}`,
        sourceMap,
        data
    )
}

this.callback 参数解释如下

  1. 第一个参数必须是 Error 或者 null
  2. 第二个参数是一个 string 或者 Buffer
  3. 可选的:第三个参数必须是一个可以被 this module 解析的 source map。
  4. 可选的:第四个参数,会被 webpack 忽略,可以是任何东西(例如一些元数据)。


直接调用this.callback和使用return source的效果是相同的,但是使用this.callback的好处是可以传递多个参数。


官方文档中的内容已经很详细了,下来列举的是比较常用的接口:


  • fs:Compilation 对象的 inputFileSystem 属性,我们可以通过这个对象获取更多资源文件的内容;
  • resource:当前文件路径;
  • resourceQuery:文件请求参数,例如 import "./a?foo=bar"resourceQuery 值为 ?foo=bar
  • callback:可用于返回多个结果;
  • getOptions:用于获取当前 Loader 的配置对象;
  • async:用于声明这是一个异步 Loader,开发者需要通过 async 接口返回的 callback 函数传递处理结果;
  • emitWarning:添加警告;
  • emitError:添加错误信息,注意这不会中断 Webpack 运行;
  • emitFile:用于直接写出一个产物文件,例如 file-loader 依赖该接口写出 Chunk 之外的产物;
  • addDependency:将 dep 文件添加为编译依赖,当 dep 文件内容发生变化时,会触发当前文件的重新构建;


取消 Loader 缓存


默认情况下,webpackloader的处理结果进行缓存,这是因为loader处理文件是CPU密集型操作,很耗费性能,使用this.cacheable方法,并传入false可以关闭缓存能力:


module.exports = function (source, sourceMap, data) {
    this.cacheable(false);
    this.callback(null,
        `/** \n * @author 田八\n */\n${source}`,
        sourceMap,
        data
    )
}

在 Loader 返回异步结果


webpack还可以异步返回处理结果,对于需要使用计算机其他硬件或者软件协助实现的功能,处理时间和时机往往都是不确定的,这个时候就需要使用异步返回(this.async)了,它返回一个this.callback,我们就可以使用这个callback返回结果:


module.exports = function (source, sourceMap, data) {
    const callback = this.async(); // 显示调用,返回一个 callback
    setTimeout(() => {
        // 异步返回结果,同this.callback的使用方式一致
        callback(null,
            `/** \n * @author 田八\n */\n${source}`,
            sourceMap,
            data
        )
    }, 1000)
}


在 Loader 中直接写出文件


this.emitFile用于直接写入内容到新的产物中,还是上面的示例,可以将注释内容写到一个单独的文件中:


module.exports = function (source, sourceMap, data) {
    this.emitFile('author.txt', '/** \n * @author 田八\n */');
    return source;
}

在 Loader 中添加额外依赖


首先需要理解一下什么叫额外依赖,额外依赖就是一开始webpack管不着的依赖,但是我需要webpack来帮我管理起来,不然这些文件发生改变了,webpack不知道,也就无法通知给对应的loader让其进行处理,这个时候就可以通过this.addDependency进行添加了:


module.exports = function (source, sourceMap, data) {
    this.addDependency(path); // 这个 path 是虚拟的,参数类型为 string
    return source;
}

由于我的loader示例太过于简单,所以这一块不太方便尝试验证,应该使用其他类型的文件匹配规则的loader,现成的示例就是sass-loaderless-loader就是使用这种方式添加的。

  • sass-loader源码片段

image.png

此外,Loader Context 还提供了下面几个与依赖处理相关的接口:


  • addContextDependency(directory: String):添加文件目录依赖,目录下内容变更时会触发文件变更;
  • addMissingDependency(file: String):用于添加文件依赖,效果与 addDependency 类似;
  • clearDependencies():清除所有文件依赖。


处理二进制资源


默认情况下,loader的第一个参数是string类型,这通常对标的是文本类型,例如.js.css.html.vue等,但是如果是.png.mp3.avi这种通常都是使用二进制流来处理的,string肯定是不行的,通过设置 rawtrueloader 可以接收原始的 Buffer,如:


module.exports = function (source, sourceMap, data) {
    console.log(source);
    return source;
}
module.exports.raw = true;

image.png

在 Loader 中正确处理日志


webpack内置了infrastructure logging,使用方式很简单,调用this.getLogger()可以获得一个logger的实例,下面是官网原文:


  • Loaders 最好使用 this.getLogger() 进行日志记录,这是指向 compilation.getLogger() 具有 loader 路径和已处理的文件。这种日志记录被存储到 Stats 中并相应地格式化。它可以被 webpack 用户过滤和导出。
  • Loaders 可以使用 this.getLogger('name') 获取具有子名称的独立记录器。仍会添加 loader 路径和已处理的文件。
  • Loaders 可以使用特殊的回退逻辑来检测日志支持 this.getLogger() ? this.getLogger() : console。在使用不支持 getLogger 方法的旧 webpack 版本时提供回退方法。
  • 代码示例


module.exports = function (source, sourceMap, data) {
    const logger = this.getLogger("xxx-loader");
    logger.info('这是info级别的日志')
    return source;
}

在 Loader 中正确上报异常


上面写到了日志,日志分级别,那么可能就会直接想到使用日志上报异常,想法很ok,但是loader中对异常的处理还有其他的方式,日志只是其中一种。


使用this.emitError,emit 一个错误,也可以在输出中显示,示例:


module.exports = function (source, sourceMap, data) {
    this.emitError(new Error('出错啦!!!'))
    return source;
}
module.exports.raw = true;

  • 运行效果

image.png

this.emitErrorthis.getLogger的区别:


  • 相同点
  • 都可以在控制台输出内容。
  • 都不会打断正常编译流程。
  • 不同点
  • this.Logger可以通过用户配置来控制控制台显示的信息级别。
  • this.emtiError没有级别,一定会打印在控制台中。

使用this.callback来提交错误信息:

module.exports = function (source, sourceMap, data) {
    this.callback(
        new Error('出错啦!!!'),
        source,
        sourceMap,
        data
    );
}

  • 运行效果

image.png

对于上面的两种方式,这种方式会直接导致编译失败,之后,Webpack 会将 callback 传递过来的错误信息当做模块内容,打包进产物文件,如上图。


总结


loader就是用于帮助webpack识别更多的计算机文件类型,因为loader的存在,使webpack处理的文件不局限在javascript上,只要有对应的loader理论上可以处理任何文件,同时配合使用webpackloader提供的上下文,我们可以实现各种各样的需求。


这一篇其实只写了一半,一般说到loader就会提到loader的加载顺序,还有loader都在开发了,但是怎么测试?这些准备下一篇写。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
缓存 JavaScript 前端开发
js开发:请解释什么是Webpack,以及它在项目中的作用。
Webpack是开源的JavaScript模块打包器,用于前端项目构建,整合并优化JavaScript、CSS、图片等资源。它实现模块打包、代码分割以提升加载速度,同时进行资源优化和缓存。借助插件机制扩展功能,并支持热更新,加速开发流程。
20 4
|
2月前
|
Web App开发 JSON 前端开发
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
53 0
|
3月前
|
JSON 前端开发 JavaScript
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)(上)
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
56 0
|
3天前
|
缓存 JavaScript 前端开发
js开发:请解释什么是Webpack,以及它在项目中的作用。
Webpack是开源的JavaScript模块打包器,用于前端项目构建,整合并优化JavaScript、CSS、图片等资源。它实现模块打包、代码分割以提升加载速度,同时进行资源优化和缓存。Webpack的插件机制可扩展功能,支持热更新以加速开发流程。
13 2
|
2月前
|
缓存 前端开发 算法
Webpack 进阶:深入理解其工作原理与优化策略
Webpack 进阶:深入理解其工作原理与优化策略
45 2
|
2月前
|
缓存 前端开发 JavaScript
|
3月前
|
前端开发 JavaScript
webpack 核心武器:loader 和 plugin 的使用指南(下)
webpack 核心武器:loader 和 plugin 的使用指南(下)
webpack 核心武器:loader 和 plugin 的使用指南(下)
|
3月前
|
JSON 前端开发 JavaScript
webpack 核心武器:loader 和 plugin 的使用指南(上)
webpack 核心武器:loader 和 plugin 的使用指南(上)
webpack 核心武器:loader 和 plugin 的使用指南(上)
|
3月前
|
XML JSON 前端开发
说说webpack中常见的loader?解决了什么问题?
在Webpack中,Loader是用于处理各种文件类型的模块加载器,它们用于对文件进行转换、处理和加载。常见的Loader解决了以下问题:
19 0
|
3月前
|
Web App开发 前端开发 JavaScript
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)(下)
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
26 0