Tauri + Wake Lock 实战:双端状态机、重获锁与发布一致性
ScreenAwake 最初只想解决一个很小的问题演示、照着教程操作或等待任务时不要让屏幕先熄灭。真正开始实现后我发现“保持常亮”不是一个按钮而是两套不同的系统契约网页只能申请浏览器提供的 Screen Wake Lock桌面端则要持有操作系统级的电源抑制资源。它们可以共用界面却不能共用对生命周期的承诺。这篇复盘不讲“几小时做完一个小工具”只拆四个工程问题用户意图与资源状态为什么要分开、网页锁为何必须重获、桌面端如何确定性清理以及双端发布物怎样避免版本身份漂移。先定义两种产品承诺ScreenAwake 同时提供网页临时模式和 Tauri 桌面模式。两端看起来都只有“开启常亮”背后的约束完全不同。维度网页模式桌面模式核心能力navigator.wakeLock.request(screen)macOScaffeinate、WindowsSetThreadExecutionState、Linuxsystemd-inhibit可见性只有可见、活跃文档能可靠申请主窗口隐藏后仍可由进程持有资源资源句柄WakeLockSentinel可能被浏览器或系统释放子进程或系统电源状态必须由应用主动清理用户预期临时使用标签页需保持可见托盘、倒计时、开机启动、关闭窗口后继续主要失败面浏览器不支持、页面隐藏、低电量或系统策略权限、平台 API、进程残留、退出清理、安装签名MDN 明确说明Screen Wake Lock 只面向可见的活跃文档系统可以因为页面不可见、电量或平台设置释放锁。W3C 草案还把底层获取描述为 advisory-only调用成功不等于应用获得了绕过所有系统策略的保证。所以网页端的产品文案必须是“在支持的浏览器和允许条件下保持屏幕常亮”不能写成“永久阻止休眠”。桌面端能力更强也不能绕过企业 MDM、手动锁屏或组织安全策略。用户意图不等于资源状态网页端最容易写出的实现是拿state.active同时表示两件事用户想继续保持常亮以及当前 Wake Lock 仍然有效。问题是浏览器释放锁时active会变成false当标签页再次可见程序已经不知道用户是主动点了“停止”还是资源被系统暂时收走。ScreenAwake 后来把这两个状态拆开letwebWakeLock:WakeLockSentinel|undefined;letwebKeepAwakeRequestedfalse;asyncfunctionstart(){webKeepAwakeRequestedtrue;webWakeLockawaitnavigator.wakeLock.request(screen);}asyncfunctionstop(){webKeepAwakeRequestedfalse;awaitwebWakeLock?.release();webWakeLockundefined;}webKeepAwakeRequested是用户意图webWakeLock是当前资源句柄。前者只有用户操作才能取消后者可以被浏览器、系统或代码释放。这个拆分不只适用于 Wake Lock。WebSocket、麦克风流、蓝牙连接、文件句柄和长任务也应该区分“用户仍然需要”与“资源此刻可用”。否则一次短暂断开就会被误判为用户取消。被释放的 Sentinel 不能复用WakeLockSentinel被释放后不能重新激活如果用户意图仍然存在应用必须申请一个新对象。ScreenAwake 在release事件中先确认回调对应的仍是当前句柄再清理状态页面重新可见时只在用户仍有意图且句柄不存在或已释放时重获锁acquiredWakeLock.addEventListener(release,(){if(webWakeLockacquiredWakeLock)webWakeLockundefined;state.activefalse;state.powerGuardActivefalse;render();});document.addEventListener(visibilitychange,(){constshouldReacquiredocument.visibilityStatevisiblewebKeepAwakeRequested(!webWakeLock||webWakeLock.released);if(shouldReacquire)voidstart();});这里保留acquiredWakeLock局部引用是为了防止旧对象的延迟release事件把新句柄清掉。它和网络请求里的 request ID、搜索建议里的 sequence number 是同一类并发防线旧事件不能覆盖新状态。桌面端先持有主资源再叠加可选兜底桌面端没有把鼠标微动当成主方案。主路径是操作系统电源抑制锁macOS 启动caffeinate -d -iWindows 调用SetThreadExecutionStateLinux 启动systemd-inhibit。鼠标微动、Shift 短按和隐藏窗口 ping 都是可选策略。鼠标微动在 macOS 上还要先检查辅助功能权限键盘兜底默认关闭。这个顺序很重要能用系统提供的电源 API就不应先模拟用户输入。桌面状态机每次开始前先停止旧控制器再创建新资源和截止时间pubfnstart(self,options:AwakeOptions)-ResultAppStatus,String{self.stop();letmutcontrollerself.controller.lock().map_err(|_|状态锁不可用.to_string())?;controller.power_guardSome(PowerGuard::start()?);controller.deadlineoptions.duration_seconds.filter(|seconds|*seconds0).map(|seconds|Instant::now()Duration::from_secs(seconds));Ok(Self::status_locked(controller))}start()先调用stop()可以避免重复点击后残留两份caffeinate子进程、输入线程或倒计时。停止时再依次翻转原子标志、结束输入线程、停止窗口 ping、释放电源锁并清空截止时间。这是一条适合小工具的简单不变量任意时刻最多只有一组持有资源的控制器。关闭窗口不等于停止任务桌面端另一个容易混淆的状态是“窗口关闭”和“应用退出”。ScreenAwake 拦截主窗口关闭把窗口隐藏到托盘只有托盘退出或进程退出时才停止电源防护。.on_window_event(|window,event|{iflettauri::WindowEvent::CloseRequested{api,..}event{api.prevent_close();let_window.hide();}}).run(|app,event|{ifletRunEvent::ExitRequested{..}event{app.state::AppState().stop();}});如果产品把“关窗口”定义成“停止常亮”就不该隐藏到托盘如果定义成后台继续界面必须明确提示托盘也必须提供停止和退出入口。生命周期不是框架默认行为而是产品契约。双端发布要记录两种构建身份ScreenAwake 的网页端在main推送后自动部署 GitHub Pages桌面端则在v*标签上构建 Release。两条流水线本身没有问题但它们会让“当前版本”出现两种身份。本次核验时发布面核验身份结果GitHub Pages /main1802fa2922ac1ddaebb748b9649f9636cdbaae10包含返回可见时重获 Wake Lock 的修复桌面 Releasev0.1.2b6e5e8f7c8e6291553e9656cc8ed27f13c661cc4早于上述网页修复package.json/ Tauri 配置0.1.2两端仍显示同一语义版本号这意味着“版本号都是 0.1.2”不能证明网页与桌面包含同一提交。更稳妥的做法是让两个界面显示version short commit发布记录同时保存 Pages 提交、Release tag 和资产校验值。最小检查可以写成gitrev-parse HEADgitrev-parsev0.1.2^{commit}gitlog--onelinev0.1.2..HEAD如果网页修复也应该进入桌面包就应发布新 tag如果只影响网页也要在发布记录中明确两个分发面的能力差异。本次验证通过了什么我在 2026-08-02 对公开仓库和页面做了重新核验npm ci成功npm run build成功Vite 生产构建生成distcargo check --manifest-path src-tauri/Cargo.toml成功cargo test --manifest-path src-tauri/Cargo.toml命令成功但实际运行0 个单元测试、0 个文档测试Ego 真实浏览器打开公开网页页面标题、四档时长和navigator.wakeLock支持回读正常GitHub Releases 公开页显示v0.1.2为 Latest并能追溯到 tag 提交源码共 9 个主要前后端文件仓库内未发现 JavaScript / TypeScript 测试文件或 Rust#[test]。所以当前能说的是“源码可以完成前端生产构建和本机 Rust 静态检查”不能说“桌面端已被自动化测试覆盖”也不能说 Windows x64、macOS ARM64、托盘、开机启动、快捷键、倒计时和退出清理都已经完成本轮端到端回归。下一版最该补的三道门第一道是状态机测试。至少覆盖用户主动停止、页面隐藏自动释放、返回可见重获、旧 Sentinel 延迟事件、重复开始、倒计时到期和退出清理。第二道是发布物身份。网页和桌面都显示提交短 SHARelease 记录资产 SHA-256面向 macOS 公开分发前补齐 Developer ID 签名和公证而不是把临时签名写成完整分发能力。第三道是真机矩阵。网页端验证支持与不支持 Wake Lock 的浏览器、页面切后台、低电量和手动锁屏桌面端分别在 macOS ARM64、Windows x64 和计划支持的 Linux 环境验证系统 API 与退出清理。可复用检查表用户意图与当前资源句柄分开保存资源被系统释放时不误判为用户取消旧资源的延迟事件不能覆盖新资源重获资源前重新检查可见性、权限和系统条件重复开始前确定性停止上一组资源关闭窗口、停止任务和退出应用有明确区分主能力优先使用平台 API模拟输入只做可选兜底网页部署提交与桌面 Release tag 分别记录“测试命令成功”与“实际测试数量”分开报告未签名、公证、真机回归的能力不写成已完成。小工具真正难的地方通常不是把按钮点亮而是把“用户还想要什么”“系统现在给了什么”“资源何时必须收回”三件事分清楚。生命周期和发布身份一旦清楚网页与桌面才能共享产品体验而不是共享一组含糊承诺。来源与验证边界ScreenAwake 仓库网页端实现核验提交桌面状态机核验提交系统电源防护实现核验提交GitHub ReleasesMDN Screen Wake Lock APIW3C Screen Wake Lock 工作草案验证边界本文验证了公开网页、源码提交、前端生产构建、本机 Rustcheck与 Release 身份没有执行 Windows / macOS 安装包真机端到端回归也没有把cargo test的 0 个测试写成测试覆盖。

