一, 了解 Node.JS
## 1. 简介:
基于事件驱动, 可以实现异步, 非阻塞 IO
由于早期的网景公司和 IE 发布的浏览器都采用了 JavaScript 的脚本语言, 使用相当广泛, 直到 Google 公司发布了基于 webkit 内核的 Chrome 浏览器, 并且重新编译了 JavaScript 的虚拟引擎, 命名为 V8, 使其运行的速度大幅度提升.
Node.JS 的发布: Ryan Dahl 的程序员根据需求开发一个高性能的服务器, 由于 Google 发布的 V8 引擎性能非常好, 而 JavaScript 生来就是基于事件, 可以轻松的实现回调数据而 JavaScript 的后端开发领域几乎是空白的, 所以 Node.JS 被创作出来, Node 是节点的意思, 当时的作者希望它可以构建大型分布式可扩展可伸缩的平台, 每一个线程, 进程甚至服务器都作为整个平台的节点
- ## 2.Node.JS 的特点
- #### 异步非阻塞 IO
同步和异步:
同步指的是在请求资源或者查询数据库或者在进行大量运算时, 需要等待操作完成才能进行下一步动作, 同步的代码都是按照串行顺序完成的, 中间某一个环节的错误有可能会导致整个程序的退出.
异步是相对同步而言, 异步仅仅是对于上面提到的动作发送一个请求, 但不需要等待请求完成, 即可继续进行后续的操作, 而请求的结果执行是在回调函数中完成的.
在专业术语中, 同步也叫串行; 异步也叫并行.
Node.JS 中大多数操作都支持异步操作进行(同时也支持同步操作进行), 无论是文件读取还是网络请求亦或是数据库访问, 都支持并行的异步 IO 操作. 由于异步非阻塞 IO 的特性, Node.JS 非常适合做高并发服务器.
#### 基于事件的回调
Node.JS 中仿照浏览器事件的原理针对大部分异步操作都采用了事件处理, 在事件中监听变化并把结果返回给回调函数. 因此我们代码编写的顺序未必就是实际的执行顺序, 这有可能会造成代码阅读上的障碍. 在学习 Node.JS 的过程中, 注意: 事件回调函数的第一个形参一般都指代 error(即异步处理可能发生的错误), 并且应该优先处理 error.
- const fs = require('fs')
- fs.readFile('hello.txt', (err) => {
- if (err) {
- // 这里发生了错误
- console.error(err)
- return
- }
- // 如果没有错误发生, err 约定是 null.
- })
- #### 单线程
因为浏览器内核运行 JavaScript 都是在单线程环境下执行的, 因此 Node.JS 继承了 JavaScript 的单线程特性, 但是目前已经拥有了实现多线程的方法:
通过 Web Workers 创建工作线程来实现多线程, 并通过 Windows.postMessage 实现线程通信.
通过 child_process 实现和 Web Workers 类似的方式, 该特性只有 Node.JS 提供.
#### 跨平台
Node 非常引入关注的特点还有具有跨平台特性, 兼容 Windows,Linux 和 Unix(Mac 的 OS X 基于 Unix), 底层的架构是借助 libuv 实现的跨平台兼容, 此外 libuv 不仅用于 Node.JS, 也成为了许多跨平台的基础组件.
3.Node.JS 的应用领域
虽然 Node.JS 是为了高性能服务器而开发, 但它并非适用于所有领域. 在当前的后端开发领域, 它主要适用于以下场景:
IO 密集型业务
天然的异步 IO 特性让 Node.JS 非常适合大量的 IO 请求, 读写, 它可以快速的处理请求并返回数据. 但 Node.JS 不适合计算密集型(例如人工智能算法), 这种操作通常交给 C++ 来处理.
分布式应用
由于高效的 IO 操作, Node.JS 非常适合开发数据库集群. 数据库的访问可以是大量 IO 请求, 只不过请求的处理需要进行密集的计算(数据库引擎来完成). 阿里巴巴利用 Node.JS 开发了 NodeFox
4Node.JS 运行时
安装完毕 Node.JS 之后, 可以在终端 (Windows CMD 命令行或者 nix 终端) 输入: node -v, 查看 node 版本. 出现版本号表示安装成功
输入: node 回车
node 会进入 REPL 环境(Read Eval Print Loop: 交互式解释器) , 类似 Windows 系统的终端或 Unix/Linux shell, 我们可以在终端中输入命令, 并接收系统的响应.
REPL 可以执行:
读取用户输入或者 JavaScript 变量声明
执行 JavaScript 代码
多行表达式 (如 for 循环) 会自动通过... 来换行
打印结果
执行完毕后:
ctrl + c - 退出当前终端.
ctrl + c 按下两次 - 退出 Node REPL.
5. 了解 NPM
Node.JS 安装完毕后, 会随同它一起安装了一个 NPM 工具, 该工具作为 NPM 命令单独使用.
NPM 是一个包管理工具, 它能够辅助用户远程安装其他的依赖包
用途如下:
用户可以从 NPM 仓库下载第三方包到本地使用.
用户可以从 NPM 仓库下载安装第三方命令行工具到本地使用.
允许用户发布自己的包或命令行工具到 NPM 仓库中.
Node.JS 默认是集成 NPM 的, 只需要在终端输入: NPM -v 即可打印版本号
$ NPM -v 6.4.1
NPM 常用的命令:
1. NPM install [-args] [<@scope>/]<name>[@<version range>]
安装第三方依赖包, 例如: NPM install --global @babel/[email protected]
2. NPM uninstall [-args] [<@scope>/]<name>[@<version range>]:
卸载模块, 例如: NPM uninstall jQuery --save-optional, 卸载可选阶段的依赖里的 jQuery
3. NPM update [-g] [<pkg>]
更新模块.
4. NPM outdated [[<@scope>/]<pkg>]
检查模块是否已经过时.
5. NPM ls [[<@scope>/]<pkg> ...]
查看安装的模块.
6. NPM init [-f|--force|-y|--yes]
在项目中引导创建一个 package.JSON 文件.
安装包的信息可保存到项目的 package.JSON 文件中, 以便后续的其它的项目开发或者他人合作使用.
7. NPM root [-g]
查看包的安装路径
8. 管理 NPM 的配置路径有关的命令
- NPM config set <key> <value> [-g|--global]
- NPM config get <key>
- NPM config delete <key>
- NPM config list
- NPM config edit
- NPM get <key>
- NPM set <key> <value> [-g|--global]
9. NPM cache verify: 清理缓存, 如果安装莫名报错可以考虑清空缓存.
[清空 NPM 缓存]
Node.JS 清空 NPM 缓存
NPM cache clean -f
10. NPM start [-- args]: 启动某个脚本指令
该命令写在 package.JSON 文件 scripts 的 start 字段中:
- "scripts": {
- "start": "gulp -ws"
- }
可以自定义命令来配置一个服务器环境和安装一系列的必要程序, 如
此时在 cmd 中输入 NPM start 命令相当于执行 gulpfile.JS 文件自定义的 watch 和 server 命令.
11. NPM stop [-- args]: 停止脚本.
12. NPM restart: 重启脚本.
13. NPM publish: 发布自己的模块到仓库.
14. NPM version: 查看版本.
详解 NPM install
NPM install 主要功能是在当前目录下安装所需的依赖, 安装的依赖包可以通过引入库的方式随意的使用. 正规格式如下: 其中 [] 中是可选的 <> 中表示的是自定义名称.
NPM install 安装的依赖包默认存储在项目目录 (需要具有 package.JSON) 的 node_modules 目录下.
引入依赖包时, 会通过一级一级查找 node_modules 目录, 直到找到该包.
NPM install [args] [<@scope>/]<name>[@<version range>]
NPM install : 需要保证当前目录下存在 package.JSON 文件, 基于 package.JSON 自动安装全部依赖.
NPM install [args] [<@scope>/]<name>[@<version range>]中的可选参数 args:
--save 或 -S : 安装包信息将加到 dependencies(生产阶段的依赖), 开发阶段和部署阶段都会使用它.
--save-dev 或 -D <package>: 安装包信息将加到 devDependencies(开发阶段的依赖), 只有开发阶段会使用它.
-O, -save-optional: 安装包信息将加入到 optionalDependencies(可选阶段的依赖).
-E, -save-exact: 精确安装指定模块版本.
NPM 安装包的几种依赖方式:
depedencies: 指定应用依赖的外部包, 这些依赖是应用正常发布后正常执行所需要的,** 但不包含测试时和本地打包时所使用的包 **
devDependencies:** 只用于开发环境的包 **, 不用于生产环境, 这些包通常是单元测试或者打包工具等, 例如 gulp, grunt, webpack, moca, coffee 等.
peerDependencies: 同等依赖, 用于指定自己写的包兼容的宿主版本.
* 试想一下, 我们编写一个 gulp 的插件, 而 gulp 却有多个主版本, 我们只想兼容最新的版本, 此时就可以用同等依赖 (peerDependencies) 来指定:*
- {
- "name": "gulp-my-plugin",
- "version": "0.0.1",
- "peerDependencies": {
- "gulp": "3.x"
- }
- }
* 当别人使用我们的插件时, peerDependencies 就会告诉明确告诉使用方, 你需要安装该插件哪个宿主版本.
通常情况下, 我们会在一个项目里使用一个宿主 (比如 gulp) 的很多插件, 如果相互之间存在宿主不兼容, 在执行 NPM install 时, cli 会抛出错误信息来告诉我们, 比如:*
- NPM ERR! peerinvalid The package gulp does not satisfy its siblings' peerDependencies requirements!
- NPM ERR! peerinvalid Peer [email protected] wants [email protected]~3.1.9
- NPM ERR! peerinvalid Peer [email protected] wants [email protected]~2.3.0
但是, 假如运行命令 NPM install gulp-my-plugin -save-dev 来安装自己写的包, 会出现以下依赖图谱:
├── [email protected]
└── [email protected]
optionalDependencies: 可选依赖, 如果有一些依赖包即使安装失败, 项目仍然能够运行或者希望 NPM 继续运行, 就可以使用 optionalDependencies.** 另外 optionalDependencies 会覆盖 dependencies 中的同名依赖包, 所以不要在两个地方都写.**
bundledDependencies / bundleDependencies: 打包依赖, bundledDependencies 是一个包含依赖包名的数组对象, 在发布时会将这个对象中的包打包到最终的发布包里.** 但是值得注意的是, 这两个包必须先在 devDependencies 或 dependencies 声明过, 否则打包会报错.**
package.JSON 文件介绍
package.JSON 描述了 NPM 模块或者自定义项目的一些基本信息
如果自定义项目需要安装第三方依赖或者自己发布模块到 NPM 仓库, 都需要在项目的根目录下面加入 package.JSON 文件, 文件格式如下:
- {
- // 作者
- "author": {
- "name": "Aseem Kishore",
- "email": "[email protected]"
- },
- // 命令行文件路径
- "bin": {
- "json5": "lib/cli.js"
- },
- // 构建依赖
- "dependencies": {
- "minimist": "^1.2.0"
- },
- // 简介, 用于 NPM 搜索
- "description": "JSON for humans.",
- // 开发依赖
- "devDependencies": {
- // 版本号介绍
- // version 必须完全和 version 一致
- //>version 必须比 version 大
- //>=version 同上
- // <version 同上
- // <=version 同上
- // ~version 大约一样, 见 semver(7)
- // 1.2.x 1.2.0, 1.2.1, 等, 但不包括 1.3.0
- "core-js": "^2.6.5",
- "eslint": "^5.15.3",
- "eslint-config-standard": "^12.0.0",
- "eslint-plugin-import": "^2.16.0",
- "eslint-plugin-node": "^8.0.1",
- "eslint-plugin-promise": "^4.0.1",
- "eslint-plugin-standard": "^4.0.0",
- "regenerate": "^1.4.0",
- "rollup": "^0.64.1",
- "rollup-plugin-buble": "^0.19.6",
- "rollup-plugin-commonjs": "^9.2.1",
- "rollup-plugin-node-resolve": "^3.4.0",
- "rollup-plugin-terser": "^1.0.1",
- "sinon": "^6.3.5",
- "tap": "^12.6.0",
- "unicode-10.0.0": "^0.7.5"
- },
- // 项目官网
- "homepage": "http://json5.org/",
- // 关键字, 用于 NPM 搜索
- "keywords": [
- "json",
- "json5",
- "es5",
- "es2015",
- "ecmascript"
- ],
- // 许可证
- "license": "MIT",
- // 程序入口, import 导入默认就加载这个入口
- "main": "lib/index.js",
- "module": "dist/index.mjs",
- // 包名, NPM install 就依赖于这个
- "name": "json5",
- // 源码地址
- "repository": {
- "type": "git",
- "url": "git+https://github.com/json5/json5.git"
- },
- // 可用于 NPM start 的命令.
- "scripts": {
- "build": "rollup -c",
- "build-package": "node build/package.js",
- "build-unicode": "node build/unicode.js",
- "coverage": "tap --coverage-report html test",
- "lint": "eslint --fix .",
- "prepublishOnly": "npm run production",
- "preversion": "npm run production",
- "production": "npm run lint && npm test && npm run build",
- "test": "tap -Rspec --100 test",
- "version": "npm run build-package && git add package.json5"
- },
- // 版本号
- "version": "2.1.1"
- }
使用 NPM 之前最好设置下 NPM 的仓库镜像, 以便提高访问速度:
NPM config set registry [https://registry.npm.taobao.org](https://registry.npm.taobao.org)
下面命令验证配置是否成功:
NPM config get registry
推荐: 全局安装 yarn
yarn 是 Facebook 官方发布的可以取代 NPM 的的工具, 使用命令的方式和 NPM 类似(可选参数不同), 能够显著提高安装依赖的速度, 同时还能和 NPM 兼容.
yarn [global][-D] add <pkg>: 安装依赖包.
模块化
Node.JS 所有的 API 都是基于模块发布和使用的
模块化的由来
在 JavaScript 的初期引用模块化是通过 script 标签来实现的, 但是产生了很多的问题: 全局的变量污染, 无法找到源头; 过多的 script 标签会导致很多杂乱无章的代码; 使用某个库却没相关库的依赖; 无法追踪到报错的位置和错误.
CommonJS 规范
Node.JS 首先采用了 CommonJS 规范, 它是 Node.JS 目前使用最广泛的模块规范, 它是为了解决 JavaScript 的作用域问题而定义的模块, 可以使每个模块的自身命名空间执行
CommonJS 使用规则
- 每个 JS 文件就是一个模块, 模块有自己的作用域. 在一个文件里面定义的变量, 函数, 类, 都是私有的, 对其他文件不可见. 每个模块内部, module 变量 (默认拥有) 代表当前模块. 这个变量是一个对象, 它的 exports 属性 (即 module.exports) 是对外的接口. 加载某个模块, 其实是加载该模块的 module.exports 属性.
- 模块必须通过 module.exports 导出对外的变量或接口.
引用
文件 a.JS
- var a=10
- module.exports=a
文件 b.JS
var a=require('./a.js') --- 此处为路径, a.JS 的路径
console.log(a)
注意
commonjs 加载是同步的, 因此无法按需加载
ES6 中的模块化
ES6 模块是 ECMA 组织从语言层面提出的标准模块化 API, 未来完全可以取代 CommonJS 和其他的模块化系统.
基本使用方式
文件 a.JS
- var a=10
- export default a
- export const name='小明'
文件 b.JS
- import a,{
- name
- }from'./a.js'// 路径, a.JS 路径
- console.log(a)
- console.log(name)
它具有如下优势:
基于编译时加载, 可以让模块在编译阶段就能进行静态语法分析, 从而提前预警语法错误或者做类型校验.
注意
ES6 模块默认在严格模式下执行, 即默认在模块头部加入:"use strict".
尤其注意: 顶层的 this 指向 undefined, 不应该在顶层代码使用 this.
AMD 模块
基本使用方式
文件 a.JS
- define(function(){
- var a=10
- module.export=a
- })
文件 b.JS
- var a=require('./a.js')// 文件路径, a.JS 的路径
- console.log(a)
来源: http://www.bubuko.com/infodetail-3303489.html