文章来自《深入浅出Webpack》吴浩麟著
工作原理概括
基本概念
- Entry:入口,Webpack执行构建的第一步将从Entry开始,可抽象成输入。
- Module:模块,在Webpack里一切皆模块,一个模块对应一个文件。Webpack会从配置的Entry开始,递归找出所有依赖的模块。
- Chunk,代码块,一个Chunk由多个模块组合而成,用于代码和分割。
- Loader,模块转换器,用于将模块的原内容按照需求转换成新内容。
- Plugin:扩展插件,在Webpack构建流程中的特定时机会广播对应的事件,插件可以监听这件事件的发生,在特定的时机做对应的事情。
流程概括
Webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程。
- 初始化参数:从配置文件和shell语句中读取与合并参数,得到最终参数。
- 开始编译:用上一步参数初始化Complier对象,加载所有配置的插件,通过执行对象的run方法开始执行编译
- 确定入口:根据配置中的entry找出所有入口文件。
- 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
- 完成模板编译:在经过第4步使用loader翻译完成所有模块后,得到了每个模块被翻译后的最终内容及它们之间的依赖关系。
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再将每个Chunk转换成一个单独的文件加入输出列表中,这是可以修改输出内容的最后机会。
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,将文件的内容写入文件系统中。
流程细节
- 初始化:启动构建,读取与合并配置参数,加载Plugin,实例化Compiler。
- 编译:从Entry发出,针对每个Module串行调用对应的Loader去翻译文件的内容,再找到该Module依赖的Module,递归地进行编译处理。
- 输出:将编译后的Module组合成Chunk,将Chunk转换成文件,输出到文件系统中。
如果只执行一次构建,会按照以上流程顺序执行,但在开启监听模式,流程会在输出的地方在到初始化后面、编译的前面,再循环执行。
编写Loader
Loader就像一个翻译员,能将源文件经过转化后输出新的结果,并且一个文件还可以链式地经过多个翻译员翻译。
Loader的职责
一个Loader的职责是单一的,只需要完成一种转换,如果一个源文件需要经历多步转换才能正常使用,就通过多个Loader去转换。在开发一个Loader时,请保持其职责的单一性,我们只需关注输入与输出。
webpack是运行Node.js上的,一个Loader其实就是一个Node.js模块,这个模块需要导出一个函数。这个导出的函数的工作就是获得处理前的原内容,对原内容执行处理后,返回处理后的内容。
一个简单的Loader的源码如下:
module.exports=function(source){
return source;
};
由于Loader运行在Node.js中,所以我们可以调用任意Node.js自带的API,或者安装第三方模块进行调用:
const sass=require(‘node-sass’);
module.exports=function(source){
return sass(source);
};
Loader有同步和异步之分,同步的方式,网络请求会阻塞整个构建,导致构建非常缓慢。如果是异步转换,则我们可以这样做:
module.exports=function(source){
var callback=this.async();
someAsyncOperation(source,function(err,result,sourceMaps,ast){
callback(err,result,sourceMaps,ast);
});
};
在某些情况下,有些转换操作需要大量的计算,非常耗时,如果每次构建都重新执行重复的转换操作,则构建将会变得非常缓慢。为此,webpack会默认缓存所有Loader的处理结果,也就是说在需要被处理的文件或者其依赖文件没有发生变化时,是不会重新调用对应的Loader去执行转换操作的。
如果不想让webpack缓存该Loader的处理结果,则可以这样:
module.exports=function(source){
this.cacheable(false);
return source;
};