Skip to main content
On this page

Windows

即将随 Deno 2.9 提供

deno desktop 随 Deno v2.9.0 发布,目前尚未进入稳定版。要立即试用,请运行 deno upgrade canary 来安装 canary 构建版本。在该功能稳定之前,命令、配置键和 TypeScript API 仍可能发生变化。

Deno.BrowserWindow 类用于控制原生 窗口。当你的二进制程序启动时,会自动打开一个窗口,并导航到你的本地 HTTP 服务器。你创建的第一个 new Deno.BrowserWindow() 会接管这个初始窗口;此后的每次创建都会打开一个新窗口。所有窗口共享同一个 Deno 运行时:每个进程只有一个异步运行时,无论打开了多少个窗口。

创建窗口 Jump to heading

// 第一次创建会接管隐式启动窗口。
const win = new Deno.BrowserWindow({ title: "我的应用" });

// 后续创建会打开额外窗口。
const base = Deno.env.get("DENO_SERVE_ADDRESS")!; // "tcp:127.0.0.1:<port>"
const port = base.split(":").pop();

const settings = new Deno.BrowserWindow({
  title: "设置",
  width: 420,
  height: 320,
});
settings.navigate(`http://127.0.0.1:${port}/settings`);

构造函数接受一个 BrowserWindowOptions 对象:

选项 类型 默认值 说明
title string 窗口标题。
width number 800 以逻辑像素为单位的初始宽度。
height number 600 以逻辑像素为单位的初始高度。
x, y number 初始位置;如果省略则居中。
resizable boolean true 用户是否可以调整窗口大小。
alwaysOnTop boolean false 将窗口保持在其他窗口之上。
frameless boolean false 移除标题栏和窗口边框。仅创建时可设置。
noActivate boolean false 浮动的、不抢占焦点的面板。仅创建时可设置。
transparentTitlebar boolean false 将标题栏与内容融合。仅创建时可设置。

new Deno.BrowserWindow() 会立即打开(或接管)一个窗口。窗口会一直存在,直到调用 close() 或用户在操作系统中关闭它。

framelessnoActivatetransparentTitlebar 只能在创建时设置。frameless + noActivate 是托盘 / 菜单栏弹出面板的基础组件;参见 Tray.attachPanel

多个窗口彼此独立:每个窗口都有自己的大小、位置、焦点状态和 webview。它们可以导航到不同的路径或不同的源,设置各自的绑定,并触发各自的事件。

生命周期 Jump to heading

win.show();
win.hide();
win.focus();
win.close(); // 发送关闭请求,触发 "close" 事件
win.reload(); // 重新加载 webview 当前文档

if (win.isClosed()) { /* … */ }
if (win.isVisible()) { /* … */ }

每个窗口都有一个稳定的数字 id:

console.log(win.windowId);

关闭窗口不会停止运行时;进程会一直运行,直到所有窗口都关闭(或者你调用 Deno.exit())。

大小和位置 Jump to heading

const [w, h] = win.getSize();
win.setSize(800, 600);

const [x, y] = win.getPosition();
win.setPosition(100, 100);

if (win.isResizable()) { /* … */ }
win.setResizable(false);

if (win.isAlwaysOnTop()) { /* … */ }
win.setAlwaysOnTop(true);

大小以逻辑像素为单位。操作系统负责 HiDPI 缩放。

标题 Jump to heading

win.setTitle("我的应用:未命名");

使用稳定的前缀加上特定于文档的后缀;这是用户在窗口切换器、程序坞和任务栏中看到的内容。

导航 Jump to heading

const port = Deno.env.get("DENO_SERVE_ADDRESS")!.split(":").pop();
win.navigate(`http://127.0.0.1:${port}`);

导航支持嵌入式 webview 可以加载的任何 URL,最常见的是本地 HTTP 服务器(见 HTTP serving),也支持 https:// URL、file:// URL 和 data: URL。

对于多页面应用,使用本地 HTTP 服务器的路由,而不是切换窗口。对于模态对话框,优先创建子窗口而不是跳转离开。

事件 Jump to heading

Deno.BrowserWindow 是一个 EventTarget。使用 addEventListener 监听,或赋值给匹配的 on<event> 属性。

win.addEventListener("resize", (e) => {
  console.log("调整大小为", e.detail.width, e.detail.height);
});

win.onfocus = () => console.log("获得焦点");
win.onblur = () => console.log("失去焦点");
事件 触发时机
resize 窗口大小发生变化。
move 窗口位置发生变化。
focus 窗口获得焦点。
blur 窗口失去焦点。
close 用户请求关闭窗口。
keydown 窗口获得焦点时按下了某个键。
keyup 某个键被释放。
mousemove 指针在窗口上移动。
mouseenter 指针进入窗口。
mouseleave 指针离开窗口。
mousedown 按下了鼠标按钮。
mouseup 释放了鼠标按钮。
click 鼠标单击落在窗口上。
dblclick 鼠标双击落在窗口上。
wheel 发生了滚轮 / 触控板滚动。
menuclick 点击了应用菜单项。
contextmenuclick 点击了上下文菜单项。

指针和键盘事件与浏览器中的对应事件(KeyboardEventMouseEventWheelEvent)一致。resizemovemenuclickcontextmenuclick 是携带 detail 负载的 CustomEvent;菜单事件请参见 Menus

win.addEventListener("keydown", (e) => {
  if (e.key === "Escape") win.close();
});

在 webview 中运行 JavaScript Jump to heading

const result = await win.executeJs(
  "document.querySelectorAll('li').length",
);
console.log(result); // 当前页面上的 <li> 数量

executeJs 会在 webview 的主世界中运行代码,并以结果完成返回。该值会跨越 realm 边界,因此必须可 JSON 序列化;如果脚本抛出异常,返回的 promise 会以抛出的值拒绝。

如需更丰富的 Deno ↔ webview 通信,请改用 bindings

原生窗口句柄 Jump to heading

const surface = win.getNativeWindow();

getNativeWindow() 会将窗口的原生表面包装为 Deno.UnsafeWindowSurface,以便你使用 WebGPU 将内容渲染到其上。请先请求 GPU adapter;如果没有活动的 WebGPU 上下文,该调用会抛出:

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter!.requestDevice();
const surface = win.getNativeWindow();
const context = surface.getContext("webgpu");
// 使用 `device` 配置 `context` 并绘制…

一旦表面已被获取,close() 会降级为 hide(),这样支撑该表面的原生句柄就不会在 WebGPU 仍在使用时被销毁。

DevTools Jump to heading

win.openDevtools(); // Deno 和 renderer 两者
win.openDevtools({ deno: false }); // 仅 renderer
win.openDevtools({ renderer: false }); // 仅 Deno 运行时

参见 DevTools

关闭应用 Jump to heading

当没有打开的窗口且没有其他存活的异步任务(计时器、待处理的 fetch 等)时,运行时会退出。要显式退出:

Deno.exit(0);

如需阻止关闭(例如显示“是否保存?”对话框),请监听 close 并调用 event.preventDefault()

win.addEventListener("close", async (e) => {
  if (hasUnsavedChanges) {
    e.preventDefault();
    const ok = await win.executeJs("confirm('放弃更改?')");
    if (ok) win.close();
  }
});

Last updated on

Did you find what you needed?

编辑此页面
Privacy policy