webpack 源码阅读(二):webpack 打包基本原理和过程详解

7 篇文章 0 订阅
3 篇文章 1 订阅

上一篇文章讲了webapck命令启动的过程。本文将讲解webpack启动后的模块化打包的主要的过程:
文章将带着如下几个问题去阅读源代码:
1.如何初始化内置 plugins?
2.如何初始化自定义配置 plugins?
4.loader 是如何工作的?
5.如何构建一个模块?

这里需要提醒,如果你对tapable的机制还不熟悉,查看tapable
如果你也不知道webpack启动执行机制,查看webpack启动
本文大量都是在断点调试代码,如果想看懂,可以跟着文章中的文件地址和行号打断点来做

上一篇我们探索到compiler执行run 操作。从compiler 开始,webpack正式开始了它的模块化打包之路。
本地的测试使用的配置

const Dashboard = require('webpack-dashboard');
const DashboardPlugin = require('webpack-dashboard/plugin');
const dashboard = new Dashboard();
module.exports = {
    entry: './index.js',
    module: {
        rules: [
          { test: /\.txt$/, use: 'raw-loader' },
          { test: /\.json$/, use: 'json-loader' }
        ]
    },
    plugins:[
        new DashboardPlugin(dashboard.setData)
    ]
};

本地测试使用的index入口文件

import { add } from './test'
import aa from './test1.txt'
const json = require('./file.json');

const a = add(1,2, aa)
console.log(a, aa)

一:总结下大的执行路线。

上篇,我们讲到启动后,执行compiler.run,执行后 做了哪些事情呢?可以总结为:

  1. 调用this.compile 完成对 当前原始代码的编译打包 (打包)
  2. 调用this.emitAsserts 将source 写入到dist 目录(输出)
  3. plugin穿插在前两个过程中,响应不同的钩子函数来完成自己的逻辑。

打包和输出的两个大的步骤入口,如下图所示:
代码地址:lib/Compiler.js
在这里插入图片描述
代码地址:lib/Compiler.js
在这里插入图片描述

二:接下来我们来深入this.compile 来梳理下 打包的主要流程

compiler.run 后,执行了 this.compile函数
this.compile的代码:
代码地址:lib/Compiler.js
e
compile方法主要做了两件事情:

  1. 创建编译对象
    compilation 是webpack里的非常重要的一个对象。
    执行 newCompilation 的时候,会注入大量的内置plugins(如下图),在后续的代码中,webpack会在Compiler的hooks下 或者 compliation下的hooks上初始化很多plugins。compilation是Compiler下更细颗粒度的编译执行类。
    代码地址:lib/Compiler.js
    在这里插入图片描述
    如 FunctionModuleTemplatePlugin:

代码地址:lib/FunctionModulePlugin.js
在这里插入图片描述
上图的plugin注入的过程其实就是在往compilation的hooks上通过tappable机制来挂在监听函数,这些监听函数响应整条流水线的不同阶段的消息,对source源码的进行了不同类型的处理,如添加模块化语法,添加备注,或者单纯监听来做报表和进度消息之类的,如下图红框里:
代码地址:lib/FunctionModuleTemplatePlugin.js
在这里插入图片描述
同样的过程,一次注入如下这些plugin,他们各司其职,互相配合,通过他们将针对源代码进行的各种包装的过程挂载到compilation主流程的各个关口中去。后续我们对选择性的针对一些plugin进行细致的讲解

FunctionModulePlugin,
NodeSourcePlugin,
LoaderTargetPlugin,
JavascriptModulesPlugin,
JsonModulesPlugin,
WebAssemblyModulesPlugin,
SingleEntryPlugin,
CompatibilityPlugin,
HarmonyModulesPlugin,
AMDPlugin,
RequireJsStuffPlugin,
CommonJsPlugin,
LoaderPlugin,
LoaderPlugin,
NodeStuffPlugin,
CommonJsStuffPlugin,
APIPlugin,
ConstPlugin,
UseStrictPlugin,
RequireIncludePlugin,
RequireEnsurePlugin,
RequireContextPlugin,
ImportPlugin,
SystemPlugin,
EnsureChunkConditionsPlugin,
RemoveParentModulesPlugin,
RemoveEmptyChunksPlugin,
MergeDuplicateChunksPlugin,
FlagIncludedChunksPlugin,
SideEffectsFlagPlugin,
FlagDependencyExportsPlugin,
FlagDependencyUsagePlugin,
ModuleConcatenationPlugin,
NoEmitOnErrorsPlugin,
WasmFinalizeExportsPlugin,
OccurrenceOrderModuleIdsPlugin,
OccurrenceOrderChunkIdsPlugin,
DefinePlugin,
TerserPlugin,
TemplatedPathPlugin,
RecordIdsPlugin,
WarnCaseSensitiveModulesPlugin
    

那么webpack是在何时注入自定义配置的 plugins的呢?