相关新闻

逻辑回归实战:从乳腺癌数据集到完整机器学习工作流

逻辑回归实战:从乳腺癌数据集到完整机器学习工作流

1. 项目概述:从数据到诊断的逻辑回归之旅 在机器学习的入门与实战领域,有一个数据集的地位堪比“Hello World”,那就是威斯康星州乳腺癌诊断数据集。它不像鸢尾花数据集那样简单,也不像MNIST那样庞大,但它完美地融合了…

2026/11/5 5:34:18 阅读更多 →
Python多线程编程:从基础到高级应用

Python多线程编程:从基础到高级应用

1. Python线程基础概念解析 在Python编程中,线程是最小的执行单元,也是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。理解线程对于编写高效、响应迅速的Python程序至关重要。 Python标准库中的 thread…

2026/11/3 3:55:32 阅读更多 →
虚拟电厂随机优化调度:蒙特卡洛与CPLEX实战

虚拟电厂随机优化调度:蒙特卡洛与CPLEX实战

1. 项目概述:虚拟电厂与随机优化调度虚拟电厂(Virtual Power Plant, VPP)作为能源互联网的核心技术之一,通过聚合分布式能源资源(DERs)实现与传统电厂等效的调度功能。这个MATLAB项目针对源(发电…

2026/10/26 18:03:11 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/6 15:21:35 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/7 12:08:40 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/6 22:01:11 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/6 22:01:11 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/7 12:08:40 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/6 22:01:09 阅读更多 →

日新闻

周新闻

月新闻