分析Webpack中的require.context原理

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/snsHL9db69ccu1aIKl9r/article/details/96401691

640?wx_fmt=png

作者 | 二哲

来源 | web前端开发(web_qdkf)


前言

require.context 其实是一个非常实用的 api。但是 3-4 年过去了,却依旧还有很多人不知道如何使用。


而这个 api 主要为我们做什么样的事情?它可以帮助我们动态加载我们想要的文件,非常灵活和强大(可递归目录)。可以做 import 做不到的事情。今天就带大家一起来分析一下,webpack 的 require.context是如何实现的。

准备工作

在分析这个 api 之前呢,我们需要先了解一下一个最简单的文件,webpack 会编译成啥样。


编译之后,我们可以看见 webpack 会编译成如下代码



初次一看是很乱的,所以为了梳理结构,我帮大家去除一些跟本文无关紧要的。其实主要结构就是这样而已,代码不多为了之后的理解,一定要仔细看下每一行



__webpack_require__ 就是一个模块加载器,而我们所有的模块都会以对象的形式被读取加载



我们把这样的结构先暂时称之为 模块结构对象


正片


了解了主体结构之后我们就可以写一段require.context来看看效果。我们先新增 2 个 ts 文件并且修改一下我们的 index.ts,以便于测试我们的动态加载。




查看我们编译后的源码,发现多了这样一块的 模块结构对象



我在源码中写了很详细的注释。看完这段代码就不难理解文档中所说的require.context 会返回一个带有 3 个API的函数(webpackContext)了。


640?wx_fmt=png


接着我们看看编译后 index.ts 的源码



很简单,可以发现 require.context 编译为了 __webpack_require__加载器并且加载了 id 为./src/demos sync recursive \\.ts 的模块,sync表明我们是同步加载这些模块(之后我们在介绍这个参数),recursive 表示需要递归目录查找。自此,我们就完全能明白 webpack 是如何构建所有模块并且动态加载的了。


进阶深入探究 webpack 源码


我们知道 webpack 在 2.6 版本后,在加载模块时,可以指定 webpackMode 模块加载模式,我们能使用几种方式来控制我们要加载的模块。常用的 mode一般为sync lazy lazy-once eager


所以在 require.context 是一样适用的,我们如果查看一下@types/webpack-env就不难发现它还有第四个参数。


640?wx_fmt=png


简要来说


  • sync 直接打包到当前文件,同步加载并执行

  • lazy 延迟加载会分离出单独的 chunk 文件

  • lazy-once 延迟加载会分离出单独的 chunk 文件,加载过下次再加载直接读取内存里的代码。

  • eager 不会分离出单独的 chunk 文件,但是会返回 promise,只有调用了 promise 才会执行代码,可以理解为先加载了代码,但是我们可以控制延迟执行这部分代码。


文档在这里 https://webpack.docschina.org/api/module-methods/#magic-comments。


这部分文档很隐晦,也可能是文档组没有跟上,所以如果我们去看 webpack 的源码的话,可以发现真正其实是有 6 种 mode。


mode类型定义

https://github.com/webpack/webpack/blob/master/lib/ContextModule.js#L13


那 webpack 到底是如何做到可递归获取我们的文件呢?在刚刚上面的源码地址里我们能发现这样一行代码。


640?wx_fmt=png


这一看就是去寻找我们所需要的模块。所以我们跟着这行查找具体的源码。


640?wx_fmt=png


这就是 require.context 是如何加载到我们文件的具体逻辑了。其实就是fs.readdir而已。最后获取到文件之后在通过 context 加载器来生成我们的模块结构对象。比如这样的代码就是负责生成我们sync类型的context加载器。大家可以具体在看别的5种类型。


640?wx_fmt=png


6种类型加载逻辑并且生成 context 加载器的模块结构对象

https://github.com/webpack/webpack/blob/master/lib/ContextModule.js


总结


1.学习了解 webpack 是如何组织加载一个模块的,webpack 的加载器如何运作,最后如何生成编译后的代码。


2.本来仅仅只是想了解 require.context 如何实现的,却发现了它第三个参数有 6 种mode,这部分却也是 webpack 文档上没有的。


3.从一个实用的 API 出发,探索了该 api 的实现原理,并且一起阅读了部分 webpack 源码。


4.学会一个小小的 API 使用很简单,但是如果你去探寻本质,可以发现许多边边角角有意思的东西。探索本质远比你成为 API 的搬运工更重要。


最后留个作业,大家可以按照这样的思路再去学习另外 6 种 mode 编译后的代码。


文章里编译后的代码,都在这里 >>> https://github.com/MeCKodo/require-context-sourece



640?wx_fmt=jpeg

640?wx_fmt=jpeg

展开阅读全文

没有更多推荐了,返回首页