Node.js 12:服务端 JavaScript 的未来

阅读数:2243 2019 年 8 月 2 日 10:16

Node.js 12:服务端JavaScript的未来

自 2009 年首次发布以来,Node.js 一直是一项改变游戏规则的技术。概括来说,它允许开发人员使用 JavaScript 在服务端运行脚本,在页面发送到用户的 Web 浏览器之前生成动态 Web 内容。Node.js 是“JavaScript 无处不在”的潮流代表;在这种潮流推动下,服务端和客户端脚本不再需要使用不同的语言编写,开发 Web 应用只用一种编程语言就够了。

如果你像我一样也是 JavaScript 和 Node.js 的粉丝,你会很高兴看到它们正在迎来全方位的改进。

Node 12 新特性与改进

“全方位改进”是什么意思呢?其实就在几个月前,Node.js 12 版刚刚发布了。

2019 年 4 月 23 日 Node.js 12 正式发布,全球各地的 JavaScript 爱好者都欢欣鼓舞。需要明确的是,这不只是一个普通的旧版本更新,而是一个有诸多重要内容更新的关键版本升级。具体升级内容如下。

V8 JavaScript 引擎升级

每次的新版本 JavaScript V8 引擎都会带来一些性能调整和改进,此外这次还有一些非常值得注意的升级。具体有:

  • 零成本异步堆栈跟踪——通过异步调用框架丰富 error.stack 属性,而无需为 V8 引擎添加额外的运行时。

  • 参数不匹配的调用更快——以前 V8 必须用统一的方式处理参数过多或过少的函数调用,造成性能损失。现在引擎变得更聪明了,知道什么时候可以跳过这一步,从而将调用开销减少了 60%。

  • 更快的异步函数和 Promise ——实际上现在使用异步要比 Promise 快两个微拍(microtick),因为对 Promise 不熟悉的开发者可以使用语法风格更加同步的 async/await。

  • 更快的 JavaScript 解析——在网页启动时,解析 JS 的耗时只有引擎总耗时的 10%不到。最新发布的 JavaScript 解析器在桌面端的解析速度提高了 30%。

使用 TLS 1.3 提高安全性

TLS(传输层安全),是 Node 处理加密流通信的方法。

随着 Node.js 12 的发布,TLS 也升级到了 1.3 版;虽然版本号只多了一点,但这次实际上是重大更新,加入了很多性能和安全性增强功能。听起来有些不可思议,但 TLS 1.3 实际上是比 TLS 1.2 更简单的协议,比后者更安全、更易配置,并且可以更快地在应用程序之间协商会话。

Node 应用可以在 TLS 1.3 的帮助下更好地保护终端用户隐私,同时减少 HTTPS 握手所需的时间来提高请求性能。

小结:为所有用户提供更好的安全性并减少服务之间的通信延迟,我认为这是一大进步。

合理配置的默认堆大小

下面来谈一些底层改进。以前,JavaScript 堆大小默认等于浏览器 V8 引擎设置的堆大小上限,前者可以手动调整。Node.js 12 发布开始,JS 堆大小将根据可用内存来自动配置,这样 Node 就不会尝试使用比可用内存更多的内存空间了,避免了耗尽内存导致进程被迫终止的问题。

新版在处理大量数据时不会再出现内存不足错误——以前经常会出现这种问题。需要的话,开发者还可以使用旧的–max-old-space-size 标志手动设置上限,但新功能出现后手动设置大小的需求应该就没那么多了。

默认的 http 解析器变为 llhttp

很多人(包括我自己)都不知道,Node 中之前使用的 http_parser 库非常难以维护和改进,这就是 llhttp 诞生的原因。该项目是一个 http_parser 到 TypeScript 的端口,然后通过 llparse 运行以生成 C 或 bitcode 输出。

测试表明 llhttp 比 http_parser 快 156%;前者的代码量更少,并且所有性能优化都是自动生成的——相比之下 http_parser 需要手动优化代码。

从 Node.js 12 开始,官方决定将默认解析器切换到 llhttp,开始对其进行全面测试。我们希望它在用途各异的众多应用中都能维持出色的性能表现。

按需生成诊断报告

谈到调试的话题,Node.js 12 引入了一个新的实验性功能,允许用户根据需要或在发生某些触发事件时生成报告。

这种实时报告可以帮助开发者诊断生产中的问题,包括崩溃、性能下降、内存泄漏、高 CPU 使用率、意外错误等等——这些都是通常需要数小时甚至数天才能调试、诊断和修复的东西。

集成堆转储

这个版本中关于堆的另一项特性是 Node.js 12 内置的集成堆转储,它可以加速调试过程。

现在开发者无需安装新模块来调查内存问题——只需通过命令行或 API 调用告诉 Node 你需要哪种 JSON 格式的诊断摘要,然后解析你能处理的所有信息即可。

Node.js 中的原生模块更易使用

底层改进说完了。此外,对于 Node 生态系统中的开发人员和模块开发者来说也有一些改进值得一提。

新版进一步完善了为 Node 创建和构建原生模块的流程,包括更好地支持原生模块与 worker 线程的结合,N-API 也升级到了第 4 版,让开发者可以更容易为原生异步函数配置自己的线程。

总的来说,这意味着 Node 专属模块的创建者和维护者可以像纯 JavaScript 模块的创建者一样轻松地维护这些模块。之前维护人员需要为模块支持的所有 Node.js 版本各做一份二进制文件,而新版 N-API 大大简化了这部分工作。

