作者:Hugo
链接: zhuanlan.zhihu.com/p/460474421
来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:Bartek Iwańczuk, Luca Casonato
全文:9969 字,时间 20 分钟。
如果你已经安装了 Deno,你可以通过下面的命令升级到 1.18:
(sudo)deno upgrade
如果你是第一次安装,你可以使用下面的方法:
# 使用 Shell (macOS and Linux):
curl -fsSL https://deno.land/x/install/install.sh | sh
# 使用 PowerShell (Windows):
iwr https://deno.land/x/install/install.ps1 -useb | iex
# 使用 Homebrew (macOS):
brew install deno
# 使用 Scoop (Windows):
scoop install deno
# 使用 Chocolatey (Windows):
choco install deno
新功能和变化
Web 加密接口全部完成
经过我们 6 个月的努力,终于完成了全部网络加密接口的功能。大家可以在 Deno 上使用完整的网络加密功能。
Deno 通过了 98.1% 的网络加密接口的网络平台测试集。以下是目前具体的数据:Chrom/Edge 通过了 94.5%,Firefox 通过了 93.4%,Safari 通过了 99.8%。你可以通过这个数据来进行你自己的测试。
本次发布增加了以下接口:
- crypto.subtle.encrypt:
- AES-GCM support
- AES-CTR support
- crypto.subtle.decrypt:
- AES-GCM support
- AES-CTR support
- crypto.subtle.wrapKey:
- AES-KW support
- crypto.subtle.unwrapKey:
- AES-KW support
- crypto.subtle.importKey:
- EC P-384 support
- crypto.subtle.exportKey:
- Support for ECDSA and ECDH pkcs8/spki/jwk exports
目前还有非常不常见的特性没有支持,例如 P-521 elliptic curve keys。
感谢 Sean Michael Wykes 实现了这次的特性。
自动发现配置文件
我们继续迭代在 v1.14 版本引入的配置文件的功能。在之前,引用配置文件,需要加上 --config 标签。
从这个版本开始,Deno 会自动寻找 deno.json 或者 deno.jsonc 配置文件。你仍然可以显示增加 --config 来指定这个文件。
在 v1.18 之前。
$ deno run --config ./deno.json ./src/file1.js
$ deno fmt --config ./deno.json
$ deno lint --config ./deno.json
v1.18
$ deno run ./src/file1.js
$ deno fmt
$ deno lint
对于不需要指定文件名的子命令(例如 deno fmt),Deno 会在当前文件夹向上遍历文件夹找配置文件。对于需要指定文件名的子命令(例如 deno run ./src/file1.js),Deno 会在入口文件附件找配置文件,也会向上遍历文件夹寻找。
以下面这个文件目录为例:
/dev
/deno
/my-project
/src
/file1.js
/file2.js
如果当前的工作目录是 /dev/deno/my-project/,并且我们运行 deno run src/file.js,Deno 会尝试按照下列的方式寻找配置文件:
/dev/deno / my -
project / src / deno.json / dev / deno / my -
project / src / deno.jsonc / dev / deno / my -
project / deno.json / dev / deno / my -
project /
deno.jsonc /
dev /
deno /
deno.json /
dev /
deno /
deno.jsonc /
dev /
deno.json /
dev /
deno.jsonc /
deno.json /
deno.jsonc;
如果没有找到配置文件,Deno 会按照没有配置文件的方式运行程序。
并且,Deno 的 LSP 也会自动寻找配置文件。它会把相同的文件目录作为工作空间的根目录来寻找配置文件,并且向上遍历文件夹寻找配置文件。
我们会在接下来的时间继续迭代这个功能,期待大家的反馈。
Error.cause 会打印全部堆栈信息
Error.cause 是一个新引入的用来表示错误原因的属性。Deno 从 v1.13 开始支持了这个属性,然而,这个原因并不是所有堆栈的类型(比如未捕获的错误)。现在我们修复了这个问题,如果有未捕获的错误,也会显示错误原因。
例子:
// error_cause.js
function fizz() {
throw new Error('boom!');
}
function bar() {
try {
fizz();
} catch (e) {
throw new Error('fizz() has thrown', { cause: e });
}
}
function foo() {
try {
bar();
} catch (e) {
throw new Error('bar() has thrown', { cause: e });
}
}
foo();
在 v1.18 之前:
$ deno run error_cause.js
error: Uncaught Error: bar() has thrown
throw new Error("bar() has thrown", { cause: e });
^
at foo (file:///test.js:17:15)
at file:///test.js:21:1
v1.18:
error: Uncaught Error: bar() has thrown
throw new Error("bar() has thrown", { cause: e });
^
at foo (file:///test.js:17:15)
at file:///test.js:21:1
Caused by: Uncaught Error: fizz() has thrown
throw new Error("fizz() has thrown", { cause: e });
^
at bar (file:///test.js:9:15)
at foo (file:///test.js:15:9)
at file:///test.js:21:1
Caused by: Uncaught Error: boom!
throw new Error("boom!");
^
at fizz (file:///test.js:2:11)
at bar (file:///test.js:7:9)
at foo (file:///test.js:15:9)
at file:///test.js:21:1
test steps 接口稳定
Deno 1.15 通过 --unstable 引入了 test steps 功能。由于正面的社区反馈,我们稳定了这个功能。
这个接口可以让用户通过 Deno.test 定义测试的子步骤。这些子步骤有自己的 sanitizer scopes ,并且使用 test runner 时会用缩进进行显示。这个接口因为足够普遍,所以可以通过增加一些垫片来模拟 mocha 或者 node-tap 等测试的场景。这个 PR 详细解释了这个接口的细节。
下面是一个使用这个新 API 的例子。这个例子创建了一个数据库连接,在子测试里做了一些查询,然后关闭了连接:
Deno.test('database test', async (t) => {
const db = await Database.connect('postgres://localhost/test');
await t.step('insert user', async () => {
const users = await db.query(
"INSERT INTO users (name) VALUES ('Deno') RETURNING *",
);
assertEquals(users.length, 1);
assertEquals(users[0].name, 'Deno');
});
await t.step('insert book', async () => {
const books = await db.query(
"INSERT INTO books (name) VALUES ('The Deno Manual') RETURNING *",
);
assertEquals(books.length, 1);
assertEquals(books[0].name, 'The Deno Manual');
});
db.close();
});
如果用 Mocha 风格来写测试是:
describe('database test', () => {
let db: Database;
beforeAll(async () => {
db = await Database.connect('postgres://localhost/test');
});
it('insert user', async () => {
const users = await db!.query(
"INSERT INTO users (name) VALUES ('Deno') RETURNING *",
);
assertEquals(users.length, 1);
assertEquals(users[0].name, 'Deno');
});
it('insert book', async () => {
const books = await db!.query(
"INSERT INTO books (name) VALUES ('The Deno Manual') RETURNING *",
);
assertEquals(books.length, 1);
assertEquals(books[0].name, 'The Deno Manual');
});
afterAll(() => {
db!.close();
});
});
如果你对 Mocha 更熟悉,我们写了一个垫片程序来让你使用这个接口:gist.github.com/lucacasonat….
FFI 接口改进
这次发布增加了一些不稳定的 FFI 接口。
我们看到一些使用 FFI 接口的有趣的项目,可以从这些项目看到 FFI 接口的威力:
Symbol 类型推断
通过动态库的 symbol 的定义,TypeScript 可以推断使用的方法是否符合预期的 symbol 类型。
const dylib = Deno.dlopen('dummy_lib.so', {
method1: { parameters: ['usize', 'usize'], result: 'void' },
method2: { parameters: ['void'], result: 'void' },
method3: { parameters: ['usize'], result: 'void' },
} as const);
// 正确调用
dylib.symbols.method1(0, 0);
// error: TS2554 [ERROR]: Expected 2 arguments, but got 1.
dylib.symbols.method1(0);
// 正确调用
dylib.symbols.method2(void 0);
// TS2345 [ERROR]: Argument of type 'null' is not assignable to parameter of type 'void'.
dylib.symbols.method2(null);
// 正确调用
dylib.symbols.method3(0);
// TS2345 [ERROR]: Argument of type 'null' is not assignable to parameter of type 'number'.
dylib.symbols.method3(null);
感谢 @sinclairzx81 实现了这个功能。
symbol 定义别名
当在动态库定义可用的 symbol 时,你可以给它们增加别名。这个功能的场景有:
- 你可以把动态库的方法名改为和你代码风格一致的名字,例如把 snake_case 改为 camelCase
- 给同一方法更多的重载,例如 一个会阻塞的方法,和一个异步的方法。
use std::{
thread::sleep,
time::Duration
};
#[no_mangle]
pub extern "C" fn print_something() {
println!("something");
}
#[no_mangle]
pub extern "C" fn sleep_blocking(ms: u64) {
let duration = Duration::from_millis(ms);
sleep(duration);
}
const dylib = Deno.dlopen(libPath, {
printSomething: {
name: 'print_something',
parameters: [],
result: 'void',
},
sleep_nonblocking: {
name: 'sleep_blocking',
parameters: ['u64'],
result: 'void',
nonblocking: true,
},
sleep_blocking: {
parameters: ['u64'],
result: 'void',
},
});
dylib.symbols.printSomething();
let start = performance.now();
dylib.symbols.sleep_blocking(100);
console.assert(performance.now() - start >= 100);
start = performance.now();
dylib.symbols.sleep_nonblocking().then(() => {
console.assert(performance.now() - start >= 100);
});
感谢 @DjDeveloperr 实现了这个功能。
Deno.UnsafeFnPointer 接口
提供了一个新的允许通过指针调用动态链接库的方法的接口 Deno.UnsafeFnPointer
#[no_mangle]
pub extern "C" fn add_u32(a: u32, b: u32) -> u32 {
a + b
}
#[no_mangle]
pub extern "C" fn get_add_u32_ptr() -> *const c_void {
add_u32 as *const c_void
}
const dylib = Deno.dlopen('dummy_lib.so', {
get_add_u32_ptr: { parameters: [], result: 'pointer' },
} as const);
const addU32Ptr = dylib.symbols.get_add_u32_ptr();
const addU32 = new Deno.UnsafeFnPointer(addU32Ptr, {
parameters: ['u32', 'u32'],
result: 'u32',
});
console.log(addU32.call(123, 456));
感谢 @DjDeveloperr 实现了这个功能。
支持 WebSockets 出站设置头信息
用户现在可以在出站的 WebSockets 上设置自定义的头信息。这些头信息可以通过 WebSockets 握手来发送,并且可以让服务器用这些信息来做一些工作。因为这个功能不是符合标准的功能,所以使用时应该小心。
你可以通过使用不稳定的 WebSocketStream 接口来使用这个功能:
const ws = new WebSocketStream('wss://example.com', {
headers: { 'X-Custom-Header': 'foo' },
});
这个头信息的签名和 fetch 方法的头属性的签名一致。
在入站 WebSockets 自动进行 keep-alive
通过 Deno.upgradeWebSocket 接口进行的客户端连接,现在可以自动处理 pong 信息。当没有更多其他信息时,客户端会自动发 ping 信息来保持连接。
你可以通过 Deno.upgradeWebSocket 的 idleTimeout 来配置 ping/pong 的间隔。默认时 120 秒,如果你设置为 0,则表示禁用自动 keep-alive 功能。
import { serve } from 'https://deno.land/std@0.121.0/http/server.ts';
serve((req: Request) => {
const { socket, response } = Deno.upgradeWebSocket(req, { idleTimeout: 60 });
handleSocket(socket);
return response;
});
function handleSocket(socket: WebSocket) {
socket.onopen = (e) => {
console.log('WebSocket open');
};
}
LSP 的改进
这次发布改进了一些 Deno LSP 的特性,你可以在 VS Code、JetBrains IDE 或者其他的 IDE 来使用这些特性。
Code lens for debugging tests
现在在 Deno.test 的 Debug 模式下,会在执行框上面增加一些快捷方式,当执行这些快捷方式时,你可以在编辑器的互动窗口里看到一些新的信息。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
感谢 @jespertheend 增加了这个功能。
改善注册补全
这次发布极大改善了使用 LSP 时的注册补全功能。现在对于你引入的 URL 下的资源,会有一些有用的自动提示。对于 deno/land/x 的包,你可以获得包信息,星星数,上次更新的日期,以及包在 doc.deno.land 的文档。
如果你想增加你自己发布的包的自动补全,请阅读这个文档。
上传视频封面
好的标题可以获得更多的推荐及关注者
测试覆盖度功能变得更健壮
这次发布带来了 deno coverage 功能的全面改造。
我们在社区收到老版本的测试覆盖度功能的很多反馈,都表示又慢又不准确。生成测试覆盖度慢的原因是因为源文件疏忽了类型检查。通过简化载入流水线,我们降低了很多这一块的时间。另外,如果覆盖数据和源文件不一致,deno coverage 会警告用户。另外,通过修改收集覆盖数据的逻辑改善了数据不准确的问题。
加快了启动时间
Deno 通过 V8 的快照来加快运行时和 TypeScript 编译器的启动速度。这些快照是二进制 blob 文件。我们在建造时生成这些文件,然后嵌入这些文件在 deno 执行文件里。为了让执行文件小一些,我们用 zlib 对这些文件进行了压缩。
通过一些调研,我们发现使用 zlib 压缩造成了启动速度慢的问题。从 v1.18 开始,我们使用 lz4 和 zstd 来压缩这些文件。这个改进让 JavaScript 运行时的启动速度加快了 33%,让 TypeScript 编译器加快了 10%。
更多信息请看:github.com/denoland/de….
感谢 @evanwashere 提供了这个改进。
V8 升级到了 9.8 版本
Deno 1.18 提供的 V8 引擎升级到了 9.8 版本。这个版本没有任何的 JavaScript 新功能,只是修复了一些 BUG,包括使用私有方法导致 crash 的 bug(Crash with basic private method example · Issue #12940 · denoland/deno)和一个 ES 模块载入的 bug(github.com/denoland/de…))