【webpack系列】2. 从源码角度分析loader是如何触发和执行的

1,047 阅读3分钟

上一篇文章(juejin.cn/post/700202… )从打包结果分析了webpack核心执行流程,这篇文章详细说明laoder触发时机和执行过程,一次性解决loader所有困扰。

1. 为什么要配置loader

因为webpack只认识js和json,对于非js和json的文件类型需要loader转换为标准的js模块,才能被webpack打包处理。

2. loader本质是什么

loader本质上就是导出为函数的JavaScript模块。例如一个最简单的loader:

module.exports = function(content, map,meta) {
return `export default "Convert by my custom loader: ${content}"`;
// 注:当需要返回多个参数时,不能直接return,需要按照如下方式返回
// 异步loader
// this.async(null, result, map, meta)
// 同步loader
// this.callback(null, result, map, meta)
// 只有一个参数可以直接return
}

Webpack会对loader注入三个参数:

content:资源文件的内容。// 必须

map:前面loader生成的source map // 可选

meta:其他需要传给后方loader共享的信息,可自定义 // 可选

明白了这个本质,对于自己开发一个loader就非常简单了。例如:新建一个loaders目录,创建my-loader.js文件,内容为导出的javascript模块,拿到content就可以随意处理了。

模块解析默认从当前node_modules查找,如何找到自己开发的loader?只需要配置下解析规则即可

// 手动配置 loader 路径,先从自定义的loaders目录查找,找不到再查找node_modules
    resolveLoader: {
      modules: [path.resolve(__dirname, 'loaders'), 'node_modules']
    }

3. loader是什么时候触发的

上一篇(juejin.cn/post/700202… )我们了解了webpack整体打包流程,其中loader触发时机为标红的部分:

9fd11776832657deb5db2afa60e3f6f2.png

真正触发时机是调用module.build,build对应源码文件 webpack/lib/NormalModule.js的build方法,build方法又调用了doBuild,doBuild方法执行了runLoaders,如下图:

8762cd5ae9681a2c0b84d21d9f8a6ed0.png 其中runLoaders是一个单独的工具库(loader-runner),用来执行资源的加载和转换,具体如何执行的继续看第4个问题。

4. loader是如何执行的

了解loader执行流程前,需要知道loader函数包含pitch和normal两部分,其中pitch是非必须的,如果有pitch则会首先执行它,loader执行核心流程如下:

image.png

通过执行流程我们也知道了为什么loader按照从右向左的顺序执行,其实是因为loader执行是一个递归调用,判断当前为最后一个loader时才会处理资源。

iteratePitchingLoaders函数执行过程: image.png

processResource函数执行过程 image.png

iterateNormalLoaders函数执行过程: image.png

5. loader执行顺序是怎样的

在webpack配置文件中,我们经常看到两种场景:

场景1:同一个规则配置多条loader时是怎么样执行的?

执行优先级为:Pre loader > Inline loader > Normal loader > Post loader,可以通过enforce属性改变loader执行顺序。如果没有enforce则为相同优先级,按照配置顺序执行。

场景2:单条规则配置多个loader执行顺序?

配置中相同优先级会按照从右到左,从下到上的顺序执行,具体为什么是这个顺序通过问题4loader执行过程相信已经明白了。

总结:Loader实际的执行顺序与loader的类型pitch方法inline-loader的前缀都有关系。

通过以上5个问题,相信对于loader的所有疑问基本都解决了,码字画图不易感谢点赞支持哟~