发布网友 发布时间:2024-09-27 03:53
共1个回答
热心网友 时间:2024-10-04 14:22
建议边看文章边动手实践以加强理解
loader和plugin的区别两者的区别可以从下面几点分析:
概念
用法
执行顺序
如何开发loader和plugin
源码层面
概念上引用Webpack官网关于loader和plugin的解释:
loader。webpack自带的功能只能处理javaScript和JSON文件,loader让webpack能够去处理其他类型的文件,并将它们转换成有效的模块,以及被添加到依赖图中。
plugin。插件可以执行范围更广的任务,包括打包优化,资源管理,注入环境变量
loader的概念相对容易理解。插件的就比较拗口,其实插件就是暴露了webpack整个打包构建生命周期中的钩子给我们订阅,方便我们监听整个打包过程
用法上loader有两种方式使用loader:
配置文件
内联方式
配置文件。loader有两个属性:test属性,识别出哪些文件会被转换
use属性,定义出在进行转换时,应该使用哪个loader。
module.exports?=?{??output:?{????filename:?"my-first-webpack.bundle.js",??},??module:?{????rules:?[{?test:?/\.txt$/,?use:?"raw-loader"?}],??},};内联方式在每个import语句中显式指定loader
//?inline?loader一样可以传递options,通过?key=value&foo=bar这种方式import?Styles?from?"style-loader!css-loader?modules!./styles.css";plugin。只需要引入对应的plugin,然后在plugins数组中new一下即可
const?webpack?=?require("webpack");?//?访问内置的插件module.exports?=?{??plugins:?[new?webpack.ProgressPlugin()],};执行顺序pluginplugin的执行时机和webpack钩子或者其他插件的钩子有关,本质上利用的是Tapable定义的钩子。webpack提供了各种各样的钩子,可以看这里。因此如果想要熟练开发webpack插件,一定要对Tapable用法比较熟悉。我手写了Tapable所有的钩子,解读了Tapable的源码,并提供了使用Demo,具体可以看这里
loader默认情况下,loader按照我们在配置文件中配置的module.rules从下往上,从右到左依次执行。但是可以通过enforce以及inlineloader修改loader的执行顺序。
rules:?[??{????test:?/\.js$/,????use:?{??????loader:?"loader3",????},????enforce:?"pre",?//?enforce:?'post'??},];loader的分类按执行顺序,loader可以这么划分:
preLoader。enforce被设置成pre的loader
postLoader。enforce被设置成post的loader
normalloader。在配置文件中配置的并且没有设置enforce属性的普通loader
inlineloader。在import语句中使用的loader
loader的执行顺序默认情况下,loader按照我们在配置文件中配置的module.rules从下往上,从右到左依次执行。
实际上,loader会按照下面的顺序执行:
先执行preLoader
其次执行normalloader
然后执行inlineloader
最后执行postLoader
inlineloader的使用方式不同,也会改变loader的顺序,这又引入了新的复杂度,可以点击这里查看
如果inlineloader前面只有!号,则文件不会再通过配置的normalloader解析
import?Styles?from?"!style-loader!css-loader?modules!./styles.css";如果inlineloader前面有!!号,则表示文件不再通过其他loader处理,只经过inlineloader处理。
import?Styles?from?"!!style-loader!css-loader?modules!./styles.css";如果inline-loader前面有-!,则不会让文件再去通过preLoader以及normalloader解析,但还是会经过postLoader解析
import?Styles?from?"-!style-loader!css-loader?modules!./styles.css";执行顺序这块建议动手实践一下
如何开发loader和plugin如何开发loader
如何开发plugin
loader记住,loader只能是普通函数,不能是箭头函数,因为webpack在运行loader时,会往我们的loader中注入loaderContext上下文,可以点击这里查看。因此loader函数中的this是有意义的,不能使用箭头函数。
loader的组成loader包含两部分,pitchLoader和normalLoader,pitch和normal的执行顺序正好相反
当pitch没有定义或者没有返回值时,会先依次执行pitch在获取资源执行loader
如果定义的某个pitch有返回值则会跳过读取资源和自己的loader。假设有use:[loader1,loader2,loader3],三个loader都包含pitchloader和normalloader。
第一种情况,三个loader的pitchloader都没有返回值,那么执行顺序为:pitchloader3->pitchloader2->pitchloader1->获取资源->normalloader1->normalloader2->normalloader3
第二种情况,pitchloader有返回值,假设pitchloader2有返回值,则执行顺序为:pitchloader3->pitchloader2->noramlloader3
function?loader(source)?{??console.log("pitchLoader...",?source);}loader.pitch?=?function?()?{??console.log("pitch...");};module.exports?=?loader;建议动手实践方便比较pitchloader和normalloader的关系。目前我们用的style-loader就使用了pitchloader,具体可以查看我手写的style-loader
pluginplugin是一个类,其中必须实现一个apply方法,apply方法接收webpack的compiler对象,从中可以定义插件自己的钩子或者订阅其他插件的钩子
//?A?JavaScript?class.class?MyExampleWebpackPlugin?{??//?Define?`apply`?as?its?prototype?method?which?is?supplied?with?compiler?as?its?argument??apply(compiler)?{????//?Specify?the?event?hook?to?attach?to????compiler.hooks.emit.tapAsync(??????"MyExampleWebpackPlugin",??????(compilation,?callback)?=>?{????????console.log("This?is?an?example?plugin!");????????console.log(??????????"Here’s?the?`compilation`?object?which?represents?a?single?build?of?assets:",??????????compilation????????);????????//?Manipulate?the?build?using?the?plugin?API?provided?by?webpack????????compilation.addModule(/*?...?*/);????????callback();??????}????);??}}源码上从webpack调用loader以及plugin的时机简单介绍
loaderloader的调用在lib/NormalModule中。
webpack在打包我们的源码时,会从入口模块开始构建依赖(主要流程在Compilation.js中)。对每一个文件都会依次执行下面的顺序:
调用NormalModule.build()构建模块(一个文件对应一个NormalModule)
对每一个模块调用runLoaders执行模块匹配的loaders,获取经过loader处理后的模块源码
调用this.parser.parse()解析处理后的模块源码,提取模块依赖
对提取的模块依赖,再重复以上过程
可以看出,loader的执行在依赖解析之前完成
pluginplugin的调用时机就比较灵活。实际上webpack在整个生命周期都会调用相应的钩子。比如
在根据文件路径解析模块时,会调用相应的resolvers钩子。
假设有个需求,需要分析都有哪些文件引用了product.js这个文件,此时就可以使用resolvers钩子。
总结综上可以看出,虽然loader的分类,组成,用法比较多样,但是只要理解了这些差异,就能轻松的开发自己的loader。
plugin的组成,用法比较单一,但是如果要自己开发的话还是有难度的。因为需要理解webpack在整个生命周期过程中都暴露了哪些钩子,怎么结合自己的业务需求去使用对应的钩子。
一般来说,看到这里基本就已经熟悉了loader和plugin的区别。如果你不满足于此,可以关注我Github里面的mini-webpack。在这里我不仅手写了常见的loader,比如babel-loader,css-loader,file-loader,less-loader,style-loader以及url-loader。还原汁原味手写了webpack4的主流程源码。如果对源码感兴趣的朋友可以在仓库里面给我提issue一起讨论。
原文:https://juejin.cn/post/7098556679242907662