Node.js 模块系统学习
本文主要学习 Node.js 中最常用的模块系统,Node.js 具有两种模块系统:CommonJS 模块和 ECMAScript 模块。
本文是为了将commonjs/module模块类型,进行学习并理解分析,将各自的区别、写法、选择进行说明
梳理本人学习目标
Node.js 模块系统介绍
Node.js 模块系统就是对一个js文件的编写及使用的方式,通过 CommonJS 模块的 import/export与 ECMAScript 模块 require/module.exports,为js文件提供了对外沟通的窗口。
为什么使用模块系统
整理了下方四个常用场景,明确模块系统的重要性
- • 1、项目内你想将公用函数抽离,减少维护难度,你需要模块系统导入导出
- • 2、项目内你想处理作用域污染问题,防止重复变量、函数问题,解决类似css编写时的样式污染问题,你需要模块系统作用域隔离
- • 3、项目内你想使用其他人开源的第三方依赖或插件,你需要模块系统导入导出
- • 4、项目内你理解的路由懒加载,组件库按需加载,此类问题你需要模块系统按需加载模块(js文件)
如何选择合适的模块类型
首先查看下方这句最明显的模块类型区别
CommonJS 模块的 import/export与 ECMAScript 模块 require/module.exports
我认为你第一选择要素是,你习惯哪种方式
你是从vue/react过来的,我建议使用 CommonJS 模块,你会比较熟悉上手
你是从微信小程序过来的,我建议使用 ECMAScript 模块,你会比较顺手
OK以上你就理解了最简单的选型,二者区别可参考下方表格,自行查看,为你深层考虑提供点帮助
| | |
| | ECMAScript 官方模块标准(JS 语言标准) |
| | 参考下方 ECMAScript 模块 的导入导出部分 |
| 运行时加载(同步),脚本执行到哪加载到哪,适合服务端这类文件全部离线在本地磁盘情况 | 编译时加载(静态解析),先解析依赖再执行,适合客户端这类文件 |
| | |
| 设计于服务器端,追求的是简单直接。它像执行函数一样运行脚本并拿走结果。 | 设计于浏览器标准,追求的是静态分析和性能优化。动态绑定允许浏览器在不下载整个脚本的情况下,先解析出模块间的依赖图谱,这对于前端的 Tree Shaking 和异步加载至关重要。 |
如何区分项目使用的模块系统是什么
最直接的区分方式为,查看项目内的package.json文件内"type": "xxxxx"字段值
- 1. "type": "module"为 ECMAScript 模块系统开发
- 2. "type": "commonjs"为 CommonJS 模块系统开发
另一种方式就是打开js文件,查看文件的函数导出方式
- 1. exports则为 ECMAScript 模块系统开发
- 2. export则为 CommonJS 模块系统开发
创建自定义模块
- • 1、创建自定义模块就是你创建一个新的js\ts\mjs\cjs文件
- • 3、编写后在文件末尾,将此文件你需要提供给其他js模块或html使用的函数或变量等
CommonJS 模块
CommonJS 模块是为 Node.js 打包 JavaScript 代码的原始方式。
在 Node.js 中,每个文件都被视为一个独立的模块。
以上是官网的解释
说人话就是,nodejs在原始js基础上开发了一套自己的js文件的编写及使用的方式
可以使用前端思想理解,CommonJS与ES Modules的关系类似于 js与ts的区别
导入导出
CommonJS 是 Node.js 早期经典的模块规范。
导出内容使用 module.exports:
functionadd(a, b) {return a + b}functionsubtract(a, b) {return a - b}module.exports = { add, subtract}
导入内容使用 require:
const math = require('./math')console.log(math.add(2, 3))console.log(math.subtract(10, 4))
需要注意:
- •
require('./math') 中的 ./ 表示当前目录下的本地模块。 - • 如果要一次性导出对象、函数或类,优先使用
module.exports = ...。 - •
exports.name = ... 可以添加属性,但不要写成 exports = ...,因为这样会断开它和 module.exports 的引用关系。
commonjs模块缓存机制
当一个模块被 require 时,它会执行模块脚本并导出一个值的浅拷贝。一旦输出一个值,模块内部的变化就影响不到外部,外部的修改也不会影响内部。
ECMAScript 模块
ECMAScript 模块是是现代 JavaScript 的编写标准。
也可以理解为,非特殊情况(旧项目使用的commonjs),编码全部使用module模块类型,他更通用,适用范围对前端更友好
导入导出
- 1. 相对路径导入
import {a} from "../../a.js" - 2. 绝对路径导入
import {a} from "C:/Users/lee/Desktop/code/a.js" - 3. 裸路径导入
import {get} from "axios" - 4. 模块导入
import "../../lee.css" - 5. 动态导入
import("../../response.json").then(res=>{}) 或 const {code,data,msg} = await import("../../response.json");
- 1. 导出变量
export let a = 1; 或 export function getA (){}; 或 export class GetA {} - 2. 导出列表
export {a,b,c} 或 export {a,b,c} from "../../a.js" - 4. 导出全部
export * from "../../a.js"
module模块缓存机制
ESM 对模块的引用是“值的引用”而非“值的拷贝”,这在某些复杂循环引用场景下与 CJS 表现不同。
import 导入的是值的内存地址。模块不会缓存导出的具体值,而是建立一个连接。如果模块内部的值改变了,外部访问时拿到的也是最新的值。
项目混合使用
如果想再项目内混合使用CommonJS模块与ES Modules,建议通过文件名后缀来区分,会好区分一些
- • 1、在 type: module 下,如果你非要写 CJS 模块,可以将后缀改为 .cjs。
- • 2、在 type: commonjs 下,如果你要写 ESM 模块,可以将后缀改为 .mjs。
参考文档
commonjs模块https://node.org.cn/docs/latest/api/modules.html
ecmascript模块https://node.org.cn/docs/latest/api/esm.html
export说明https://mdn.org.cn/en-US/docs/Web/JavaScript/Reference/Statements/export
结语
有些词只不过偏专业性术语会导致理解困难,但在我们的实际开发中,其实都是我们的肌肉记忆了,所以不要被专业术语给骗了
不要觉得这玩意有多高大上,就像自定义模块实际就是我们开发中理解的函数公用、提取,以自己的概念理解才能更好的使用
最经典的是你觉得词元,他爱叫啥叫啥,我说token是令牌他就是令牌,我说token是LLM的上下文计算量,他就是,不要别人觉得,你理解知道就行。