我们在自己的测试代码的webpack配置文件里,引入一个plugins的配置。
在这里插入图片描述
调试模式执行后,我们可以在webpack文件下看到下图初始化自定义plugin的地方。
代码地址:lib/webpack.js
webpack plugins 加载
plugin apply方法会将plugin中的一些监听函数挂载到compiler的hooks上。这些监听函数会在compiler的不同时机被触发。 如下图所示,这个dashboard-plugin 就在apply的时候,监听了compiler的:watch-run run compile 等钩子函数,在这些函数出发的时候,记录了一些统计信息用来提供用户打包数据仪表盘可视化显示的能力。

代码地址:安装的 DashBoardPlugin 模块
在这里插入图片描述

我们回到刚刚的compilation的创建流程。注意在下图 670 行 的newCompliation调用前,Compiler对象下的hooks.compile.call执行了(668行),我们可以看到这个时候挂载的我们上面自定义plugin的webpack-dashboard 就被触发了。因为我们自定义plugin是监控了compile的hook的。
代码地址:lib/Compiler.js
在这里插入图片描述
2. 找入口
接着往下,执行this.hooks.make,make上挂载着 SingleEntryPlugin。

代码地址:lib/Compiler.js
在这里插入图片描述
SingleEntryPlugin 中监听make的函数,当make触发的时候(上图672行)干了两件事情

  1. 创建依赖对象dep
  2. 执行compliation的addEntry方法
    代码地址:lib/SingleEntryPlugin.js在这里插入图片描述
    addEntry方法又调用了_addModuleChain方法
    代码地址:lib/Compilation.js
    在这里插入图片描述
    _addModuleChain 方法里重点做的事情是,按照依赖属性找到了NormalModuleFactory,并创建其实例,调用其create方法,如下图:

代码地址:lib/Compilation.js
在这里插入图片描述
create 方法调用自己的factory钩子,我们可以看到在钩子上之前已经apply了一个监听函数
代码地址:lib/NormalModuleFactory.js
在这里插入图片描述

上图的 call 执行后会触发下图的tap挂在的观察函数123行对应的函数,
代码地址:lib/NormalModuleFactory.js
在这里插入图片描述

继续执行到124行,this.hooks.resolver.call(null); 会触发159行对应的挂载函数执行
代码地址:lib/NormalModuleFactory.js
在这里插入图片描述
该函数里重要的的270行-279行,在解析webpack.config.js 里的module 中的rules里关于loader的配置
代码地址:lib/NormalModuleFactory.js
在这里插入图片描述
270行的 ruleSet 是 lib/RuleSet.js里的方法,通过一大堆的参数以及递归的处理,把loader的配置梳理出来

代码地址:lib/RuleSet.js
在这里插入图片描述
下图这是转换出来的结果,如下图的 raw-loader
代码地址:lib/NormalModuleFactory.js
在这里插入图片描述

resover执行完后,就触发回调callback,回掉函数里传入了各种揉好的loader配置,依赖的配置
代码地址:lib/NormalModuleFactory.js
在这里插入图片描述
继续执行到155 的回调函数
代码地址:lib/NormalModuleFactory.js
在这里插入图片描述
再继续执行 409的回调,执行到下图lib/Compilation.js 981 行,终于开始build了,
代码地址:lib/Compilation.js
在这里插入图片描述
执行到Compilation.js 的buildModule.js 方法
代码地址:lib/Compilation.js
在这里插入图片描述
739行继续执行 module.build,进入到NormalModule 的build函数,继续执行446行的 doBuild 方法
代码地址:lib/NormalModule.js
在这里插入图片描述
执行到下图 295的runLoaders方法
代码地址:lib/NormalModule.js
在这里插入图片描述

runLoader 是一个独立于webpack的包,loader-runner。重点关注下图365行的这个iteratePitchingLoaders函数
代码地址:安装webpack时候 webpack的内部依赖包 loader-runner
在这里插入图片描述
下图的174航对应的是loader的函数,继续执行178行的runSyncOrAsync方法
在这里插入图片描述

可以看到下图 fn.apply,就是在执行rawLoader函数,并传入source

在这里插入图片描述
可以大致感受下,他做了哪些事情,下图

  1. 对换行符号进行了转义替换
  2. 包裹上模块化代码
    代码地址:安装的row-loader的代码
    在这里插入图片描述

好,本文到这里就不继续往下展开了,通过各种plugin和loader的配合,webpack完成了对souce的处理和监控。
所以一个简单的打包可以理解为,webpack使用loader 对不同的source(源代码)进行处理封装。

在这里插入图片描述

总结,通过基本的了解我们可以看出webpack的大致工作流程:
webpack 先通过入口解析出各种依赖的文件,
在webpack处理的各个阶段,会有很多钩子,plugin就是监听不同的钩子来确定自己的执行实际
不同类型的文件由不同的loader处理
最终通过loader所有的资源都模块话了,整合成一个bundle,写入磁盘。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

森哥的歌

一杯咖啡半包烟,助我熬过那长夜

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值