Node 初识
什么是 Node
- 是JS运行时环境
- 构建在V8引擎之上
- 通过libuv实现异步I/O
- 目标是让并发编程更简单,主要应用在以网络编程为主的I/O密集型应用中,不适合CPU密集型应用。JAVA在基础平台建设及大数据等领域有着非常深厚的基础,直接使用即可。

- V8 解释并执行JS
- libuv 由事件循环和线程池组成,负责所有I/O任务的分发与执行。
Node 的特点
- 构建传统网站
- 构建移动端 API:在后端的 API 接口开发上封装一层专门供前端使用的 API Proxy 是非常有必要的。后端API接口开发用于数据库访问,将返回的数据进行包裹,以 HTTP 形式返回;API Proxy 针对前端使用的API进行优化,使前端开发更人性化。
- 构建 RPC 服务:如 JAVA 的 Dubbo,Node 实现的 DNode、微服务工具集Seneca、跨语言的gRPC客户端。将数据库访问返回的数据以 TCP 形式传输给调用方。组装多个RPC服务,完成某项服务。将RPC服务包装成HTTP API。
- 前后端分离:前端页面静态化;前端页面服务化(PAAS);服务端渲染(SSR); 渐进式Web应用(PWA)
- 适用于Severless:无须关心运维(自带运维功能,一般集成Kubernetes),无须关心流量(自带服务网格功能,比如Istio,根据流量可以快速实例化容器),无须关心高并发(API层有缓存)。
Node 的应用场景
- 跨平台开发:PC web、移动端、HTML5、React Native,Weex 以及 硬件 Ruff.io
- 后端开发:面向网站、API、RPC 服务等
- API: 绝大部分是I/O密集型应用,是node最擅长的
- RPC:主要是对OLTP(联机事务处理过程)数据库进行操作,node可以支持,但如果是复杂计算,使用Go或JAVA会更好。
- 前端开发:三大框架辅助开发及工程化演进过程(使用Gulp、Webpack构建Web开发工具)
- 工具开发:主要是开发npm上的各种工具模块,包括各种前端预编译工具、构建工具Gulp、脚手架、命令行工具等
- 数据挖掘
- AI
Node 安装与入门
3m安装法
- nvm: 解决多版本共存、切换、测试等问题
- npm: 解决Node模块安装问题,其本身也是一个Node模块,每次安装都会内置某个版本的npm
nrm: 解决npm镜像访问慢的问题,提供测速、切换下载器(registry)功能
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bashcat ~/.bashrc_nvm | grep nvm # 我自己的Mac电脑是把nvm命令的执行路径放到该文件下的。有可能你的在~/.bashrcsource ~/.bashrc_nvm # 使系统环境变量生效vi ~/.zshrc # 手动将环境变量放到~/.zshrc里,将~/.bashrc_nvm的内容copy一份到这个文件中source ~/.zshrc # 使系统环境变量生效nvm ls-remote # 查看可安装的node版本nvm install 10.15.3which nodenvm alias default node # 手动指定一个default别名node -vnvm lsnvm use stable # 切换版本nvm reinstall-packages stable # 一键安装全局模块
⚠️若经常切换版本,最痛苦的是全局模块需要重新安装。通过
nvm reinstall-packages来解决npm v2: 典型的树形依赖,层次深,依赖重叠,安装时间长,但相对更清晰
- npm v3: 扁平化依赖,将依赖移到了顶层,同一模块多版本依赖时采用npm v2 模式,由于Node项目模块依赖非常多,因此在查找依赖时特别麻烦,虽没有npm v2用起来体验好,但下载速度确实快了不少。
- npm v5: 自动记录依赖树,下载使用强校验,重写缓存系统等功能得到了升级和改造。代表性特征是新增了package.json文件,在操作依赖时默认生成,用于记录和锁定依赖树的信息,与Yarn类似。 | 命令 | 简写 | 说明 | | —- | —- | —- | | 无 | 无 | 将模块安装到本地 node_modules目录下,但不保存到package.json里 | | —save-prod | -P | 将模块安装到本地 node_modules目录下,同时保存到package.json里的dependencies,这在安装模块时是必须安装的 | | —save-dev | -D | 将模块安装到本地 node_modules目录下,同时保存到package.json里的devDependencies仅供开发时使用,一般测试模块等辅助开发工具都会这样安装 | | —global | -g | 安装的模块为全局模块,如果是命令行模块,会直接链接到环境变量里 |
如果用nvm安装的安装包放在用户目录的nvm版本对应的bin目录~/.nvm/versions/node/v13.3.0下,否则在/usr/local下。
npm install --global nrmnrm test # 测速nrm lsnrm use cnpmnrm add yourcompany http://registry.npm.yourcompony.com/
hello node
touch hello_node.js
const http = require('http')http.createServer((req, res) => {let status = 200res.writeHead(status, { 'Content-Type': 'text-plain' })res.end('Hello Node')}).listen(3000, '127.0.0.1')console.log('Server running at http://127.0.0.1:3000/')
编程与调试
编程三等境界:
- 打日志:最简单、便捷、略显低级
- 断点调试:可以很直观地跟踪代码执行逻辑、调用栈、变量等
- 测试驱动开发:在写代码之前先写测试,在保证代码质量、重构等方面有帮助
VSCODE
安装code命令
打开Command Palette(command shift P),并键入 shell command:Install 'code' command in PATH
调试
https://code.visualstudio.com/docs/editor/debugging
| 编号 | 名称 | 描述 |
|---|---|---|
| 1 | Launch Program | 直接执行 |
| 2 | Launch via NPM | 通过npm来启动 |
| 3 | Attach to Port | 附加端口,根据端口来查找进程 |
| 4 | Attach to Process | 附加到进程,即跨进程调试 |
| 5 | Nodemon Setup | 代码变动自动重启的 node monitor, |
| 6 | Mocha Tests | 简单、强大的测试框架 |
| 7 | Yeoman generator | 使用最多的脚手架生成器 |
| 8 | Gulp task | 最流行的流式构建系统, |
request 有两个选项,对应着两种调试模块:
- launch:启动程序,相当于直接在编辑器里启动node.js,即本地调试
- attach: 附加到进程,在编辑器外通过node —debug 命令启动,然后附加到 debug 进程中,即远程调试
{"type": "node","request": "attach","name": "启动程序","address": "localhost","port": 5858},
{"type": "chrome","request": "launch","name": "Chrome","url": "http://127.0.0.1:8092/","webRoot": "${workspaceRoot}"},
{"type": "node","request": "launch","name": "Mocha JS","program": "${workspaceRoot}/node_modules/mocha/bin/_mocha","args": ["-u","tdd","--timeout","999999","--compilers","js:babel-core/register","--colors","${workspaceRoot}/test/js/**/*.spec.js"],"internalConsoleOptions": "openOnSessionStart"},
{"type": "node","request": "launch","name": "Mocha TS","program": "${workspaceRoot}/node_modules/mocha/bin/_mocha","args": ["-u","tdd","--timeout","999999","-r","ts-node/register","--colors","${workspaceRoot}/test/ts/**/**/*.spec.ts"],"protocol": "auto","internalConsoleOptions": "openOnSessionStart"}
{"type": "node","request": "attach","name": "Attach to Remote","address": "TCP/IP address of process to be debugged", // IP 地址或域名"port": 5858, // 端口"localRoot": "${workspaceRoot}", // 本地的代码根目录"remoteRoot": "Absolute path to the remote directory containing the program" // 远程代码的相对目录},
更了不起的node
架构变迁
| | 单体式架构 | 页面即服务 | | —- | —- | —- | | 开发难度 | 越大越复杂 | 单个页面不会特别复杂,更容易把控 | | 可用性 | 容易雪崩,影响极大 | 单个页面崩溃对整体来说影响极小 | | 运维 | 容易 | 麻烦(必须配合DevOps) |
单体式架构(Monolithic Architecture)
- 表现层:处理HTTP请求,直接返回HTML渲染,或者返回API结果。对于一个复杂的应用系统,表现层通常是代码中比较重要的部分。
- 业务逻辑层:完成具体的业务逻辑,是应用的核心组成部分
- service,通常组合多个DAO对象进行某项业务处理
- controller里组装了多个service对象,可实现具体的功能
数据访问层:访问基础数据,例如数据库、缓存和消息队列等
node层
- 提供请求转发、反向代理等功能
- 提供web服务,所有后端语言能做到的,node都能做到
- 圈定范围,便于维护,只要修改功能包含在当前node项目范围内即可维护
- 充当HTTP客户端,访问后端接口,但一般不直接访问数据库
- nginx层
- 负载均衡
- 反向代理
- 缓存
- 限流限速
- nginx需要繁杂的配置文件,且不能动态控制与实时生效,而openResty 只需要少量的lua代码即可
Node模块
| 分类 | 举例 |
|---|---|
| 压缩 | UglifyJS |
| CSS预处理 | Less |
| 依赖管理 | npm |
| 模块系统 | AMD | CMD | CMJ | ESM |
| 模块加载 | system.js |
| 模块打包器 | webpack + npm scripts |
| 构建工具 | gulp |
| 模版引擎 | jade | nunjucks |
| JS超集 | TS(动态类型一时爽,代码重构火葬场) |
| 跨平台打包 | Electron(客户端) | cordova(移动端) | Ruff.io(物联网) |
| 生成器 | Yeoman | vue-cli | create-react-app |
| 其他 | imagemin | DataURL |
| 后端 | express | koa | egg |
| 测试 | AVA(支持新特性,基于Babel安装) | Mocha |
| 数据库 | Mongoose(MongoDB) |
框架选型原则:
- 考虑业务场景、特点,不必为了用而用,避免本末倒置
- 考虑自身团队能力、喜好,有时候技术选型决定团队氛围,需要平衡激进与稳定
- 保证在出现问题的时候有人能够做到源码级定制
- 考虑安全、生态及历史遗留问题的处理
BFF
- API Proxy
- 服务组装
- API 网关
- 请求转发
- 跨域JSONP支持
更好的node
编程范式
面向过程
面向对象
TS 无缝兼容ES6,且提供静态类型、接口、反射、范型等特性,在代码质量、抽象程度等方面都表现的比较好。
函数式编程
- 一等公民是函数
- 函数柯里化(curry): 传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数
- 通过“函数组合”来解决函数嵌套问题
- 函数式编程的思想如果是在团队中应用,还是要谨慎选择,持观望态度,因为其一学习成本高,其二维护成本高。
Metetor: 著名的同构 Web 框架
ThinkJS: 典型的结合 npm scripts 和 Babel 一起使用的框架
单线程会死
Node是单线程的,只要代码出错肯定是要崩溃的。
- uncaughtException
- try/catch 捕获异常
- 自动重启:forever模块 | pm2
- 通过cluster模块充分利用服务器多核优势,启动多个实例
即本质上单线程会死是个伪命题:
- 单个应用实例可以适当捕获异常,减少崩溃几率
- 单个应用实例崩溃之后,采用forever或pm2自动重启,可以继续服务
- 利用多核集群同时在一台服务器上启动多个实例,崩溃的几率极低
- 应用线上部署就只部署一台服务器吗?这种几率其实也很小,多台服务器也要做集群。
- 如果所有集群中的服务器都崩溃了该怎么办?这其实是运维的问题,和Node无关
异步解决方案
| 解决方案 | 描述 | 推荐指数 |
|---|---|---|
| callback | Node API 天生就是这样的,异步必须要约定错误优先风格的问题 | 3 |
| thunk | 编译器的“传名调用”实现,通常做法是将参数放到一个临时的函数之中,再将这个临时函数传入函数体。 | 1 |
| promise | 最开始是社区定义的Promise/A+规范,大家习惯称之为Promise,随后成了ES6语言标准 | 4 |
| generator | Es6中的生成器,用于计算,但TJ想将其用作流程控制 | 1 |
| co | Generator用起来非常麻烦,故而TJ编写了co这个Generator生成器,用法简单 | 2 |
| async | NodeV7以上的版本都可以使用 | 5 |
Node 是如何执行的
以 node v8 为基础剖析核心原理:
- V8 和 libuv 的作用
- JS 是如何与C/C++通信的
- 掌握模块机制
./configure --debug # 开启debug会进行两个CMake任务,且在源码根目录分别连接node.js和node_g,编译完成后,生成的文件将位于out/Release和out/Debug目录下make -j4sudo make install
模块与核心
工具资源
vscode debugger:https://code.visualstudio.com/docs/editor/debugging
node 各版本对ES6的支持情况:https://node.green/
