1. 模块化的演进
模块化特点的特点是什么? 防止变量污染
在以前的开发中,如果不用模块化,我们无法处理依赖关系,所以代码很难维护
那最早的时候我们是使用 单例 的方式解决,但是有个缺点,我们不能保证变量的唯一性,而且单例可能导致调用时浮躁,命名过长等问题, 如下方代码所示:
1 | const a = { |
当然,我们还有用到了 IFFE(立即执行函数)来处理这个问题
1 | (function(a){ |
再近点,就出现了一些解决模块化的第三方库,下面两种,在现在未前后端分离的项目(也就是 mvc 模式下 jquery 项目)还是会见到的。
- seajs库 cmd(就近依赖,只有在用到某个模块的时候再去require)
- requirejs库 amd(依赖前置, 在定义模块的时候就要声明其依赖的模块)
这里不多说了,感兴趣的可以百度了解下这两个库。
那当下 vue, react 框架流行下,我们是如何来处理模块化的呢?
结论是: umd(统一模块化),常见的是 es6 module(node 中无法使用) 和 commonjs 规范
如果想要在 node 中使用 es module,也就是 import 和 export,怎么办呢?
node 官网提供了一个 ECMAScript模块,它需要将文件后缀改成 .mjs,来支持 import 导入的方式,但是目前还是实验版。
还有一种方式,就是使用 babel-node 来转化 es6 模块。
1.1 commonjs 规范
特点是啥,往下看?
- 每个 js 文件都是一个模块
- 每个文件如果需要用到别的模块 require()
- 想把代码给别人使用 需要导出模块 module.exports
那文件之间是怎么隔离的?
其实很简单,就是给当前文件代码加了个闭包来隔离
1 | (function(){ |
2. fs path vm 等 node 模块
这里为什么单独介绍下这几个模块,因为这几个是核心模块,看过源码,你就知道, require 的实现就需要用到这几个模块。
- path
path 模块提供了一些实用工具,用于处理文件和目录的路径
具体可看:path 文档
- fs
fs 模块可用于与文件系统进行交互(以类似于标准 POSIX 函数的方式)。
具体可看:fs 文档
- vm
vm 模块可在 V8 虚拟机上下文中编译和运行代码。 vm 模块不是安全的机制。 不要使用它来运行不受信任的代码。
具体可看:vm 文档
vm 模块这里单独提下,
我们回忆下让字符串执行的方法有哪些?
- eval
1 | let a = 1; |
- new Function
1 | let strFn = new Function('console.log(a)') |
vue 模板引擎的实现原理就是 new Function + with
- vm
vm 沙箱,创造一个干净的执行上下文环境,不会向上查找
1 | vm.runInThisContext(str) |
3. require 实现过程
3.1 断点调试
这里简单提下如何断点调试,不方便展示,大家可以网上翻阅下文档
我用的 vscode, 需要配置下 launch.json 文件,下面是我的配置
1 | { |
然后运行 debug 模式,就可以查看源码,面板如下所示
3.2 require 执行过程
- 加载时 先看一下模块是否被缓存过 第一次没有缓存过
- Module._resolveFilename 解析出当前引用文件的绝对路径
- 是否是内置模块,不是就创建一个模块 模块有两个属性 一个叫 id = 文件名, exports = {}
- 将模块放到缓存中
- 加载这个文件 Module.load
- 拿到文件的扩展名 findLongestRegisteredExtension() 根据扩展名来调用对应的方法
- 会读取文件 差一个加一个自执行函数,将代码放入
3.3 手写 require 源码
1 | // a.js 文件 |
4. module.exports 与 exports 关系
exports 是 module.exports 一个简写
- 常用的导出方式
exports.xxx = xxx
module.exports
module.exports.a = xxx
global.a = xxx(可以,但不会用,全局污染)
exports = xxx(错误)
5. 模块查找方式
有几种模块:
- 内置模块 fs path vm
- 文件模块 自定义模块 ‘./‘
- 第三方模块 (bluebird….)必须安装才能使用, 用法和内置模块是一样的
方式:
- 先查找当前文件下的文件存不存在,不存在 添加 .js .json 后缀 找到后就结束
- 找不到后会找对应的文件夹,默认找索引文件,如果有package.json ,有这个文件,会查找 main对应的入口文件,去进行加载
- 按照包的方法查找 多个文件组成一个包 npm init -y
- 除了文件的查找方式 第三方模块的查找方式
1 | let r = require('xxx') // xxx表示的是第三方文件夹的名字,找到名字后会找package.json ,如果找不到向上找,找不到就报错。 |