Node 10 LTS值得关注的特性

1,592 阅读7分钟
原文链接: mp.weixin.qq.com

# 前言

经过一年多的沉淀后,Node.js 10 终于正式进入 LTS 了,本文将为大家阐述值得关注的特性。

LTS 指的是 Long Term Support ,即『长期维护版本』,适用于生产环境的可靠版本。

同时,由阿里云团队研发的 Alinode 一直在积极地与上下游协作,已经第一时间支持到了最新版本,只需更新到 alinode-v4.5.0,使用 Node.js 性能平台 的开发者就可以享受到新特性啦!

详细的版本对应关系可以参见 文档 。

TLDR,主要关键词:

  • HTTP/2

  • BigInt

  • Window 安装包优化 - 解决原生模块安装编译问题

  • Work Threads - 实验性 API

  • new Buffer()- 构造函数的 API 被彻底废弃


# 新功能

以下新功能已经在 Node.js 10 LTS 中默认提供,无需附加任何的 flag 即可享受到。

## HTTP/2

正式支持 HTTP/2 特性,将给 Node.js 项目以及 Web 平台整个生态的巨大进步。

我们可以看下 HTTP/2 的一些特点:

  • 更有效的网络利用率

  • 引入 HTTP Header 压缩减小报文体积

  • 在同一个连接中支持多路并发

  • 支持 Server Push

借助于 HTTP/2 的这些新能力,开发者无疑可以给用户提供更佳的体验,尤其是首屏静态资源优化方式会有不一样的思考。如果你想进一步了解 HTTP/2 的使用方式,可以详细去看下官方文档:Node.js docs for HTTP/2。


## BigInt

JS 的 Number 类型实现的是 IEEE-754 双精度浮点型的标准,因此计算精度一直被诟病:

  • 最大能安全标示整数范围为:-2^53 ~ 2^53(不包含头尾区间)

  • 分母包含非 2 的因数的小数也是无法精确表示的,因此会出现 0.1 + 0.2 !== 0.3 这样的问题

其中针对第一点,对应的 TC39 标准提案 BigInt 目前处于 Stage 3 的状态。

Node.js 10 中已经实现了对应的部分,BigInt 的正式支持有助于我们能够更加精确地去处理大整数类问题。当然,第二点的问题还存在着,期待以后对 JS 的小数计算精度这个维度能有内置更好的解决方案。


## fs.mkdir 和 fs.mkdirSync 支持递归参数

这两个 API 对应于 Linux mkdir 指令,但却不支持  -p 参数来递归创建目录。

导致在之前版本中,fs.mkdir 创建目录时,我们需要确保父级目录一定是存在的,否则会抛出 ENOENT 的错误。所以社区一般不会直接使用,而是用 mkdirp 这个模块。

在新版本中,增加了 recursive 的选项,把该能力直接继承到了内核,会方便不少:

fs.mkdirSync('/home/admin/aaa/bbb/ccc', { recursive: true });

## CLI Flag 自动补全

我们都知道 Node 命令行启动时可以带上一些 flag 来开启诸如 GC 追踪、实验性 API 等能力。

但在之前版本中,由于没有自动补全功能,导致我们经常需要查文档来找 flag 那长长的名字。

在新版本里面,可以通过简单的操作让 bash 能自动补全这些 flag 了,使用方法如下:

$ node --completion-bash > node_bash_completion
$ source node_bash_completion

完成后,当我们在当前 bash 输入 node -- 后基于可以通过 TAB 键进行命令补全了。

值得一提的是,这个功能是在 PR 20713 里面实现的,有兴趣的同学可以看下实现过程。


## Windows 安装包优化

对于 Node.js 开发者来说,Windows 下的环境安装一直都是非常值得吐槽的。

主要带来的问题是:Node 原生模块的安装编译问题。

终于,从该版本开始,Windows 下的 MSI 安装包里面会包含一个额外的选项:『是否安装 Node.js 本地编译工具套件』

它将大大减少在 Windows 上编译 Node.js 源码以及安装那些需要本地编译的 Node.js 扩展所会遇到的困难,从而进一步降低了 Windows 环境下开发 Node.js 项目的门槛。

虽然官方在不断改善 Windows 下开发 Node.js 应用的体验,但是还是推荐大家使用 Mac/Ubuntu 避免给自己挖坑,投资自我研发效率,永远是高回报的。


## 代码覆盖率

通过自动化集成的方式,对代码进行单元测试,并保持较高的代码覆盖率,是保证项目和模块质量的重要手段。

