原PPT地址
翻译:@仙森
本文经译者授权,刊登在 Alinode 团队博客。如未经译者授权,谢绝转载。
github: nodejs/LTS
偶数版本会在每年四月发布,然后在10月份开始长期支持。
LTS 会一直支持18个月,然后将进入长达一年的维护期。
这两种方式的区别主要是修复的优先级。
Release | LTS Status | Codename | Active LTS Start | Maintenance Start | Maintenance End |
---|---|---|---|---|---|
v0.10 | End-of-Life | - | - | 2015-10-01 | 2016-10-31 |
v0.12 | End-of-Life | - | - | 2016-04-01 | 2016-12-31 |
4.x | Active | Argon | 2015-10-01 | 2017-04-01 | 2018-04-01 |
5.x | No LTS | ||||
6.x | Active | Boron | 2016-10-18 | 2018-04-18 | 2019-04-18 |
7.x | No LTS | ||||
8.x | Pending | Carbon | 2017-10-01 | 2019-04-01 | 2019-12-31 |
9.x | No LTS | ||||
10.x | Pending | Pending | 2018-10-01 | 2020-04-01 | 2021-04-01 |
Intl.DateTimeFormat.prototype.formatToParts
被添加到了 (ECMA402)v8-release-57
speeding-up-v8-regular-expressions
v8-release-58
one-small-step-for-chrome-one-giant
how-v8-measures-real-world-performance
features | v5.7 | v5.8 |
---|---|---|
Object static methods | yes | yes |
String padding | yes | yes |
Trailing commas in function syntax | flag | yes |
Async Functions | yes | yes |
Shared memory and atomics | no | no |
Object static methods: values
, entries
, getOwnPropertyDescriptors
Table: node.green, compat-table
Slide: abouthiroppy/ecmascript
ES2017
misc
annex b
ES2018
template literal revision
V8: Behind the Scenes (November Edition feat. Ignition+TurboFan and ES2015)
V8 优化了 JIT 编译器,它是用 Sea of Nodes 概念进行设计的。之前采用的编译技术是 Crankshaft,它支持优化更多的代码。但是 ES 的标准发展很快,后来发现 Crankshaft 已经很难去优化 ES2015 代码了,而通过 Ignition 和 TurboFan 可以做到。
TurboFan
High-performance ES2015 and beyond
已知的是到目前为止 V8 都没有自己的解析器,都是直接把 JS 编译成机器码。
作为JIT的JIT的问题,即使代码被执行一次,它也消耗大量的存储空间,所以需要尽量避免内存开销。
使用 Ignition,可以精简 25% - 50% 的机器码。
Ignition 是没有依赖 Turbofan 的底层架构,它采用宏汇编指令。为每个操作码生成一个字节码处理程序。
通过 Ignition 可以较少系统内存使用情况。
如图:
Ignition Design Doc
Firing up the Ignition Interpreter
Node.js 与 v8 底层探索
原计划是四月25号发布的,现在被延期到了5月30号。
V8版本从 v5.7 更新到 v5.8。
这是为了兼容 ABI(Application Binary Interface)6.0。
让 V8 Ignition 和 TurboFan pipeline 进入 8.0.0,也方便 9.x backport 到 LTS。
Ignition 和 TurboFan 在 v5.9 默认打开,到时候 Node 会以 semver-minor 方式升级到 v5.9。
V8 plan for Node.js LTS Carbon (A potential path to TurboFan + Ignition)
Ignition + TurboFan: Node.js benchmarks
8.0.0 Release Proposal
semver-minor since 7.8.0
nodejs/abi-stable-node
ABI stable abstraction layer of native module
不同的 Node 版本、VM间,提供 ABI (Application Binary Interface) 来保证兼容性,
支持 N-API 的本地模块将无需重新编译就可以工作。
VM Summit - 2017-03-03
node-eps/005-ABI-Stable-Module-API.md
module: add support for abi stable module API
目前 Node 的实现中,V8 的 API 是直接暴露出来的。由于 V8 经常变更 API,那就存在下面的这些问题:
这些问题之前的 NAN(Native Abstractions for Node.js)(https://github.com/nodejs/nan) 搞不定。
目前已经在 ChakraCore 中验证了
abi-stable-node/tree/doc
Module | Conversion Status | Performance Assesment |
---|---|---|
leveldown | Completed | #55 |
nanomsg | Completed | #57 |
canvas | Completed | #77 |
node-sass | Completed | #82 |
iotivity | Completed | N/A |
node-sqlite3 | Started | - |
semver-major
> assert.deepEqual(new Set([1, 2]), new Set([1]))
AssertionError: Set { 1, 2 } deepEqual Set { 1 }
at repl:1:8
at ContextifyScript.Script.runInThisContext (vm.js:44:33)
at REPLServer.defaultEval (repl.js:239:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval](domain.js:314:12)
at REPLServer.onLine (repl.js:433:10)
at emitOne (events.js:120:20)
at REPLServer.emit (events.js:210:7)
at REPLServer.Interface._onLine (readline.js:262:10)
at REPLServer.Interface._line (readline.js:611:8)
> assert.deepEqual(new Set([1, 2]), new Set([1, 2]))
undefined
nodejs/node#12142
semver-minor since 7.1.0
通过使用 icu transcode,把 Node 支持的编码从缓冲区转移到另一个缓冲区。
> const icu = process.binding('icu');
undefined
> const newBuf = icu.transcode(Buffer.from('€'), 'utf8', 'ascii'); // source, from, to
undefined
> console.log(newBuf)
> Buffer.isBuffer(newBuf)
true
buffer: add buffer.transcode
channel
到公共接口semver-minor since 7.1.0
之前是名为 _channel
的私有接口,这个私有接口将来会被废弃掉。
IPC(Inter Process Communication) 会返回一个到 channel 的引用。
> const fork = require('child_process').fork
> const n = fork('./test.js')
> n.channel
Pipe {
bytesRead: 0,
_externalStream: [External],
fd: 12,
writeQueueSize: 0,
buffering: false,
onread: [Function],
sockets: { got: {}, send: {} } }
> n.channel === n._channel
true
child_process: add public API for IPC channel
semver-minor since 7.3.0
if (cluster.isMaster) {
const worker = cluster.fork();
cluster.fork().on('listening', (address) => {
setTimeout(() => {
const w = worker.disconnect();
console.log(w); // undefined
// Worker // 7.3.0 ~
}, 1000);
});
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
});
} else {
http.createServer((req, res) => {}).listen(8000);
}
cluster: return worker reference from disconnect()
semver-minor since 7.2.0
> const key = '0123456789'
> const tagbuf = Buffer.from('tagbuf')
> const aadbuf = Buffer.from('aadbuf')
> const decipher = crypto.createDecipher('aes-256-gcm', key)
> decipher.setAuthTag(tagbuf)
Decipher {
_handle: {},
_decoder: null,
_options: undefined,
writable: true,
readable: true }
> assert.strictEqual(decipher.setAuthTag(tagbuf), decipher)
undefined
> assert.strictEqual(decipher.setAAD(aadbuf), decipher)
undefined
crypto: return this in setAuthTag/setAAD
semver-minor since 7.5.0
支持能够使用系统提供 CA。
CLI: --use-openssl-ca
, --use-bundled-ca
环境变量: SSL_CERT_DIR=dir
, SSL_CERT_FILE=file
crypto: use system CAs instead of bundled ones
semver-minor since 7.2.0
可以通过 dns.resolve4()
、 dns.resolve6()
获取每个记录的TTL(生存时间)。
> dns.resolve4("google.com", {ttl:true}, console.log)
null [ { address: '216.58.221.174', ttl: 273 } ]
> dns.resolve6("google.com", {ttl:true}, console.log)
null [ { address: '2404:6800:400a:807::200e', ttl: 242 } ]
dns: implement {ttl: true} for dns.resolve4() and dns.resolve6()
semver-minor since 7.6.0
在 fs 模块中支持使用 whatwg-url 的文件协议。
根据 whatwg-url 的 spec,必须使用文件的绝对路径。
> const URL = require('url').URL;
undefined
> const myURL = new URL('file:///C:/path/to/file');
undefined
> fs.readFile(myURL, (err, data) => {});
TypeError: path must be a string or Buffer
// 7.6.0 ~
> fs.readFile(myURL, (err, data) => {});
undefined
fs: allow WHATWG URL and file: URLs as paths
semver-major
> fs.SyncWriteStream
...
> (node:57352) [DEP0061] DeprecationWarning: fs.SyncWriteStream is deprecated.
nodejs/node#10467
semver-minor since 7.7.0
添加了三个方法:getHeaderNames()
, getHeaders()
, hasHeader()
无需再调用 _headers
私有对象
const http = require('http');
http.createServer((req, res) => {
res.setHeader('x-test-header', 'testing');
res.setHeader('X-TEST-HEADER2', 'testing');
console.log(res._headers); // { 'x-test-header': 'testing', 'x-test-header2': 'testing' }
console.log(res.getHeaders()); // { 'x-test-header': 'testing', 'x-test-header2': 'testing' }
console.log(res.getHeaderNames()); // [ 'x-test-header', 'x-test-header2' ]
console.log(res.hasHeader('X-TEST-HEADER2')); // true
}).listen(3000, function() {
http.get({ port: this.address().port }, (res) => {});
});
http: add new functions to OutgoingMessage
semver-minor since 7.5.0
http.request
和 https.request
可以使用 URL 对象。
const http = require('http');
const url = require('url');
const URL = url.URL;
const server = http.createServer((req, res) => {
console.log(req.url); // /foo?bar
console.log(req.method); // GET
res.end();
server.close();
}).listen(3000, function() {
const u = `http://localhost:${this.address().port}/foo?bar`;
http.get(u);
http.get(url.parse(u));
http.get(new URL(u)); // 7.5.0 ~
});
url: allow use of URL with http.request and https.request
semver-minor since 7.6.0
--debug-brk
通过这个参数,在开始调试的时候能够定位到代码的第一行。
$ node --inspect-brk test.js
Debugger listening on 127.0.0.1:9229.
To start debugging, open the following URL in Chrome:
chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229 ...
Debugger attached.
inspector: add --inspect-brk flag
semver-major
Node 8 之后 --debug
就不再支持了。
$ node --debug
node: bad option: --debug
$ node --inspect
Debugger listening on 127.0.0.1:9229.
To start debugging, open the following URL in Chrome:
chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229 ...
>
$ node --inspect-brk
Debugger listening on 127.0.0.1:9229.
To start debugging, open the following URL in Chrome:
chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229 ...
Switch the CLI debugger to V8 inspector
deps: Add node-inspect
src: Remove support for --debug
semver-minor since 7.5.0
--
在 -e
之后说明是 end-of-options
$ node -e "console.log(process.argv)" -- -arg1 -arg2
[ '/Users/xxx/.yyy/node/v7.4.0/bin/node' ]
# 7.5.0 ~
$ node -e "console.log(process.argv)" -- -arg1 -arg2
[ '/Users/xxx/.yyy/node/v7.5.0/bin/node', '-arg1', '-arg2' ]
--
after -e
means end-of-options
semver-minor since 7.5.0
通过设置 NODE_PRESERVE_SYMLINKS
环境变量为 1
关闭进程的 warning 信息。
参数 --no-warnings
效果同上。
process: add NODE_NO_WARNINGS environment variable
semver-minor since 7.1.0
通过设置 NODE_PRESERVE_SYMLINKS
环境变量为 1
开启符号链接。
参数 --preserve-symlinks
效果同上。
Add NODE_PRESERVE_SYMLINKS environment variable
semver-minor since 7.2.0
返回 C++ 对象的内存使用量。
> console.log(util.inspect(process.memoryUsage()));
{ rss: 23371776,
heapTotal: 10465280,
heapUsed: 5756560 }
// 7.2.0 ~
> console.log(util.inspect(process.memoryUsage()));
{ rss: 23244800,
heapTotal: 7692288,
heapUsed: 4918392,
external: 22846 }
process: add externalMemory
to process.memoryUsage
通过参数 --trace-warnings
开启优化 Promise
和 UnhandledPromiseRejectionWarning
堆栈信息。
$ node --trace-warnings
> const p = Promise.reject(new Error('This was rejected'))
> setImmediate(() => p.catch(() => {}))
(node:40981) Error: This was rejected
...
(node:40981) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated.
In the future, promise rejections that are not handled will terminate
the Node.js process with a non-zero exit code.
...
(node:40981) PromiseRejectionHandledWarning:
Promise rejection was handled asynchronously (rejection id: 1)
at getAsynchronousRejectionWarningObject (internal/process/promises.js:12:10)
at rejectionHandled (internal/process/promises.js:42:21)
...
promise: better stack traces for --trace-warnings
NODE-WHATWG-URL
semver-major
对于嵌套数组,改为写为[Array]。
> const obj = util.inspect({'a': {'b': ['c']}}, false, 1)
> obj
'{ a: { b: [Object] } }'
> assert.strictEqual(obj, '{ a: { b: [Array] } }')
AssertionError: '{ a: { b: [Object] } }' === '{ a: { b: [Array] } }'
// 8.0.0
> obj
'{ a: { b: [Array] } }'
> assert.strictEqual(obj, '{ a: { b: [Array] } }')
undefined
nodejs/node#12046
semver-minor since 7.9.0
%i
: 格式化为整型%f
: 格式化为浮点型> util.format('%d', 42.2)
'42.2'
> util.format('%i', 42.2)
'%i 42.2'
> util.format('%f', 42.2)
'%f 42.2'
// 8.0.0
> util.format('%d', 42.2)
'42.2'
> util.format('%i', 42.2)
'42'
> util.format('%f', 42.2)
'42.2'
util: add %i and %f formatting specifiers
semver-major
这是因为 V8 已经不支持 SIMD 了。
$ node --harmony_simd
> assert.strictEqual(util.inspect(SIMD.Int32x4()), 'Int32x4 [ 0, 0, 0, 0 ]');
undefined
// 8.0.0
> assert.strictEqual(util.inspect(SIMD.Int32x4()), 'Int32x4 [ 0, 0, 0, 0 ]');
AssertionError: 'Int32x4 {}' === 'Int32x4 [ 0, 0, 0, 0 ]'
lib: remove simd support from util.format()
semver-minor since 7.6.0
> const URL = require('url').URL
> const myURL = new URL('http://example.org/?a=b#c')
> const str = url.format(myURL, {fragment: false, search: false})
> console.log(str)
http://example.org/?a=b#c
// 7.6.0 ~
> console.log(str)
http://example.org/
url: extend url.format to support WHATWG URL
semver-minor since 7.2.0
v8 HeapStatistics 已经添加 does_zap_garbage
。
这是一种覆盖堆垃圾的模式。
通过参数--zap_code_space
开启。malloced_memory
, peak_malloced_memory
同时被添加到字段中。
> v8.getHeapStatistics()
{ total_heap_size: 7168000,
total_heap_size_executable: 3670016,
total_physical_size: 6132432,
total_available_size: 1492201768,
used_heap_size: 5416688,
heap_size_limit: 1501560832,
malloced_memory: 8192,
peak_malloced_memory: 1412016,
does_zap_garbage: 0 }
src: Add does_zap_garbage to v8 HeapStatistics
semver-major
在当前 Node 的核心模块中,每个文件中都会定义错误信息。现在的任务是统一到internal/errors.js
/lib/internal/errors.js
Tracking Issue: Migrate errors to internal/errors.js
semver-minor since ???
这可以说是最令人兴奋的 PR 了。通过 util.promisify,原生模块完美支持 async/await
const util = require('util');
const setTimeoutPromise = util.promisify(setTimeout);
setTimeoutPromise(2000, 'foobar').then((value) => console.log(value));
const stat = util.promisify(require('fs').stat);
async function callStat() {
const stats = await stat('.');
console.log(`This directory is owned by ${stats.uid}`);
}
callStat();
util: add util.promisify()