require的使用场景
require的最终执行结果是在例如.js文件源码外层套一个自执行函数,这个函数的入参就有module, exports, dirname, filename, require等。自执行函数的返回结果为exports或modules.exports的值。
- 加载模块类型:
- 加载内置模块: require('fs')
- 加载node_modules模块: require('ejs')
- 加载本地模块: require('./utils')
- 支持文件类型:
- 加载.js文件
- 加载.mjs文件
- 加载.json文件
- 加载.node文件
- 加载其他类型文件
require源码阅读过程中的一些思考
CommonJs 加载主模块的流程 加载流程和require基本一致, 主要区别就是 isMain 为 true, parent 为 null
require 如何加载内置模块? 通过 loadNativeModule(filename, request, experimentalModules) 方法在内置模块的map集合中查找。找到就直接返回 mod.exports
require 如何加载node_modules模块 require('ejs') 和 require('./ejs/index.js) 加载的方法基本类似,只不过 require('ejs') 会先通过Module._resolveFileName解析node_modules中文件的真是路径
require 为什么会将非js/json/node 文件视为js文件加载? 非 js/json/node 文件会被添加.js文件后缀,最后通过Module._extensions[.js]执行加载流程
require 连续加载同一个模块时,是如何进行缓存的? 第一次加载的时候通过 Module._cache[filename] = module 将文件缓存下来,第二次加载的时候如果在缓存中找到这个模块,就直接返回 module.exports
Module对象
- id:源码文件路径,如:/Users/sam/Desktop/vue-test/imooc-test/bin/ejs/index.js
- path:源码文件对应的文件夹,通过 path.dirname(id) 生成
- exports:模块输出的内容,默认为 {}
- parent:父模块信息
- filename:源码文件路径
- loaded:是否已经加载完毕
- children:子模块对象集合
- paths:模块查询范围
Module核心API
- Module._resolveFileName : 返回文件的真实路径
- Module._nodeModulePaths :返回请求文件可能存在的所有路径
- Module._resolveLookUpPath : 将上一步返回的路径再拼装环境变量中的路径
- Module._findPath : 返回文件真实路径
- Module.prototype._compile :将.js文件外层包装一个自执行函数,并注入require、modules、exports、dirname、 filename等参数,返回执行结果。
- Module.prototype.load : 解析文件后缀名,执行对应方法