摘要:用来转换内容,内部调用了方法进行转换,这里简单介绍一下的原理将代码解析成,对进行转译,得到新的,新的通过转换成,核心方法在中的方法,有兴趣可以去了解一下。将函数传入参数和归并,得到。通常我们是用不上的,估计在某些中可能会使用到。
什么是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的optionsconst loaderUtils = require("loader-utils"); module.exports = function(source) { // 获取用户为当前Loader传入的options console.log(loaderUtils.getOptions(this)); return source; }返回其他结果
如上,我们返回的是转换后的内容,但是有些情况下,我们不仅仅需要返回转换后的内容,还需要返回一些其他的内容,如sourceMap或是AST语法树,那么这时候我们可以使用webpack提供的APIthis.callback,当使用this.callback了,那么我们就必须需要在Loader函数返回undefined,以此来让webpack知道返回的结果在this.callback中,API详细参数如下:
this.callback( // 无法装换原内容时的Error err: Error || null, // 装换后的的内容,如上述的source content: string | Buffer, // 用于通过装换后的内容得出原内容的Source Map,方便调试 // 我们了解到,SourceMap我们只是会在开发环境去使用,于是就会变成可控制的, // webpack也提供了this.sourceMap去告诉是否需要使用sourceMap, // 当然也可以使用loader的option来做判断,如css-loader sourceMap?: 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。
babel-loader源码简析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: {...})
源码第一行如下:
let babel; try { babel = require("@babel/core"); } catch (err) { if (err.code === "MODULE_NOT_FOUND") { err.message += " 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-env、babel-plugin-transform-runtime、babel-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=>待转换的code,inputSourceMap=>上一个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.ucloud.cn/yun/109116.html
摘要:模块化原理简析的核心原理一切皆模块在中,,静态资源文件等都可以视作模块便于管理,利于重复利用按需加载进行代码分割,实现按需加载。模块化原理以为例,分析构建的模块化方式。 webpack模块化原理简析 1.webpack的核心原理 一切皆模块:在webpack中,css,html.js,静态资源文件等都可以视作模块;便于管理,利于重复利用; 按需加载:进行代码分割,实现按需加载。 2...
摘要:需要得到最后一个产生的处理结果。这个处理结果应该是或者被转换为一个,代表了模块的源码。另外还可以传递一个可选的结果格式为对象。在异步模式中,必须调用,来指示等待异步结果,它会返回回调函数,随后必须返回并且调用该回调函数。 准备工作 安装 Node.js, 建议安装LTS长期支持版本 mkdir webpack and cd webpack and npm init -y npm ...
摘要:本文首发于的技术博客实用至上,非经作者同意,请勿转载。只是最近学习生态,用起来转换之余,也不免碰到诸多用上的教程案例,因此便稍作学习。在当前的浏览器市场下,想在生产环境用上,是必不可少的。 本文首发于Array_Huang的技术博客——实用至上,非经作者同意,请勿转载。原文地址:https://segmentfault.com/a/1190000006992218如果您对本系列文章感兴...
摘要:三集成所需要的依赖和在或加载模块时,对代码进行预处理,语法转化为语法。到目前位置,用于开发应用的环境已经配置好了。 本系列主要学习webpack的配置。webpack自己间接的用过不少次,但是自己配置却没多少次,所以特地写写文章,学习webpack的配置,有不恰当的地方,欢迎指正。这次配置 babel 。 若你对webpack的概念还不了解,先查看相应文档webpack中文文档 一、初...
摘要:四用于对模块的源代码进行转换。对于图片等都不能识别,所有需要引入对应的对对应格式的文件进行转换以便来识别。支持链式调用,调用顺序由下到上,由右到左五插件目的在于解决无法实现的其他事。 一.entry entry是webpack打包的入口配置,entry对应的值可以是字符串,数组,对象;单入口可以使用字符串、数组、对象,多入口配置则必须使用对象的方式 二.output output是we...
阅读 2268·2021-11-23 09:51
阅读 5656·2021-09-22 15:39
阅读 3342·2021-09-02 15:15
阅读 3492·2019-08-30 15:54
阅读 2354·2019-08-30 15:53
阅读 1396·2019-08-30 14:04
阅读 2445·2019-08-29 18:33
阅读 2363·2019-08-29 13:08