Worker 线程即将到来——实验标志已被删除

虽然 Node 10 开始就引入了 Worker 线程,但到了 12 版中,启用 Worker 线程不再需要标志——实验阶段快要结束了。在 Node.js 11.7.0 之前,开发者需要在命令行中使用–experimental-worker 标志启动 node 才能访问 Worker 线程模块。

复制代码
$ node -e "require('worker_threads'); console.log('success');"
internal/modules/cjs/loader.js:605
throw err;
^
Error: Cannot find module 'worker_threads'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:603:15)
at Function.Module._load (internal/modules/cjs/loader.js:529:25)
at Module.require (internal/modules/cjs/loader.js:657:17)
at require (internal/modules/cjs/helpers.js:22:18)
at [eval]:1:1
at Script.runInThisContext (vm.js:123:20)
at Object.runInThisContext (vm.js:312:38)
at Object. ([eval]-wrapper:6:22)
at Module._compile (internal/modules/cjs/loader.js:721:30)
at evalScript (internal/bootstrap/node.js:720:27)
$
$ node --experimental-worker -e "require('worker_threads'); console.log('success');"
success
$

在执行 CPU 密集型 JavaScript 操作时 Worker 的表现非常出色;但它对 I/O 密集型任务没什么帮助,这种时候 Node 内置的异步 I/O 操作比 Worker 更高效。

启动时间的改进

Node.js 11 引入了内置的代码缓存支持,将 Worker 线程的启动时间减少了近 60%

Node 12 在此基础上更进一步,在构建时提前为内置库生成代码缓存,允许主线程使用代码缓存来处理用 JavaScript 编写的内置库的初始加载过程。

结果主线程的启动速度又提升了 30%,你的应用为用户加载的速度也能变得更快了。

ES6 模块支持,马上就要完成了

重头戏留到最后介绍。对我来说最令人兴奋的功能之一是 ES6 模块支持——这也是我们很多人一直在等待的东西。这个功能仍然是实验性的,Node 团队正在收集测试反馈。这个功能意味着开发者只要动动手指就能够实现从前端到后端 JavaScript 的无缝过渡。

以下是最新版本的–experimental-modules 的亮点所在:

  • ES2015 import 语句可以将 Javascript 文件引用为相对链接./examples.js、绝对链接 file:///opt.app/examples.js、包名称 example-package 或包中的路径 example-package/lib/examples.js。
复制代码
// relative urls
‘./examples.js’
// absolute URLs
file:///opt.app/examples.js’
// package names
‘example-package’
// paths within packages
example-package/lib/examples.js
  • .js 文件中的导入和导出语法现在也都能用了。自 ES6 发布以来,开发人员终于可以像在传统 JS 中那样指定默认导出 import test from ‘./examples’、命名导出 import {example1, example2} from ‘./examples’ 和命名空间导出 import * as samples from ‘./examples’ 了。
复制代码
// default imports / exports
import test from ‘./examples’
// named imports / exports
import {example1, example2} from ‘./examples’
// namespace exports
import * as samples from ‘./examples’
  • 将“type”: “module”添加到项目的 package.json 中,且 Node.js 将项目中的所有.js 文件视为 ES 模块。这种方法允许 Node 将 package.json 用于包级元数据和配置,做法类似 Babel 等捆绑和配置工具。

  • 使用.mjs 结尾的文件会被显式视为模块,使用.cjs 结尾的文件会被视为 CommonJS。这些是仍然使用 require 和 module.exports 语法类型的文件。

现在这些功能不再需要标志就能使用,真是太棒了。

Node 12 的新编译器和平台最低标准

最后要谈的是新版 Node 的运行需求变动。

Node.js 12 通过内部改进和 V8 引擎的升级引入了许多新功能,最低运行需求也随之变化。对于 macOS 和 Windows 以外的平台而言,新版代码库现在至少需要 GCC 6 和 glibc 2.17。使用这套新工具链编译的版本会包含编译时性能和安全性增强。

如果你使用的是 Mac 或 Windows 系统应该就没问题了:Windows 系统版本需求没变,Mac 用户至少需要 Xcode 8 和 10.10“Yosemite”系统来运行新版 Node。来自 nodejs.org 的 Linux 兼容版本将支持 Enterprise Linux 7、Debian 8 和 Ubuntu 14.04,但可能需要在原生不支持 GCC 6 的系统上使用自定义工具链。我相信你会很快找出所需要的东西。

总结

Node.js 的历史只有 10 年,现在还是单线程的,也没有像其他一些编程语言那样广泛应用,但 Node 拥有一些其他编程语言难以企及的优势:它是用 JavaScript 构建的 ,并且既可以在客户端也能在服务端运行。

致力于支持和改进 Node 的团队和企业是业界最优秀的。Node 正在持续从核心 JavaScript 和其他语言中学习,挑选合适的部分并吸纳到自身当中,为开发人员和应用程序打造一个不断进步的平台。

Node.js 12 带来了一些非常令人兴奋的改进,例如 ES6 模块支持、更好的应用程序安全性和更快的启动速度等。虽然直到 2019 年 10 月它才会进入 LTS(长期支持)模式,但我还是要深入研究这些新功能,看看团队还能想到哪些内容来继续改进这个平台,使其成为更加出色的服务端解决方案。

英文原文: https://blog.logrocket.com/node-js-12/

评论

发布