【webpack系列】3. webpack是如何解析模块的

1,598 阅读3分钟

上两篇文章分析了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解析规则是什么

解析规则是通过路径区分的,不同的引入方式解析规则也不同,解析流程图如下:

38be30f3eddcfb0e1d052c89aff24657.png

  • 绝对路径:已经是文件的绝对路径,不需要解析,不过项目一般很少这么写。
  • 相对路径:在这种情况下,使用 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)找到对应的文件进行加载。

image.png

我们的项目使用webpack打包各个模块,没有指定,webpack优先会选取ES模块方式引入包,因此会使用module对应的文件.

4.源码验证

通过clone单独的enhanced-resolve库,在根目录创建一个js文件,内容如下:

image.png 执行该代码,得到结果:

image.png

结论:enhanced-resolve会把不同的模块规范和引入路径方式统一解析为文件的绝对路径。

通过本文相信会对webpack模块解析有了更深的理解,感谢点赞支持哟~