什么是Loader?

继上两篇文章webpack工作原理介绍(上篇、下篇),我们了解到Loader:模块转换器,也就是将模块的内容按照需求装换成新内容,而且每个Loader的职责都是单一,只会完成一种转换,所以我们一般对源文件的处理,也是由多个Loader以链式顺序执行的方式来进行多次装换,然后得到我们要的结果。

那么这样Loader只需要关心输入和输出,Loader其实是一个Node.js模块,该模块导出的是一个函数(意味着,所有node.js的api我们都可以使用),如下:

    module.exports = function (source) {// 对source做一系列的转换return source;}

下面我们介绍一下webpack提供了哪些供Loader调用的api,对Loader有个比较深刻的理解,然后来分析babel-loader的源码,看看我们常用的loader是怎么编写出来的。

获得Loader的options

    const loaderUtils = require('loader-utils');module.exports = function(source) {// 获取用户为当前Loader传入的optionsconsole.log(loaderUtils.getOptions(this));return source;}

返回其他结果

如上,我们返回的是转换后的内容,但是有些情况下,我们不仅仅需要返回转换后的内容,还需要返回一些其他的内容,如sourceMap或是AST语法树,那么这时候我们可以使用webpack提供的APIthis.callback,当使用this.callback了,那么我们就必须需要在Loader函数返回undefined,以此来让webpack知道返回的结果在this.callback中,API详细参数如下:

    this.callback(// 无法装换原内容时的Errorerr: Error || null,// 装换后的的内容,如上述的sourcecontent: string | Buffer,// 用于通过装换后的内容得出原内容的Source Map,方便调试// 我们了解到,SourceMap我们只是会在开发环境去使用,于是就会变成可控制的,// webpack也提供了this.sourceMap去告诉是否需要使用sourceMap,// 当然也可以使用loader的option来做判断,如css-loadersourceMap?: SourceMap,// 如果本次转换同时生成ast语法树,也可以将这个ast返回,方便后续loader需要复用该ast,这样可以提高性能abstractSyntaxTree? AST);

同步与异步

看看异步Loader在this.asyncAPI下如何实现,

    module.exports = async function (source) {const callback = this.async();const { err, content, sourceMap, AST } = await Func();callback(err, content, sourceMap, AST); // 如上诉`this.callback`参数一样}

处理二进制数据

file-loader这样的Loader,处理的是二进制数据,那么就需要告诉webpack给loader传入二进制格式的数据,代码可以如下:

    module.exports = function(source) {if (source instanceof Buffer) {// 一系列操作return source; //当然我本身也可以返回二进制数据提供给下一个loader}}moudle.exports.raw = true; //不设置,就会拿到字符串

通过moudle.exports.raw = true;告知webpack,自己本身需要二进制数据。

缓存加速

优化的最佳点,可以使用this.cacheable(Boolen),缓存loader转换后的内容,当处理文件或依赖文件没有发生变化时,使用缓存的转换内容,以此提速!

其他API

说到学习,当然越系统越好了,api多介绍 ,除了上面常用的api之外,还存在以下常用的api。

  • this.context: 当前处理转换的文件所在的目录
  • this.resource: 当前处理转换的文件完整请求路径,包括querystring
  • this.resourcePath: 当前处理转换的文件的路径
  • this.resourceQuery: 当前处理文件的querystring
  • this.target: webpack配置的target
  • this.loadMoudle: 处理文件时,需要依赖其他文件的处理结果时,可以使用this.loadMoudle(request: string, callback: function(err, source, sourceMap, module))去获取到依赖文件的处理结果。
  • this.resolve: 获取指定文件的完整路径,this.resolve(context: string, request: string, callback: function(err, result: string))
  • this.addDependency: 为当前处理文件添加依赖文件,以便依赖文件发生变化时重新调用Loader转换该文件,this.addDependency(file: string)
  • this.addContextDependency: 为当前处理文件添加依赖文件目录,以便依赖文件目录里文件发生变化时重新调用Loader转换该文件,this.addContextDependency(dir: string)
  • this.clearDependencies: 清除当前正在处理的文件的所有依赖
  • this.emitFile: 输出一个文件,使用的方法为this.emitFile(name: string, content: Buffer | string, sourceMap: {...})

babel-loader源码简析

源码第一行如下:

    let babel;try {babel = require("@babel/core");} catch (err) {if (err.code === "MODULE_NOT_FOUND") {err.message +="\n babel-loader@8 requires Babel 7.x (the package '@babel/core'). " +"If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'.";}throw err;}

babel-loader依赖了@babel/core,这就是安装babel-loader需要同时安装@babel/core(通常会再安装babel-preset-envbabel-plugin-transform-runtimebabel-runtime)的原因。我们接下去看,src/index.js整个文件是不是按照我们前面所讲编写Loader的方法来组织代码的。

//引入package.json
const pkg = require("../package.json");
/*
根据babel-loader是否配置cacheDirectory属性来告诉
babel-loader是否缓存loader的执行结果,如果true,
便会使用cache方法去实现,`cache.js`文件有着read、write、filename(文件命名方法)
以及如何处理缓存的handleCache方法(有则读,无则写再读),有兴趣可以去看看。
*/
const cache = require("./cache");
/*transfrom.js用来转换内容,内部调用了babel.transform方法进行转换,这里简单介绍一下babel的原理:babylon将es6/es7代码解析成ast,babel-traverse对ast进行转译,得到新的ast,新的ast通过babel-generator转换成es5,核心方法在@babel/core/lib/transformation/index.js中的`runSync`方法,有兴趣可以去了解一下。
*/
const transform = require("./transform");
const injectCaller = require("./injectCaller");
const path = require("path");
// 获取Loader参数options
const loaderUtils = require("loader-utils");module.exports = makeLoader();
module.exports.custom = makeLoader;function makeLoader(callback) {const overrides = callback ? callback(babel) : undefined;return function(source, inputSourceMap) {// 上面介绍过的api可以得知,这是个异步Loader,做的是异步装换的工作const callback = this.async();loader.call(this, source, inputSourceMap, overrides).then(args => callback(null, ...args), err => callback(err));};
}async function loader(source, inputSourceMap, overrides) {....
}

可以看到确实和我们Loader编写方式是一样的,通过module.exports = makeLoader();导出一个函数,makeLoader()是一个高阶函数,又返回了一个函数,通过const callback = this.async();可以知道,这是一个异步的loader,不难看出最重要的实现都在这一步函数loader里面了,那么到底在loader函数里面究竟做了些什么呢?我们来看看,在阅读源码前,最好先看看babel-loader的README,先做个基本了解.

上面代码可以看出loader(source, inputSourceMap, overrides)函数入参有三个,分别是source=>待转换的codeinputSourceMap=>上一个loader处理后的sourceMap,有的话overrides=>自定义加载器,整块源码可以分成几部分,

  • let loaderOptions = loaderUtils.getOptions(this) || {};,获取options,并且获取当前处理转换的文件的路径this.resourcePath
  • 判断是否自定义加载器转换,这里会进行一系列对options.customize进行判断,options.customize一个相对路径,loader函数参数overrides为空时起效,执行let override = require(loaderOptions.customize);,有了override之后,后续逻辑(如转换、获取option)override都会进行介入处理。
  • 将函数传入参数和LoaderOptions归并,得到programmaticOptions。
  • 调用babel.loadPartialConfig可以拿到babel配置并赋值给config变量,其实就是为了允许系统轻松操作和验证用户的配置,此功能解决了插件和预设
  • 生成cacheIdentifier
  • 判断options.cacheDirectory是否需要缓存Loader转换内容,如为true,调用cache.js的module.export Cache方法(上面已做介绍)
  • config.babelrc不为空,则有.babelrc文件,依赖.babelrc文件变化,使用this.addDependency(config.babelrc);
  • metadataSubscribers 订阅元数据,主要作用是订阅一些编译过程中的一些元数据,订阅以后这些元数据将会被添加 到webpack的上下文中。通常我们是用不上的,估计在某些babel-plugin中可能会使用到。
  • 最后将处理后的结果返回

小结

每一个Loader其实返回值就是一个Function,而且就是把带转换内容传入,得到转换后的内容,做的事情就是这样,这篇文章先对Loader的基本概念进行介绍,并且了解webpack为Loader的编写提供一些常用的API,最后通过简析babel-loader的源码,我觉得应该差不多知道如何去写一个简单的Loader了,文章原文地址我的博客。

转载于:https://www.cnblogs.com/wuxiaobin/p/10510784.html

Webpack学习-Loader相关推荐

  1. 什么是loader? (webpack学习篇4)

    什么是loader ? 官方把它定义为:用来将一段代码转换成另一段代码的webpack插件. 同时也给出了解释:虽然本质上说,loader也是插件,但因为webpack的体系中还有一个专门的名词就叫插 ...

  2. webpack学习之路

    webpack学习之路 当自己在学习webpack的时候,在网上发现中文的很详细的教程很少,于是便想将自己学习webpack的笔记记录整理下来,便有了这篇文章,希望对大家有所帮助,如果有错误,欢迎大家 ...

  3. 前端模块化工具--webpack学习心得

    话说前头 webpack前段时间有听说一下,现在已经到了3.x的版本,自己没去接触.因为之前使用gulp来作为自己的项目构建工具.现在感觉gulp使用的趋势在减少.现在这段时间去接触了webpack, ...

  4. webpack学习笔记1

    webpack学习笔记1:基本概念 前言: 现在在日常的开发中,webpack已经是必不可少的东西了,现在的需求基本都是用webpack对资源进行打包整合,所以打算写一点关于webpack的东西,这是 ...

  5. webpack学习(四) -- css tree shaking

    css tree shaking 我们和webpack(三)一样,用案例说话 安装css需要的两个loader,css-loader 和style-loader npm install css-loa ...

  6. vue-cli Webpack之Loader原理及自定义Loader

    文章目录 1. Loader 分类与执行顺序 1.1 Loader 分类 1.2 Loader 执行顺序 1.3 使用Loader方式 1.3.1 内联 Loader 2. 开发 Loader 步骤 ...

  7. webpack学习之 style-loader / css-loader

    继续我们上一篇的webpack学习,现在我们进一步学习 其他的配置 css-loader:用于让webpack加载这个css文件 安装:npm install css-loader -D / npm ...

  8. webpack学习之2.自动编译、实时重载LiveReload、热替换HMR

    代码沿用webpack学习之1.基础配置 每次要编译代码时,手动运行 npm run build 就会变得很麻烦. webpack 中有几个不同的方式,可以在代码发生变化后自动编译代码: webpac ...

  9. webpack学习:性能优化

    本文内容如下 性能优化相关内容 如果你都有了答案,可以忽略本文章,或去webpack学习导图寻找更多答案 性能优化两大方面 一,开发环境性能优化 优化: 构建速度,代码调试 HMR热模块更新(代码调试 ...

最新文章

  1. oracle 用户 表空间绑定,ORACLE表空间绑定用户操作流程
  2. linux mysql 5.7 双机热备_2017年5月5日 星红桉liunx动手实践mysql 主主双机热备
  3. FragmentStack
  4. dft + fft(模板)
  5. tornado框架基础11-tornado异步
  6. windows apche php mysql zend_Windows XP上安装配置 Apache+PHP+Mysql+Zend
  7. Opencv--从CalibrateCamera到SolvePnp(一)
  8. 华为机试HJ12:字符串反转
  9. 队列的基本操作c语言代码大全,数据结构――队列(循环队列)的基本操作(实现链队列逐一取出c语言代码)...
  10. 大数据安全的需求有哪些
  11. 广数系统加工中心编程_数控铣床与数控加工中心操作与编程
  12. mapgis新手教程(非常适合新手参考)
  13. SetLocale和GetLocale函数
  14. (附源码)计算机毕业设计SSM基于Yigo平台库房管理系统
  15. SQL学习(四)Where语句中的各种匹配方式
  16. 计算机的色彩在哪调整w10,win10电脑色彩太冷怎么调节屏幕色彩
  17. TeamTalk 详细介绍
  18. 什么是软链接, 什么是硬链接
  19. Scheduler (Long-term,Short-term, Medium-term Scheduler) Dispatcher
  20. 适合计算机专业的电脑配置,设计师电脑配置,真的要好好看清单

热门文章

  1. c语言c2182是什么错误,C语言中一种更优雅的异常处理机制
  2. go 向buff写入一个字节_我在 Go 中犯的 5 个错误
  3. 在线提交信息 程序 php,在线短消息收发的程序,不用数据库_php基础_脚本
  4. yii2.0使用ueditior完成上传单张,多张图片,上传视频等操作
  5. python【数据结构与算法】六度空间
  6. 【网页前端设计Front end】CSS入门(看不懂你来打我)
  7. python【Opencv计算机视觉库】opencv模块cv2常用函数用法(全)
  8. 爬虫python课程价位,用python编写的抓京东商品价格的爬虫
  9. 基于matlab的fisher线性判别及感知器判别_Deep Domain Adaptation论文集(一):基于label迁移知识...
  10. python get score gain_什么是“熵和信息增益”?(What is “entropy and information gain”?)...