NodeJS
模块
安装运行和环境配置就不说了,因为都很简单.
与python类似,也有内置模块和第三方模块.模块主要是一个代码复用方案,而且可以避免函数名和变量名冲突.
比如写这么一个hello.js
1 |
|
module.exports = greet;
: 把greet()
函数作为模块的输出暴露出去,这样子,其他模块就能使用greet()
了.
在main.js
中调用
1 |
|
const greet = require('./hello');
: 引入的模块作为变量保存在greet
中,其实变量greet
就是在hello.js
中用module.exports = greet;
导出的greet函数
.
引入模块时候,注意模块的相对路径,如果只写模块名,node会依次在内置模块,全局模块和当前模块下查找.如果找不多会报错
1 |
|
CommonJS规范
上面的这种模块加载机制称为CommonJS规范,在这种规范下,每个.js
文件都是一个模块,不同模块内部的变量名和函数名都不冲突.
一个模块想要对外暴露变量(函数也是变量),用module.exports = variable
想要引用其他模块暴露的变量,用var ref = require('module_name')
模块原理
当我们编写JS代码时,可以申明全局变量
1 |
|
但是,大量定义全局变量肯定不好,容易造成冲突.在ESM之前,a.js
和b.js
中如果定义了相同的全局变量,是会造成冲突的.
为了避免这个冲突,node.js利用了JS的闭包.如果我们把一段JS代码用一个函数包装起来,这段代码的所有全局
变量就会变成函数内部的局部
变量.
1 |
|
这样子就实现了变量的隔离.
模块的输出module.exports
实现靠的是一个对象module
1 |
|
变量module
是node在加载js文件前准备的一个变量,并将其传入加载函数.所以我们在hello.js
中可以直接使用变量module
.因为它实际上是函数的一个参数.
load()
函数把module
变量传递给了node执行环境,node把它保存在某个地方.
由于Node保存了所有导入的module
,当我们用require()
获取module时,Node找到对应的module,把这个module
的exports
变量返回,这样另一个模块就能拿到模块的输出.
不过这个,看看就行了,现代JS开发中都是用ESM.
ESM
Node.js一开始就支持模块,但那并不是JS原生的功能,只能算是一种模拟
.
ES6后,原生JS终于有了原生内置的模块支持,称为ECMAScript Modules(ESM),不仅可以直接在浏览器中使用模块,也可以在Node.js中使用ESM模块.
另外ES6后使用模块,会自动使用严格模式,所以并不需要显示写use strict;
普通导入导出
export
关键字标识需要导出的函数
1 |
|
将该文件保存为hello.mjs
,注意后缀是.mjs
,不过这个不是强制的.
在node.js中有三种方式使用ESM:
使用
.mjs
1
2
3
4
5
// math.mjs
export const add = (a, b) => a + b;
// main.mjs
import { add } from './math.mjs';在
package.json
中设置"type": "module"
1
2
3
4
5
6
7
8
9
10
11
// package.json
{
"type": "module"
}
// 然后就可以使用 .js 结尾
// math.js
export const add = (a, b) => a + b;
// main.js
import { add } from './math.js';如果在
package.json
中没有设置type
,就要使用.mjs
1
2
3
4
// 因为package.json中默认"type": "commonjs"
// 此时需要使用.mjs
// math.mjs
export const add = (a, b) => a + b;
再写一个main.mjs
来调用hello
模块
1 |
|
默认导入导出
当一个模块主要提供一个功能时,使用默认导入导出更方便
1 |
|
1 |
|
重命名导入
为了避免模块命名冲突,可以用as
重命名(别名)
1 |
|
浏览器加载ESM
浏览器也可以直接使用ESM
1 |
|
1 |
|
基本模块
列一下node.js常用基本模块
fs
: 文件系统模块,负责文件读写,同时提供了同步和异步的方法stream
: 提供流式数据的支持http
: 用JS编写web服务器程序crypto
: 提供通用的加密和哈希算法.这些功能如果用JS实现会非常慢,所以node.js用c/c++实现后,通过crypto这个模块暴露为JS接口,方便使用.
node.js项目
整理一下node项目常用命令和文件
项目初始化
1 |
|
依赖安装
1 |
|
项目运行相关
1 |
|
package.json
1 |
|
重要文件和路径
package-lock.json
- 依赖锁定文件
- 锁定依赖的具体版本
- 应该提交到代码仓库
- 保证团队所有成员使用相同版本的依赖
node_modules/
- 依赖包安装目录
- 不需要提交到代码仓库
- 可以通过
npm install
重新生成
.npmrc
- npm 配置文件1
2registry=https://registry.npmmirror.com
sass_binary_site=https://npmmirror.com/mirrors/node-sass/
常见部署流程
1 |
|
一些高级用法
1 |
|
环境变量
1 |
|
运维
- 缓存
node_module
目录以加快构建 - 使用
CI=true
环境变量 - 保存构建产物
- 使用
npm ci
代替npm install
在CI环境中安装依赖
NODE_ENV
development
模式下:
- 开启详细的调试信息和警告
- 不会进行代码压缩和优化
- 启用热重载(Hot Reload)
- 包含 source map
- 可能包含测试数据或模拟 API
production
模式下:
- 关闭调试信息和警告
- 进行代码压缩和优化
- 移除开发时才需要的代码
- 不包含 source map (除非特别配置)
- 使用真实 API 地址
CI=true
表明在持续集成环境中运行
影响:
- 禁用交互式输出
- 遇到错误立即退出
- 更严格的错误处理
- 禁用进度条显示
- npm 会以最大日志级别运行
npm install
根据 package.json 安装依赖
如果有 package-lock.json,会尽量遵循它
可能会更新 package-lock.json
可以安装单个包
会重用现有的 node_modules
npm ci
严格按照 package-lock.json 安装依赖
必须有 package-lock.json,否则报错
不会修改 package.json 和 package-lock.json
不能安装单个包
总是删除现有的 node_modules 后重新安装
通常比
npm install
快(因为跳过了一些计算和验证)