U13 · 页面不能直接决定写到电脑的哪里
v0.4 · 桌面选修,先完成 U03;工作台。产品参考:TINKER;平台原理参考 Electron preload。
把 JSON 小工具装进桌面窗口,增加“另存为”。界面仍负责输入与显示;选择磁盘路径、写文件属于主进程。先做这一个能力,不搬整个工具箱、不修改系统设置。
动手路线:每一步都有文件和结果
先完成 统一启动与恢复手册 的首次准备。以下命令均在自己的练习根目录执行;原文中的 api/web/desktop 概念对应本节列出的现成文件,不需要猜测如何搭空项目。
- 打开
u13/starter/model.ts,按 prepareExport 的顺序逐个实现。下面第1节起解释规则;每完成一个函数就重跑样例/测试。 - 执行以下三条,分别核对样例、行为和类型。未完成的 TODO 应失败,全部实现后测试应为
fail 0:
node tools/try.mjs u13
node tools/check.mjs u13
npm run check
-
打开
desktop/main.cjs、desktop/preload.cjs、desktop/renderer.js、desktop/save.cjs,找到对自己规则函数的调用。先用一句话记录输入从哪里来、结果写到哪里;读不懂的语法回到本节先修对应章节。 -
按 桌面启动与打包 依次安装、编译、开窗口、打包。本节下面逐项解释文件职责。
-
实际操作:开发窗口中先格式化,再另存为并取消,原文仍在且不写文件;再次保存检查中文和换行。打包后从产物重做同样操作。
-
解释与再做一次:页面能传建议名称,为什么不能直接决定绝对保存路径?取消与写入失败怎样区分? 在自己的 LEARNING.md 先写答案,再选本节末尾的变式,列一个新失败例子后修改。保持原用例通过。仅跑通配套界面、没有完成规则与解释,记录为“参考演示已观察”,不记录为自己完成作品。
卡在规则时按“函数输入→分支条件→返回值→参考实现”逐级看提示;卡在运行时按 故障表 定位。若想单独观察参考成品,在构建命令后加 --reference;页面会明确标出参考演示。观察完再去掉该参数重建,继续自己的 starter。
1. 先把导出规则与写文件分开
打开 u13/starter/model.ts。prepareExport 接建议名称、原始 JSON、是否取消,返回待写的 name/content 或 null。取消时直接返回 null;名称只允许约定的小写英数字/连字符加 .json,拒绝路径和保留名;JSON 有效时格式化并在结尾留换行。
运行样例、类型检查和核心测试。这里的 name 只是建议文件名,不是用户已经批准的绝对路径;函数不会访问磁盘。
2. 画出三个文件的职责
打开配套 desktop 目录中的 main.cjs、preload.cjs、index.html 和 renderer.js。main 创建窗口和处理另存为;preload 只暴露一个 saveJson({name,source});renderer 只收集输入和显示结果。不要把 ipcRenderer、fs 或任意文件路径操作整体暴露给页面。
按统一启动手册运行已固定版本并有锁文件的桌面样例,对照 Electron 首个应用 解释 package/main 入口。安装下载失败停在环境环节,核心练习仍可完成;不要改系统防护。平台安装包制作在第 6 步,空窗口能开不等于已经可分发。
3. 把规则编译成可加载 JavaScript
执行 node tools/build-desktop.mjs,它把自己完成的 u13/starter/model.ts 按 ES2022、CommonJS 编译到 desktop/dist-rules;main.cjs 用 require 读取生成文件。先编译再启动,不依赖 Electron 内嵌 Node 恰好支持直接运行 TS。
窗口 webPreferences 明确 contextIsolation: true、nodeIntegration: false,preload 使用绝对路径。只加载本地 index.html,不加载外部网页来获得桌面权限。让窗口先显示 U03 的输入/输出,功能与浏览器版一致。
4. 增加一个窄的桥接函数
preload 通过 contextBridge 暴露 saveJson,内部只调用固定频道 json:save;主进程 ipcMain.handle 验证参数是对象、name/source 为字符串且长度在本题限制内,再调用 prepareExport 做校验。题目限制 source 最多 64 KiB UTF-8;这与核心未设大小上限不同,须在主进程入口另验。
主进程使用系统另存为对话框,并提供建议名称。取消返回 {saved:false,canceled:true},不写文件;确认后只写对话框返回的路径,写入成功才返回 saved=true。写入失败向页面返回可理解的错误,不把成功与取消混用。
不要让 renderer 传入一个绝对路径便直接写入;保存对话框让用户决定位置。主进程还应拒绝非本窗口发来的调用,页面导航或新窗口请求须受控。
5. 主动测取消、失败和权限边界
先输入合法 JSON,点击保存后取消:应没有新文件且原文保留。再选学习目录,保存后用普通编辑器打开,检查缩进、中文与换行。最后在受控的不可写测试位置尝试,观察明确错误,不能显示已保存。
打开开发者工具验证页面没有直接 require/fs 能力;尝试超长 source、非法 name 和错误参数形状,应在主进程拒绝。核心测试不检查 IPC、窗口来源或实际文件权限,需要本机运行记录。
6. 打包、变式与完成边界
按 Electron 打包教程 为自己实际使用的平台制作包,记录依赖、系统、架构和输出文件。从包启动后重做合法保存与取消,不能只验证开发启动模式。没有签名和其他系统设备时,明确写“本机未签名教学包/其他平台未验证”,不声称跨平台发行完成。
独立变式:提供“压缩 JSON”导出选项,规则与桥接都明确接收该选项;非法选项必须拒绝。先完成原版本验收再扩展。阅读 TINKER 插件结构是后续选学,不要求此处把独立应用伪装成兼容插件。
暂停时关闭 Electron 窗口与开发服务,保存实际构建命令。完整单元成果包括源码、锁文件、核心测试、真实桌面保存/取消记录及本机打包结果;只有核心通过时记“规则阶段完成”。