NodeJS作为JavaScript在后端运行的宿主平台,保留了浏览器中许多常见的JavaScript接口,在不额外给JavaScript语言附加其他特性的基础之上,将前端 异步、事件驱动、单线程的思想迁移到了服务端。基于其自身的这些特点,也使得NodeJS成为更适于处理 I/O密集 与 高并发 服务的后端技术选型。
模块机制
CommonJS
NodeJS借鉴了CommonJS的Module规范,实现了一套非常易用的同步模块系统。
模块引用
在CommonJS规范中,通过require
函数,接受一个小驼峰规范命名的模块名或者URI,引入指定的模块API到当前上下文中。
1 | const math = require('math'); |
模块定义
每一个模块(JS文件)都有一个类似于在浏览器中document
的全局对象module
,表示模块自身。它是NodeJS内部的Module
类的对象实例。
而每一个模块都有自己的私有作用域,所以开发者完全不用考虑变量全局污染的情况。要使用全局数据的话,需要将变量绑定在全局对象global
上(类似前端的window
)。
module
上的属性exports
表示模块暴露给外部的内容,即其他模块可以通过require
函数引入的部分。
需要注意:模块全局虽然存在一个exports
对象,但它仅仅是一个指向module.exports
的引用而已,直接给exports
赋值只会改变它的引用对象,并不是修改了module.exports
。这一点和浏览器环境下任何全局变量都会默认挂在window
下是很不一样的。这也是为什么module
更类似document
而不是window
。
1 | const function test() { |
除了exports
属性之外,module
上的属性还有:
module.id: 模块的识别符,通常是带有绝对路径的模块文件名;
module.filename: 模块的文件名,带有绝对路径;
module.loaded: 返回一个布尔值,表示模块是否已经完成加载;
module.parent: 返回一个对象,表示调用该模块的模块;
module.children: 返回一个数组,表示该模块要用到的其他模块。
AMD
不同于CommonJS规范同步加载模块的方式,AMD(Asynchronous Module Definition)规范则是异步加载模块的,允许指定回调函数。由于Node环境下使用的模块都存在本地,不存在网络阻塞的场景,因此采用同步的模块机制CommonJS。而对于浏览器这种依赖网络加载的环境,引入模块则更适于使用AMD规范。例如采用此规范的 requireJS。
模块的引入
通过require
函数传入指定模块(模块名或模块URI)的数组,以及所有模块加载成功以后的回调函数。在所有模块加载成功以后,各个模块将作为参数传递给回调函数开始执行函数体。
1 | require(['//module/math', '//module/ramda'], function (math, R) { |
模块的定义
使用define
函数定义模块,可以将模块对象作为参数,也可以传递一个导出模块数据的回调函数。
1 | // 直接定义模块对象 |
CMD
CMD(Common Module Definition)即通用模块定义,是国内发展出来的,就像AMD有个requireJS,CMD也有个浏览器的实现 SeaJS ,SeaJS要解决的问题和requireJS一样,只不过在模块定义方式和模块加载(可以说运行、解析)的时机上有所不同。
在AMD中定义一个模块时,如果模块依赖其他模块,则需要在定义的同时声明模块依赖,也就是依赖前置。而CMD则推崇依赖就近原则,在真正需要使用依赖模块的时候再通过require
引入。
模块引入
模块的引入方式和AMD类似。
1 | seajs.use(['//module/math', '//module/ramda'], function (math, R) { |
模块定义
1 | // CMD模块定义 |
UMD
UMD(Universal Module Definition)规范即通用模块定义规范,是AMD、CommonJS和定义全局模块的兼容策略。AMD模块以浏览器第一的原则发展,异步加载模块。CommonJS模块以服务器第一原则发展,选择同步加载。这迫使人们又想出另一个更通用的模式UMD。以此解决了跨平台与全局引用的模块化场景。
1 | (function (root, factory) { |
ESM
ESM(ECMAScript Module)是用于处理模块化的 ECMAScript 标准。 虽然 NodeJS 长期使用 CommonJS 标准,但CommonJS只是社区提供的解决方案。而真正官方认定的统一模块标准,则是由ECMA(欧洲计算机制造商协会)伴随着ES6标准提出的ES Module。是同时支持服务端和前端的模块化标准。
基本特性
- ESM 自动采用严格模式,忽略 ‘use strict’;
- 每个 ES Module 都是运行在单独的私有作用域中;
- ESM 是通过 CORS(跨域) 的方式请求外部 JS 模块的;
- ESM 的 script 标签会延迟执行脚本(浏览器页面渲染后执行)。
模块定义
ESM规定,模块必须是一个单独的文件,引入、导出的操作只允许在模块中进行(所以尝试在控制台或者script标签中使用import导入模块时,会抛出语法错误)。
NPM包管理工具
NPM
是随着NodeJS
一同安装的包管理工具,为用户提供了方便、快捷的管理和开发各种基于Node
环境的依赖包。它的主要功能有:
- 允许用户从NPM服务器下载别人编写的第三方包到本地使用;
- 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用;
- 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。
常用命令
NPM
版本信息1
npm -v // npm --version
- 在项目中安装
NPM
依赖,并依据package.json
安装各类依赖(生成package-lock.json
)1
npm install
NPM
版本更新1
npm install npm@latest -g
- 全局安装依赖
1
npm install x -g
- 在项目中安装依赖(安装在项目node_modules目录下)
1
npm install x
- 在项目中安装依赖,并将依赖信息补充在
package.json
文件中的dependencies
属性下,以便下次执行npm install
时自动安装相关依赖1
npm install x --save
- 在项目中安装依赖,并将依赖信息补充在
package.json
文件中的devDependencies
属性下,以便下次执行npm install
时自动安装相关依赖。但在生产环境中安装时(npm install x --production
)不会安装该依赖1
npm install x --save-dev
- 更新依赖模块
1
npm update x
- 搜索依赖模块
1
npm search x
- 自定义模块(生成
package.json
)1
npm init
- 注册
NPM
库用户1
npm adduser
- 发布自定义模块
1
npm publish
- 撤销发布的模块
1
npm unpublish x@<version>
- 删除已安装的模块
1
2
3npm uninstall x
npm uninstall x --save
npm uninstall x --save-dev - 清除
NPM
缓存1
npm cache clear
package.json与package-lock.json的区别
- 在
package.json
中每一项依赖的版本号^1.0.0
中的^
指向后兼容依赖,在执行npm install
时,会依据最大版本号1
安装最新的依赖版本(如1.2.0
); - 在
package-lock.json
中将记录上一次执行npm install
实际安装各个依赖的版本号,使得其他开发者知道原来使用时的依赖的实际版本号,避免向后兼容依赖导致的问题。
npm install与npm i的区别(Windows下)
- 用
npm i
安装的模块无法用npm uninstall
删除,用npm uninstall i
才卸载掉; npm i
会帮助检测与当前node
版本最匹配的npm
包版本号,并匹配出来相互依赖的npm
包应该提升的版本号;- 部分
npm
包在当前node
版本下无法使用,必须使用建议版本; - 安装报错时
intall
肯定会出现npm-debug.log
文件,npm i
不一定。
异步I/O与非阻塞I/O
异步编程
内存控制
Buffer
网络编程
构建Web应用
进程
测试
参考资料
《深入浅出Nodejs》——朴灵
npm i和npm install的区别
npm install、npm install –save与npm install –save-dev区别