构建你自己的 JavaScript 运行时,第 3 部分
本篇文章是《构建你自己的 JavaScript 运行时》和《构建你自己的 JavaScript 运行时,第 2 部分》的续篇。
更新于 2024-09-26:已将代码示例更新至最新版本的 deno_core
我们很高兴看到大家对这个关于构建自定义 JavaScript 运行时的系列文章的积极反响。一些人表示有兴趣了解如何使用快照来获得更快的启动时间。快照可以在(通常)可忽略不计的文件大小增加的情况下提供更高的性能。
在这篇博文中,我们将以第一部分和第二部分为基础,通过在构建脚本中创建 runtime.js
的快照,然后在 main.rs
中加载该快照,以加快我们自定义运行时的启动时间。
设置
如果你按照第一篇和第二篇博文进行操作,你的项目应该有三个文件
example.ts
:我们打算使用自定义运行时执行的 JavaScript 文件src/main.rs
:异步 Rust 函数,用于创建JsRuntime
的实例,该实例负责 JavaScript 执行src/runtime.js
:运行时接口,定义并提供将与main.rs
中的JsRuntime
互操作的 API
让我们编写一个 build.rs
文件,它将创建自定义运行时 runjs
的快照。
build.rs
中创建快照
在 在创建 build.rs
文件之前,我们先将 deno_core
添加为 Cargo.toml
中的构建依赖项
[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
脚本。然后,我们将添加一行来加载快照。
最后,为了加载快照,我们将在 RuntimeOptions
中添加 startup_snapshot
,它指向 RUNTIME_SNAPSHOT
,RUNTIME_SNAPSHOT
在 run_js
上方定义为我们在 build.rs
中创建的快照的静态字节切片。
就是这样!让我们尝试运行
cargo run -- example.ts
它应该可以工作了!
下一步是什么?
快照是一个出色的工具,可以帮助提高自定义运行时的启动速度。这是一个非常简单的示例,但我们希望它可以阐明 Deno 如何使用快照来优化性能。
通过本系列文章,我们展示了如何构建你自己的自定义 JavaScript 运行时,添加像 fetch
这样的 API,以及现在如何通过快照加速启动时间。我们很乐意听到你的反馈,所以如果你有任何希望我们涵盖的内容,请在 Twitter、YouTube 或 Discord 上告知我们。
不要错过任何更新 — 在 Twitter 上关注我们。