在之前的实践中,覆盖率统计这块,只能通过 Istanbul 来实现。它的主要原理是通过 Loader 转译代码插桩的方式,带来测试速度和分析方面的问题。

经过作者和 V8 团队的探索,最终在 Node 内核里面直接引入 V8 相应的能力,来实现上述的功能,将极大的提升开发者体验。

在新版本中,开发者只需要简单的开启环境变量 NODE_V8_COVERAGE 即可通过对应的工具收集和分析覆盖率。

有兴趣的同学可以阅读下:

  • 探索过程:Rethinking JavaScript Test Coverage [1] 

  • 实现过程:coverage: expose native V8 coverage by bcoe · Pull Request #22527 · nodejs/node [2]


# 废弃功能

以下功能在 Node.js 10 LTS 版本中已经被强制废弃,如果你的项目中使用到了这些方法,升级前请记得一定要对这些代码进行处理,否则 Node.js 项目可能会无法运行!

## new Buffer()

直接调用 Buffer 构造函数的 API 被彻底废弃了,新版中强制使用以下方法来替代:

  • Buffer.from()

  • Buffer.alloc()

  • Buffer.allocUnsafe()

废弃该 API 主要还是基于安全的考虑,详细的可以看关于描述 Node.js Buffer 的官方文档描述 [3]。


# 实验性 API

以下方法当前处于 stability 1: Experimental 的状态,API 随时可能变动甚至消失,请勿在线上使用!

## fs.promises

fs.promises 下对所有的 fs 原始方法提供了对应的 promise 版本。

让我们来看看用法:

(async ()=> {
  let content;

  // util.promisify (8.x 后版本)
  const fs = require('fs');
  const { promisify } = require('util');
  const readFile = promisify(fs.readFile);
  content = readFile('package.json');
  console.log(content.toString());

  // mz/fs (8.x 前)
  const mz = require('mz');
  content = await mz.fs.readFile('package.json');
  console.log(content.toString());

  // fs.promises (10.x,试验特性,慎用)
  const fsPromise = require('fs').promises;
  content = await fsPromise.readFile('package.json');
  console.log(content.toString());})();

## Worker Threads

新版本也将社区呼声最大的 Worker Threads 作为实验性 API 引入,它用于帮助 Node.js 去提升处理以前不擅长的 CPU 密集型计算的性能。

要使用这一新特性,需要加上 --experimental-worker 这个 Flag。

const {
  Worker, isMainThread, parentPort, workerData} = require('worker_threads');if (isMainThread) {
  module.exports = async function parseJSAsync(script) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, {
        workerData: script
      });
      worker.on('message', resolve);
      worker.on('error', reject);
      worker.on('exit', (code) => {
        if (code !== 0)
          reject(new Error(`Worker stopped with exit code ${code}`));
      });
    });
  };} else {
  const { parse } = require('some-js-parsing-library');
  const script = workerData;
  parentPort.postMessage(parse(script));}

这个功能也是在几个月前引入的,因此 Node.js 核心团队尚在观察社区开发者的使用反馈来决定这一功能后续的发展方向,如果你在项目中使用了 Worker Threads,并且有一些好或者不好的体验的话,可以提交 issue 进行反馈。

PS:MicroSoft 之前也发布过一个第三方模块,https://github.com/Microsoft/napajs  。


# 其它

以下功能可能绝大部分的应用开发者不会太关心,简单给大家列出来知晓即可。

## OpenSSL 1.1.0i

正式支持  openssl@1.1.x,openssl@1.1.x 版本因为核心架构的变更和大量代码的重构,因此相比之前的 1.0.x 版本增加了对 ChaCha20-Poly1305 [4] 加密算法的支持。

## 支持 PEM 级别的加密

具体可以参加 RFC 1421 [5],PEM 级别的加密以前是用在电子邮件上的,目前已经广泛应用到了各个方面,成为一种事实上的通用加密数据格式。


# 参考资料

  • What's New to LTS with Node.js 10 LTS

  • Node.js ChangeLog

## Ref:

  • [1] Rethinking JavaScript Test Coverage: https://medium.com/the-node-js-collection/rethinking-javascript-test-coverage-5726fb272949 (需翻墙)

  • [2] coverage: expose native V8 coverage by bcoe · Pull Request #22527 · nodejs/node: https://github.com/nodejs/node/pull/22527

  • [3] Node.js Buffer 官方文档: https://nodejs.org/api/buffer.html#buffer_buffer_from_buffer_alloc_and_buffer_allocunsafe

  • [4] ChaCha20-Poly1305: https://tools.ietf.org/html/rfc7539

  • [5] RFC 1421: https://tools.ietf.org/html/rfc1421