On this page
HTTP 服务
deno desktop 随 Deno v2.9.0 一起发布,但尚未进入稳定版。要现在试用它,请运行 deno upgrade canary 来安装
canary 构建。在该功能稳定之前,命令、配置键和 TypeScript API 仍可能发生变化。
deno desktop 应用通过本地 HTTP 提供其 UI,并将嵌入式 webview 指向该地址。这使应用结构与普通 Deno 网站完全一致。
Deno.serve() 是入口点,所有请求都会流经你的处理器,但无需管理端口,也不会暴露到远程网络。
工作原理 Jump to heading
当二进制程序启动时:
- 运行时会选择一个未使用的本地端口,并将
DENO_SERVE_ADDRESS环境变量设置为tcp:127.0.0.1:<port>。 - 你的代码调用
Deno.serve(...)。serve API 会读取DENO_SERVE_ADDRESS(在此模式下由 Deno 自身设置,而不是由用户设置)并绑定到该端口, 忽略你传入的任何端口。 - 当监听器准备就绪后,webview 会导航到
http://127.0.0.1:<port>。
你编写的处理器与任何 Deno HTTP 服务器所用的一样。没有桌面专用的服务 API。
Deno.serve((req) => {
const url = new URL(req.url);
if (url.pathname === "/api/hello") {
return Response.json({ hello: "world" });
}
return new Response(HOMEPAGE, {
headers: { "content-type": "text/html" },
});
});
const HOMEPAGE = `<!doctype html>
<html><body>
<h1>Hello, desktop</h1>
<button onclick="fetch('/api/hello').then(r => r.json()).then(console.log)">
Ping
</button>
</body></html>`;
deno desktop main.ts
默认导出形式也同样可用:
export default {
fetch(req: Request): Response {
return new Response("Hello!");
},
};
为什么使用本地 HTTP? Jump to heading
本地 HTTP 架构以极小的开销换取了对桌面应用很重要的特性:
- 浏览器和桌面端使用同一份代码。 首页、fetch、websocket 和
cookies 在
deno run和deno desktop中的行为完全一致。你可以在浏览器标签页中开发,并将相同代码作为桌面二进制文件发布。 - 没有特殊的模块系统。 导入、静态资源和模块级代码都会像在 Web 服务器上一样运行。
- 框架可无缝运行。 Next.js、Astro、Fresh 以及其他框架本就附带了一个
生产级 HTTP 服务器。
deno desktop会运行该服务器,并将 webview 指向它。参见 Frameworks。
代价是每次请求都要在 127.0.0.1 内多一次网络跳转。对于 UI 服务
(HTML、CSS、打包后的 JS、JSON API 响应)来说,这点开销可以忽略不计。
对于 Deno → webview 的高吞吐通信,如果开销很重要,请使用 bindings,它会完全绕过 HTTP,并通过进程内通道进行路由。
网络暴露 Jump to heading
绑定地址始终是 127.0.0.1(或 [::1])。即使你向
Deno.serve() 传入 0.0.0.0,编译后的二进制文件也绝不会绑定到公共接口。其他应用以及同一台机器上的其他用户都无法访问你的服务器。
如果你需要向其他机器上的用户提供服务(一个自托管的本地服务器),
不要把 deno desktop 用在你技术栈的那一部分。请使用带显式地址的
deno run,或者构建一个单独的服务。
自定义端口行为 Jump to heading
在 deno desktop 内部,你无法覆盖 Deno.serve() 绑定的端口。这是有意为之:webview 需要导航到与运行时监听相同的端口,而运行时是该值的唯一真实来源。
如果你需要知道服务器绑定到了哪里,请读取 DENO_SERVE_ADDRESS。它的格式为
tcp:127.0.0.1:<port>,因此当你需要一个 http://
URL 时,可以把端口部分拆出来:
const addr = Deno.env.get("DENO_SERVE_ADDRESS")!; // "tcp:127.0.0.1:54321"
const port = addr.split(":").pop();
console.log("正在监听:", `http://127.0.0.1:${port}`);
提供多个窗口 Jump to heading
当你创建额外的 windows 时,默认情况下它们都会从同一个本地 HTTP 服务器加载。可为不同窗口使用不同路径来区分:
const port = Deno.env.get("DENO_SERVE_ADDRESS")!.split(":").pop();
const settings = new Deno.BrowserWindow();
settings.navigate(`http://127.0.0.1:${port}/settings`);