
简介这份资源面向希望把本地HTML页面封装成Windows桌面程序的开发者尤其适合已掌握HTML、CSS与JavaScript、想快速上手Electron打包流程的前端人员。内容围绕Electron框架展开覆盖将网页资源打包为.exe可执行文件、窗口全屏显示、隐藏任务栏图标、监听Esc键退出程序以及通过electron-packager或electron-builder自定义exe图标与输出文件名等实用知识点并涉及版本管理、错误处理与跨平台测试等注意事项。压缩包共约2000个文件以js、ts源码及map映射文件为主辅以json配置、md说明文档、license许可文件与少量pak、exe等运行时资源整体约95.61MB目录结构完整便于对照查阅依赖与打包配置。目前已有1161人学习下载可作为从网页到桌面应用落地的参考工程帮助读者理解主进程与渲染进程通信、窗口属性设置及打包参数配置等关键环节。1. 从网页到桌面程序为什么我选 Electron 而不是直接打包浏览器快捷方式很多做内部工具的朋友都遇到过这个场景用 HTMLCSSJS 写好了一个数据看板或者操作面板本地双击打开就能用但一旦发给非技术同事问题就来了——有人用 Chrome有人用 Edge还有人默认浏览器是某个套壳的打开后地址栏、书签栏、插件栏全露在外面看着就不像个正经软件。更麻烦的是如果这个页面需要全屏展示比如车间里的看板、展厅里的查询机、收银台的操作界面浏览器那几行 UI 会直接破坏沉浸感。我试过几种方案用 kiosk 模式启动浏览器但不同浏览器参数不一样而且任务栏还是可能弹出来用 HTA兼容性玄学Win11 上各种报错用 NW.js打包体积和文档更新速度劝退。最后落到 Electron原因很直接——它把 Chromium 和 Node.js 一起打包你写的是网页得到的是一个真正的 exe窗口怎么显示、任务栏怎么处理、退出键怎么绑定全在你自己手里。这份资源要解决的就是这个完整链路把一个本地 HTML 文件或整个静态目录转成单文件 exe要求全屏、遮住任务栏、Esc 退出、自定义图标和文件名。适合手里有现成网页、不想重写、只想快速交付一个桌面壳的开发者。下面按我实际踩过的流程拆开讲。2. 环境搭建与项目初始化从零到能跑起来的 Electron 壳2.1 为什么不用 electron-forge 或 electron-builder 的默认模板Electron 的脚手架很多electron-forge、electron-builder、electron-packager 各有拥趸。我一开始用 electron-forge它默认生成的项目结构比较重带了一堆示例代码和配置对于“只想包一个本地 HTML”的需求来说清理成本比从头搭还高。后来我固定用最朴素的方式手动建 package.json装 electron 和 electron-builder 两个依赖自己写 main.js。这样每一步做了什么、哪个参数控制哪个行为心里有数。另一个原因是图标替换。electron-forge 的图标配置在不同版本里改过几次路径规则而 electron-builder 的 win.icon 参数一直很稳定直接指向 .ico 文件就行。对于需要频繁改图标和文件名的场景electron-builder 的配置更直观。2.2 初始化步骤与依赖安装先建一个空目录名字随意比如web-to-exe。在里面执行npm init -y npm install electron --save-dev npm install electron-builder --save-dev装完后 package.json 里会有两个 devDependencies。注意 Electron 的版本我写这篇文章时稳定版是 28.x但你不必追最新选一个 LTS 附近的版本即可。版本太高有时会遇到 Node API 变动版本太低又可能不兼容新版 Windows 的某些显示缩放行为。接下来建三个核心文件main.js、index.html、package.json的 build 字段。目录结构如下web-to-exe/ ├── main.js ├── index.html ├── package.json └── build/ └── icon.icoindex.html就是你的网页可以是一个单文件也可以是一个带 assets 的目录。如果是目录把入口 HTML 放在根下其他资源用相对路径引用即可。2.3 main.js 的最小可用写法与参数说明main.js 是 Electron 的主进程入口控制窗口行为。下面是我常用的最小配置逐行说明const { app, BrowserWindow, globalShortcut } require(electron); const path require(path); let mainWindow; function createWindow() { mainWindow new BrowserWindow({ width: 1920, height: 1080, fullscreen: true, // 启动即全屏 frame: false, // 去掉窗口边框和标题栏 autoHideMenuBar: true, // 隐藏菜单栏 icon: path.join(__dirname, build/icon.ico), // 任务栏图标 webPreferences: { nodeIntegration: false, // 安全起见页面里不暴露 Node contextIsolation: true } }); mainWindow.loadFile(index.html); // 注册 Esc 退出 globalShortcut.register(Escape, () { app.quit(); }); // 窗口关闭时注销快捷键避免残留 mainWindow.on(closed, () { globalShortcut.unregisterAll(); mainWindow null; }); } app.whenReady().then(createWindow); app.on(window-all-closed, () { app.quit(); });几个关键点fullscreen: true让窗口直接全屏但此时任务栏可能还会在鼠标移到屏幕边缘时弹出。要彻底遮住任务栏还需要配合frame: false和setAlwaysOnTop下一章细说。globalShortcut.register(Escape)是全局快捷键即使焦点不在窗口内也能触发退出。icon参数在开发模式下可能不生效打包后才会正确显示这是 Electron 的已知行为不用纠结。2.4 开发模式验证与常见启动报错写完 main.js 后在 package.json 的 scripts 里加一行scripts: { start: electron . }然后npm start。如果窗口正常弹出并加载了 index.html说明基础链路通了。常见报错有两个一是Cannot find module electron通常是 npm install 没装完或者 node_modules 被误删二是页面白屏多半是loadFile的路径写错或者 index.html 里有语法错误导致渲染进程崩溃。白屏时按CtrlShiftI打开开发者工具看 Console比盲猜快得多。3. 全屏与任务栏遮挡窗口参数、置顶策略和分辨率适配3.1 fullscreen 和 kiosk 模式的区别Electron 的 BrowserWindow 支持fullscreen: true和kiosk: true两种模式。fullscreen 是全屏但保留退出全屏的能力kiosk 是更严格的锁定模式用户无法通过常规快捷键退出。我一开始用 kiosk结果发现某些 Windows 版本下任务栏仍然会在特定操作后闪现而且调试时想切出来很麻烦。后来改用 fullscreen frame:false setAlwaysOnTop 的组合实测在 Win10 和 Win11 上都能稳定遮住任务栏。具体做法是在 createWindow 里加两行mainWindow.setAlwaysOnTop(true, screen-saver); mainWindow.setFullScreen(true);setAlwaysOnTop的第二个参数screen-saver是置顶层级比普通置顶更高能盖住任务栏。但注意这个层级在某些安全软件环境下可能被拦截如果发现任务栏还是弹出来先检查是否有安全软件在干预窗口层级。3.2 多显示器与分辨率适配的坑如果目标机器接了多个显示器fullscreen: true默认在主显示器全屏。如果希望指定显示器需要在创建窗口前获取screen模块的 displays 信息然后设置窗口的x、y坐标。常见做法是const { screen } require(electron); const displays screen.getAllDisplays(); const targetDisplay displays[1] || displays[0]; // 选第二个显示器没有就用主屏 mainWindow new BrowserWindow({ x: targetDisplay.bounds.x, y: targetDisplay.bounds.y, width: targetDisplay.bounds.width, height: targetDisplay.bounds.height, fullscreen: true, frame: false });分辨率适配方面如果网页本身是固定宽度布局在全屏后可能出现左右留白或内容被拉伸。建议在 HTML 的 head 里加meta nameviewport contentwidthdevice-width, initial-scale1.0并且用 CSS 的vh、vw单位替代固定像素。如果页面是给特定分辨率设计的可以在 main.js 里根据targetDisplay.bounds动态调整zoomFactor但这是最后手段优先改页面样式。3.3 防止用户通过 AltF4 或 Win 键退出Esc 退出是我们主动绑定的但用户可能按 AltF4 或者 Win 键。AltF4 会触发窗口的 close 事件可以在 mainWindow 上监听close并阻止默认行为mainWindow.on(close, (e) { e.preventDefault(); // 可以选择在这里什么都不做或者弹确认框 });但这样会导致 app.quit() 也无法关闭窗口所以需要加一个标志位在 Esc 触发时先设标志再退出。Win 键是系统级的Electron 层面无法完全屏蔽只能通过 kiosk 模式部分限制。如果场景要求严格禁止 Win 键需要配合系统组策略或第三方工具这超出了 Electron 的能力范围选型时要心里有数。3.4 任务栏遮挡的验证方法改完参数后怎么确认任务栏真的被遮住了我的做法是把鼠标移到屏幕最下方停留几秒看任务栏是否弹出。如果弹出检查setAlwaysOnTop是否在窗口 ready 之后调用以及是否有其他窗口抢了置顶层级。另一个验证点是按 WinD 显示桌面看我们的窗口是否还在最前面。如果不在说明置顶层级不够可以尝试screen-saver换成pop-up-menu或status不同 Windows 版本表现略有差异需要实测。4. 图标与文件名定制从默认 electron 图标到自定义 exe4.1 图标文件的格式要求与制作Electron 打包 Windows exe 时图标必须是 .ico 格式且建议包含多个尺寸16x16、32x32、48x48、256x256。如果只放一个尺寸在某些系统缩放比例下会显示模糊。我一般用在线工具或 ImageMagick 把 PNG 转成多尺寸 icomagick convert icon.png -define icon:auto-resize256,128,64,48,32,16 icon.ico如果没有 ImageMagick也可以用 Python 的 Pillowfrom PIL import Image img Image.open(icon.png) img.save(icon.ico, sizes[(256, 256), (128, 128), (64, 64), (48, 48), (32, 32), (16, 16)])生成的 icon.ico 放到 build 目录下main.js 里的icon路径指向它。4.2 electron-builder 配置productName 与 artifactNamepackage.json 里加 build 字段build: { appId: com.example.webtoexe, productName: MyTool, directories: { output: dist }, win: { target: portable, icon: build/icon.ico }, portable: { artifactName: MyTool.exe } }productName决定 exe 在系统中显示的名称artifactName决定输出文件名。如果 target 是portable生成的是一个单文件 exe双击即用不需要安装。如果 target 是nsis会生成安装包安装后 exe 在安装目录里。对于内部工具分发portable 更省事。注意appId虽然看起来不重要但 Windows 会用它来区分任务栏图标分组建议写成反向域名格式避免和别的应用冲突。4.3 打包命令与输出验证在 package.json 的 scripts 里加dist: electron-builder --win portable然后npm run dist。打包完成后dist 目录下会出现MyTool.exe。双击运行检查三件事图标是否替换成功、窗口是否全屏且遮住任务栏、Esc 是否能退出。如果图标没变清理一下%APPDATA%下对应的缓存目录再试Windows 图标缓存有时会延迟更新。4.4 文件名的动态修改与版本管理如果每次发版都要改文件名手动改 artifactName 很烦。可以在打包前用脚本读取 package.json 的 version 字段动态生成 artifactName// build-script.js const fs require(fs); const pkg JSON.parse(fs.readFileSync(package.json, utf8)); const buildConfig pkg.build; buildConfig.portable.artifactName MyTool_v${pkg.version}.exe; fs.writeFileSync(package.json, JSON.stringify(pkg, null, 2));然后在 dist 脚本前执行这个脚本。这样每次改 version 就能自动生成带版本号的文件名避免新旧版本混淆。5. 避坑与排查打包过程中最容易翻车的五个点5.1 打包后白屏开发模式正常现象npm start一切正常npm run dist后双击 exe 白屏。原因打包后文件路径变了loadFile(index.html)的相对路径基准是 exe 所在目录而不是项目根目录。解决用path.join(__dirname, index.html)或者app.getAppPath()拼接绝对路径。如果 index.html 在 asar 包里确认 electron-builder 没有把它排除掉。5.2 Esc 退出失效按了没反应现象开发模式 Esc 能退出打包后按 Esc 没反应。原因globalShortcut 在某些 Windows 环境下被其他应用占用或者窗口失焦后快捷键未注册成功。解决在app.whenReady()之后、窗口创建之前注册快捷键并且加一个globalShortcut.isRegistered(Escape)的判断。如果仍然失效改用mainWindow.webContents.on(before-input-event)监听键盘事件在渲染进程层面捕获 Esc。5.3 任务栏遮挡不彻底鼠标下移就弹出现象全屏后任务栏隐藏但鼠标移到屏幕底部时任务栏滑出。原因setAlwaysOnTop的层级不够或者窗口没有真正覆盖任务栏区域。解决确认frame: false和fullscreen: true同时生效尝试把置顶层级改为screen-saver。如果还不行在窗口 ready-to-show 事件里再次调用setAlwaysOnTop因为某些 Windows 版本在窗口显示后会重置层级。5.4 图标替换后任务栏显示默认 Electron 图标现象exe 文件图标变了但任务栏和窗口左上角还是 Electron 默认图标。原因main.js 里的icon参数在打包后路径不对或者 .ico 文件缺少小尺寸图层。解决确保icon路径用path.join(__dirname, build/icon.ico)并且 ico 文件包含 16x16 和 32x32 尺寸。如果仍然不显示检查 electron-builder 的 win.icon 配置是否指向同一个文件。5.5 打包体积过大超过 200MB现象一个简单的 HTML 页面打包后 exe 有 200MB 以上。原因Electron 本身包含 Chromium 和 Node.js基础体积就在 150MB 左右。解决如果对体积敏感可以在 electron-builder 里配置asar: true压缩资源排除不必要的 locales 文件只保留 zh-CN 和 en-US以及用files字段只打包必要文件。但要注意体积压缩有上限Electron 的底线就在那里如果要求 50MB 以内这个方案本身就不适合需要换 Tauri 或 WebView2。6. 进阶技巧用 before-input-event 替代 globalShortcut 做退出控制globalShortcut 有一个硬伤它是系统级的注册后即使窗口最小化也能触发。对于“Esc 退出”这个需求其实更合理的做法是在窗口获得焦点时监听键盘事件失焦时不响应。Electron 的before-input-event正好满足这个场景。具体写法是在 main.js 里mainWindow.webContents.on(before-input-event, (event, input) { if (input.key Escape input.type keyDown) { event.preventDefault(); app.quit(); } });这段代码的好处是只在窗口聚焦时生效不会干扰其他应用不需要注册全局快捷键避免被安全软件拦截而且可以精确控制按键组合比如只响应单独的 Esc不响应 CtrlEsc。对比两种方案方案生效范围被拦截风险适用场景globalShortcut系统全局较高窗口可能失焦但仍需响应before-input-event窗口聚焦时低全屏工具、看板、查询机我现在的习惯是如果目标机器上可能运行其他需要 Esc 的软件优先用 before-input-event如果确定是独占设备globalShortcut 更省心。另外before-input-event 还可以用来屏蔽其他按键比如禁止 F12 打开开发者工具、禁止 CtrlR 刷新页面对于交付给非技术用户的场景很实用。还有一个细节如果页面里有输入框用户按 Esc 可能是想取消输入这时候直接退出会显得很突兀。可以在渲染进程里监听 Esc如果焦点在输入框内就只清空输入否则再通知主进程退出。这需要在 preload 脚本里做进程间通信复杂度高一些但体验更好。从那以后我每次打包前都会在目标机器上实际跑一遍 Esc 退出、任务栏遮挡和图标显示确认无误再分发。希望帮到你。本文还有配套的精品资源点击获取