上两篇文章分析了webpack整体打包流程(juejin.cn/post/700202… )和loader触发时机及执行过程(juejin.cn/post/700239… ),这篇文章来分析下webpack是如何处理模块的。
我们都知道webpack会把一切前端资源都当成模块来处理,然而不同的模块化规范(AMD,CMD,commonJS,UMD,es6)及不同的引入方式(路径、别名等),webpack是如何识别和处理的?
1. 什么是webpack模块
简单来说通过require、import、define和require、@import(css文件中)和url(...)引入的功能块都可以称为模块。
2. webpack是如何解析模块的
通过源码可以看到webpack是通过引入enhanced-resolve(增强版的require.resolve)来解析文件的。这个库会将 require或者import语句中引入的资源,解析为引入文件的绝对路径,然后通过文件读取加载资源。
以import为例,常见的引入方式有:
import x from 'xx'; // 模块引入
import x from './xx/xx' ;// 相对路径引入文件
import x from './xx'; // 相对路径引入文件夹
import x from '@xx/xx'; // 别名引入
想要弄清楚enhanced-resolve,最好先了解下node的模块解析。enhanced-resolves在这个基础上主要完善了别名和高度支持可配置化,通过tapable将整个流程串起来。
3. webpack解析规则是什么
解析规则是通过路径区分的,不同的引入方式解析规则也不同,解析流程图如下:
- 绝对路径:已经是文件的绝对路径,不需要解析,不过项目一般很少这么写。
- 相对路径:在这种情况下,使用
import
或require
的资源文件所处的目录,被认为是上下文目录。 - 模块路径:在
resolve.modules
中指定的所有目录检索模块。 也可以通过配置别名的方式来替换初始模块路径,具体请参照resolve.alias
配置选项。 - 别名路径:在
resolve.alias
指定别名对应的文件路径。
resolve: {
alias: {
'@': path.resolve(__dirname, "src"),
}
}
通过上述规则解析路径后,resolver将会检查路径是指向文件还是文件夹
(1)路径为文件:如果文件具有扩展名,可以直接加载文件。没有扩展名则将使用 resolve.extensions
选项作为文件扩展名来解析。
(2)路径为文件夹:
- 如果有
package.json
文件,在当前目录下的node_modules查找,找不到继续查找上一层,直到根目录。找到后根据resolve.exportsFields
配置选项中指定的字段会依次查找,常见的main或module字段就可以找到模块的入口文件 - 如果没有package.json,会查找是否有index.js
为了容易理解,举个常用的例子,例如项目中引入vue(import vue from 'vue')。
由于vue对多种引入方式以不同文件提供支持,包括符合CommonJS模块规范的 vue.runtime.common.js
、符合ES Module模块规范的vue.runtime.esm.js
和符合UMD模块规范的 vue.js
以及支持TypeScript的类型定义文件index.d.ts
。
webpack如何找到需要的vue.js版本?
根据解析规则,属于模块路径,去当前目录下的node_modules查找。该路径是一个文件夹,会根据package.json中的配置(main、module)找到对应的文件进行加载。
我们的项目使用webpack打包各个模块,没有指定,webpack优先会选取ES模块方式引入包,因此会使用module对应的文件.
4.源码验证
通过clone单独的enhanced-resolve库,在根目录创建一个js文件,内容如下:
执行该代码,得到结果:
结论
:enhanced-resolve会把不同的模块规范和引入路径方式统一解析为文件的绝对路径。
通过本文相信会对webpack模块解析有了更深的理解,感谢点赞支持哟~