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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 《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日志并进行多维度分析。
目录
相关文章
|
20天前
|
监控 前端开发 JavaScript
Webpack 中 HMR 插件的工作原理
【10月更文挑战第23天】可以进一步深入探讨 HMR 工作原理的具体细节、不同场景下的应用案例,以及与其他相关技术的结合应用等方面的内容。通过全面、系统地了解 HMR 插件的工作原理,能够更好地利用这一功能,为项目的成功开发提供有力保障。同时,要不断关注技术的发展动态,以便及时掌握最新的 HMR 技术和最佳实践。
|
20天前
|
缓存 前端开发 JavaScript
Webpack 动态加载的原理
【10月更文挑战第23天】Webpack 动态加载通过巧妙的机制和策略,实现了模块的按需加载和高效运行,提升了应用程序的性能和用户体验。同时,它也为前端开发提供了更大的灵活性和可扩展性,适应了不断变化的业务需求和技术发展。
|
20天前
|
缓存 前端开发 JavaScript
Webpack 4 和 Webpack 5 区别?
【10月更文挑战第23天】随着时间的推移,Webpack 可能会继续发展和演进,未来的版本可能会带来更多的新特性和改进。保持对技术发展的关注和学习,将有助于我们更好地应对不断变化的前端开发环境。
|
20天前
|
缓存 前端开发 JavaScript
webpack 原理
【10月更文挑战第23天】Webpack 原理是一个复杂但又非常重要的体系。它通过模块解析、依赖管理、加载器和插件的协作,实现了对各种模块的高效打包和处理,为现代前端项目的开发和部署提供了强大的支持。同时,通过代码分割、按需加载、热模块替换等功能,提升了应用程序的性能和用户体验。随着前端技术的不断发展,Webpack 也在不断演进和完善,以适应不断变化的需求和挑战。
|
1月前
|
缓存 前端开发 JavaScript
Webpack 打包的基本原理
【10月更文挑战第5天】
|
1月前
|
前端开发 UED
Webpack 中处理 CSS 和图片资源的多 Loader 配置
【10月更文挑战第12天】 处理 CSS 和图片资源是 Webpack 配置中的重要部分。通过合理选择和配置多个 Loader,可以实现对这些资源的精细处理和优化,提升项目的性能和用户体验。在实际应用中,需要不断探索和实践,根据项目的具体情况进行灵活调整和优化,以达到最佳的处理效果。通过对 Webpack 中多 Loader 处理 CSS 和图片资源的深入了解和掌握,你将能够更好地应对各种复杂的资源处理需求,为项目的成功构建和运行提供坚实的基础。
55 1
|
1月前
|
前端开发 JavaScript
Webpack 中多个 Loader 的配置
【10月更文挑战第12天】使用多个 Loader 进行配置是 Webpack 中常见的操作,可以实现对各种资源的精细处理和优化。在配置时,需要根据具体需求合理选择和排列 Loader,并注意它们之间的顺序和交互关系。同时,不断了解和掌握新的 Loader 以及它们的特性,有助于更好地发挥 Webpack 的强大功能,提升项目的开发效率和质量。通过深入理解和熟练运用多个 Loader 的配置方法,你将能够更加灵活地处理各种资源,满足项目的多样化需求。
45 2
|
1月前
|
前端开发 JavaScript
Webpack 常用 Loader 和 Plugin
【10月更文挑战第12天】Webpack 是一个强大的模块打包工具,能够将各种资源模块进行打包和处理。Loader 用于转换模块的源代码,如 `babel-loader` 将 ES6+ 代码转换为 ES5,`css-loader` 处理 CSS 文件等。Plugin 扩展 Webpack 功能,如 `HtmlWebpackPlugin` 自动生成 HTML 文件,`UglifyJsPlugin` 压缩 JavaScript 代码。通过合理配置和使用 Loader 和 Plugin,可以构建高效、优化的项目。
21 2
|
1月前
|
移动开发 JavaScript 前端开发
webpack学习四:使用webpack配置plugin,来使用HtmlWebpackPlugin、uglifyjs-webpack-plugin、webpack-dev-server等插件简化开发
这篇文章主要介绍了如何通过配置Webpack的插件,如HtmlWebpackPlugin、uglifyjs-webpack-plugin和webpack-dev-server,来简化前端开发流程。
39 0
webpack学习四:使用webpack配置plugin,来使用HtmlWebpackPlugin、uglifyjs-webpack-plugin、webpack-dev-server等插件简化开发
|
2月前
|
设计模式 前端开发 JavaScript
webpack实战之手写一个loader和plugin
该文章详细讲解了如何从零开始编写一个自定义的Webpack Loader和Plugin,包括它们的工作原理、开发步骤以及如何将自定义的Loader和Plugin集成到Webpack配置中。
webpack实战之手写一个loader和plugin