自己动手打造 JavaScript 运行时,第 3 部分
这篇文章是 自己动手打造 JavaScript 运行时 和 自己动手打造 JavaScript 运行时,第 2 部分 的延续。
更新于 2024-09-26:已将代码示例更新到最新版本的 deno_core
我们对这个关于自己动手打造自定义 JavaScript 运行时的系列文章的积极反响感到高兴。 有些人表示有兴趣 了解如何使用快照来获得更快的启动时间。 快照可以在文件大小(通常)略微增加的情况下提供更好的性能。
在这篇博文中,我们将基于 第一部分 和 第二部分,在构建脚本中创建 runtime.js
的快照,然后在 main.rs
中加载该快照,以加快自定义运行时的启动时间。
开始设置
如果您按照 第一篇 和 第二篇博客文章 的步骤操作,您的项目应该包含三个文件
example.ts
:我们打算使用自定义运行时执行的 JavaScript 文件src/main.rs
:创建JsRuntime
实例的异步 Rust 函数,该实例负责 JavaScript 执行src/runtime.js
:运行时接口,它定义并提供与main.rs
中的JsRuntime
交互的 API
让我们编写一个 build.rs
文件,该文件将创建自定义运行时 runjs
的快照。
build.rs
中创建快照
在 在创建 build.rs
文件之前,让我们首先在 Cargo.toml
中添加 deno_core
作为构建依赖项
[package]
name = "runjs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.net.cn/cargo/reference/manifest.html
[dependencies]
deno_ast = { version = "0.42", features = ["transpiling"] }
deno_core = "0.311"
reqwest = "0.12"
tokio = { version = "1.40", features = ["full"] }
+ [build-dependencies]
+ deno_core = "0.311"
接下来,让我们在项目的根目录中创建一个 build.rs
文件。 在这个文件中,我们需要执行以下步骤
- 创建
src/runtime.js
的一个小扩展 - 构建指向快照的文件路径
- 创建快照
将上述步骤转换成代码,您的 build.rs
脚本应该如下所示
use deno_core::extension;
use std::env;
use std::path::PathBuf;
fn main() {
extension!(
// extension name
runjs,
// list of all JS files in the extension
esm_entry_point = "ext:runjs/src/runtime.js",
// the entrypoint to our extension
esm = ["src/runtime.js"]
);
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let snapshot_path = out_dir.join("RUNJS_SNAPSHOT.bin");
let snapshot = deno_core::snapshot::create_snapshot(
deno_core::snapshot::CreateSnapshotOptions {
cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"),
startup_snapshot: None,
skip_op_registration: false,
extensions: vec![runjs::init_ops_and_esm()],
with_runtime_cb: None,
extension_transpiler: None,
},
None,
)
.unwrap();
std::fs::write(snapshot_path, snapshot.output).unwrap();
}
主函数是 create_snapshot
,它接受几个选项。 让我们在下一节中详细介绍它们。
CreateSnapshotOptions
深入 create_snapshot
函数是一个很好的抽象层,它使用选项结构来确定快照的创建方式。 我们将使用以下选项来配置它
cargo_manifest_dir
:Cargo 将所有内容编译到的目录。 我们通过解析OUT_DIR
和RUNJS_SNAPSHOT.bin
来定义快照路径。extensions
:要包含在生成的快照中的扩展。 我们传递runjs
扩展,它是从src/runtime.js
构建的。
main.rs
中加载快照
在 目前,main.rs
文件的 run_js
函数加载 runjs
扩展。 我们将修改此函数,改为加载我们在 build.rs
中创建的快照
+ use deno_core::Snapshot;
// Other stuff…
+ static RUNTIME_SNAPSHOT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/RUNJS_SNAPSHOT.bin"));
extension!(
runjs,
ops = [
op_read_file,
op_write_file,
op_remove_file,
op_fetch,
],
- esm_entry_point = "ext:runjs/runtime.js",
- esm = [dir "src", "runtime.js"],
)
async fn run_js(file_path: &str) -> Result<(), AnyError> {
let main_module = deno_core::resolve_path(file_path)?;
let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions {
module_loader: Some(Rc::new(TsModuleLoader)),
+ startup_snapshot: Some(Snapshot::Static(RUNTIME_SNAPSHOT)),
- extensions: vec![runjs::init_ops_and_esm()],
+ extensions: vec![runjs::init_ops()],
..Default::default()
});
let mod_id = js_runtime.load_main_module(&main_module, None).await?;
let result = js_runtime.mod_evaluate(mod_id);
js_runtime
.run_event_loop(Default::default())
.await?;
result.await
}
我们将删除 esm
和 esm_entry_point
声明,因为它们已移至 build.rs
脚本。 然后,我们将添加一行来加载快照。
最后,要加载快照,我们将添加 startup_snapshot
到 RuntimeOptions
中,它指向 RUNTIME_SNAPSHOT
,RUNTIME_SNAPSHOT
在 run_js
之上定义为我们在 build.rs
中创建的快照的静态字节切片。
就是这样! 让我们尝试运行以下命令
cargo run -- example.ts
它应该可以正常工作!
接下来做什么?
快照是帮助提高自定义运行时启动速度的绝佳工具。 这是一个非常简单的示例,但我们希望它能帮助您了解 Deno 如何使用快照来优化性能。
通过这个系列,我们展示了如何构建您自己的自定义 JavaScript 运行时,添加像 fetch
这样的 API,现在通过快照来加快启动时间。 我们乐于收到您的反馈,如果您想让我们涵盖任何内容,请在 Twitter、YouTube 或 Discord 上告诉我们。
不要错过任何更新 - 在 Twitter 上关注我们。