Electron 全屏 Kiosk 封装:HTML 转 EXE 与任务栏隐藏实战
简介这份资源面向希望把本地HTML页面封装成Windows桌面程序的开发者尤其适合已掌握HTML、CSS与JavaScript、想快速上手Electron打包流程的前端或全栈人员。内容围绕Electron框架展开覆盖将网页资源打包为.exe可执行文件的核心流程并重点实现全屏显示、隐藏任务栏、按Esc键退出程序以及自定义exe图标与文件名等实用功能同时涉及主进程与渲染进程间的IPC通信、electron-packager与electron-builder的打包参数配置。压缩包共约2000个文件以js、ts、map、json、md、license等为主另有cmd、yml、pak、dll、exe等运行时与配置相关文件整体约95.61MB目录结构完整便于对照查阅依赖与构建产物。目前已有1161人学习下载可作为从网页到桌面应用落地的参考工程帮助读者理解窗口创建、全屏控制、键盘事件监听与打包定制的实现思路。1. 从网页到桌面程序为什么我选 Electron 做全屏 kiosk 封装去年接手一个展厅项目客户给了一个已经做好的 HTML 页面要求是双击打开就全屏任务栏不能露出来按 Esc 才能退出exe 图标和文件名都得换成他们自己的。听起来简单但真动手才发现坑不少——浏览器 F11 全屏会留白边任务栏偶尔还会弹出来抢焦点打包出来的 exe 默认叫 electron.exe图标是那个原子模型客户一看就皱眉。这类需求其实很常见把现成的 HTML 页面不管是单文件还是带资源的目录封装成一个独立的 Windows 可执行文件运行起来像原生程序一样全屏铺满没有浏览器地址栏、没有标签页、没有右键菜单退出方式由你说了算。Electron 就是干这个的——它把 Chromium 和 Node.js 打包在一起让你用前端技术写桌面应用同时能直接调用系统级 API 控制窗口行为。适合谁看手里有 HTML 页面想转成 exe 的前端做数字标牌、展厅互动、工控面板、考试系统客户端的开发者或者单纯想给自己写的网页工具套个壳的人。下面我从项目结构开始把全屏、遮任务栏、Esc 退出、图标和文件名修改这几个核心点逐个拆开讲。2. 项目骨架与主进程BrowserWindow 的创建与全屏参数2.1 目录结构与依赖安装Electron 项目的结构很扁平核心就三个东西主进程入口、渲染进程页面、package.json。我一般会这样组织my-kiosk/ ├── main.js # 主进程入口 ├── preload.js # 预加载脚本可选 ├── index.html # 你的 HTML 页面 ├── assets/ # 页面资源 ├── build/ │ └── icon.ico # 打包用的图标 └── package.json初始化依赖只需要两步。先建 package.json然后装 electron 和打包工具npm init -y npm install electron --save-dev npm install electron-builder --save-dev这里选 electron-builder 而不是 electron-packager原因是它对图标替换、安装包生成、文件名自定义的支持更完整配置写在 package.json 里就行不用记一堆命令行参数。装完之后 package.json 里会有 devDependencies版本号按你实际装的来不锁死。2.2 主进程创建全屏窗口主进程是整个应用的入口负责创建窗口、控制生命周期。全屏和遮任务栏的关键都在 BrowserWindow 的构造参数里// main.js const { app, BrowserWindow, globalShortcut } require(electron); const path require(path); let mainWindow null; function createWindow() { mainWindow new BrowserWindow({ fullscreen: true, // 直接全屏铺满屏幕 frame: false, // 去掉标题栏和边框 kiosk: true, // kiosk 模式强制独占全屏 alwaysOnTop: true, // 置顶防止被其他窗口盖住 skipTaskbar: true, // 不在任务栏显示图标 autoHideMenuBar: true, // 隐藏菜单栏 webPreferences: { preload: path.join(__dirname, preload.js), contextIsolation: true, nodeIntegration: false } }); mainWindow.loadFile(index.html); // 注册 Esc 退出 globalShortcut.register(Escape, () { mainWindow.close(); }); mainWindow.on(closed, () { mainWindow null; }); } app.whenReady().then(createWindow); app.on(window-all-closed, () { app.quit(); });逐段说下参数。fullscreen: true让窗口直接以全屏启动但光有这个还不够——Windows 上任务栏有时会盖在上面。kiosk: true才是真正的独占模式它会强制窗口占据整个屏幕并且阻止用户通过常规方式退出这是遮住任务栏最可靠的一招。frame: false去掉系统标题栏skipTaskbar: true让程序不在任务栏留图标alwaysOnTop: true防止被其他窗口抢焦点。globalShortcut.register(Escape, ...)注册的是全局快捷键即使焦点不在窗口上也能响应。注意要在窗口关闭时注销否则多次启动会冲突。contextIsolation: true和nodeIntegration: false是安全默认值除非你的 HTML 页面需要直接调 Node API否则别开。2.3 加载页面与资源路径处理loadFile加载本地 HTML 文件路径相对于主进程文件所在目录。如果你的 HTML 引用了 CSS、JS、图片用相对路径就行Electron 会按文件系统解析。但有个坑打包成 asar 之后__dirname指向的是 asar 包内部loadFile依然能正常工作但如果你在 HTML 里用file://绝对路径就会挂。所以统一用相对路径别写死盘符。如果页面需要读取本地文件或者调用系统能力通过 preload 脚本暴露有限接口而不是直接开 nodeIntegration。这是安全底线也是 electron-builder 打包时不会报警告的前提。3. 全屏与退出机制kiosk 模式、Esc 监听与防误触3.1 kiosk 模式与 fullscreen 的区别很多人以为fullscreen: true就够了实际在 Windows 上它只是把窗口最大化到屏幕尺寸任务栏仍然可能因为某些触发条件比如鼠标移到屏幕底部弹出来。kiosk: true是 Chromium 提供的独占全屏模式它会接管整个显示区域任务栏被完全遮挡用户也无法通过 AltTab 切出去取决于系统设置。我一般两个都开fullscreen: true保证启动即全屏kiosk: true保证任务栏不露头。如果只开 kiosk 不开 fullscreen某些 Electron 版本启动时会先闪一下窗口模式再进全屏体验不好。3.2 Esc 退出的三种实现方式Esc 退出有三种做法各有适用场景第一种是全局快捷键就是上面代码里的globalShortcut.register(Escape, ...)。优点是即使页面失焦也能响应缺点是会占用系统级快捷键如果同时开了其他程序可能冲突。第二种是在渲染进程里监听键盘事件// 在 index.html 的脚本里 document.addEventListener(keydown, (e) { if (e.key Escape) { window.close(); // 需要 preload 暴露 close 方法 } });这种方式需要 preload 暴露window.close因为默认情况下渲染进程不能直接关窗口。优点是只在页面聚焦时生效不会干扰系统。第三种是主进程监听before-input-eventmainWindow.webContents.on(before-input-event, (event, input) { if (input.key Escape) { mainWindow.close(); } });这种方式介于两者之间不需要全局快捷键也不依赖页面脚本。我一般用第一种做展厅场景保证一定能退出用第三种做普通桌面工具避免全局冲突。3.3 防止用户误触退出的加固如果这是给客户用的展示程序你可能不希望用户随便按 Esc 就退出。常见做法是加一个隐藏的退出组合键比如 CtrlShiftQ或者连续按三次 Esc 才退出let escCount 0; let escTimer null; globalShortcut.register(Escape, () { escCount; if (escCount 1) { escTimer setTimeout(() { escCount 0; }, 1000); } if (escCount 3) { clearTimeout(escTimer); mainWindow.close(); } });这段逻辑是1 秒内按满 3 次 Esc 才退出否则计数清零。这样普通用户乱按不会关掉程序维护人员知道暗号就能退。参数1000是毫秒按需调整。注意globalShortcut 注册的快捷键在应用退出前要调用globalShortcut.unregisterAll()否则下次启动可能注册失败。4. 打包与定制图标替换、文件名修改与 electron-builder 配置4.1 package.json 里的 build 配置electron-builder 的配置全部写在 package.json 的build字段里。下面是一个针对 Windows exe 的完整配置{ name: my-kiosk, version: 1.0.0, main: main.js, scripts: { start: electron ., dist: electron-builder --win }, build: { appId: com.example.mykiosk, productName: 展厅展示程序, directories: { output: dist }, win: { target: nsis, icon: build/icon.ico, artifactName: ${productName}.${ext} }, nsis: { oneClick: false, allowToChangeInstallationDirectory: true, createDesktopShortcut: true, shortcutName: 展厅展示程序 } } }关键字段解释productName决定 exe 的文件名和安装后的程序名这里写「展厅展示程序」打包出来就是「展厅展示程序.exe」。win.icon指向图标文件必须是.ico格式尺寸建议包含 256x256。artifactName控制输出文件名${productName}.${ext}表示用产品名加扩展名。nsis.oneClick设为 false 会生成带安装向导的安装包如果只想生成绿色版 exe把 target 改成portable。4.2 图标制作的坑Windows 的 ico 文件不是随便一张 png 改后缀就行。它需要包含多个尺寸16x16、32x32、48x48、256x256否则在某些场景下会显示模糊或者不显示。我一般用在线工具或者 ImageMagick 生成magick convert icon.png -define icon:auto-resize256,128,64,48,32,16 icon.ico这条命令把一张 png 转成包含 6 个尺寸的 ico。如果没有 ImageMagick用在线 ico 生成器也行但注意别用那些带水印的。4.3 打包命令与输出验证配置写好后运行npm run distelectron-builder 会自动下载对应平台的 Electron 二进制包然后打包。输出在dist目录下你会看到「展厅展示程序 Setup 1.0.0.exe」或者「展厅展示程序.exe」portable 模式。双击安装或直接运行检查三件事图标是不是你设置的、文件名对不对、全屏和 Esc 退出是否正常。如果图标没换成功八成是 ico 文件不合规或者路径写错了。如果文件名不对检查productName和artifactName的拼写。如果全屏失效确认kiosk和fullscreen都开了。5. 避坑与常见问题从白屏到任务栏弹出的排查记录5.1 打包后白屏开发时正常现象npm start能正常显示页面打包成 exe 后打开是白屏。原因HTML 里用了绝对路径或者__dirname在 asar 包内解析异常。常见的是script src/js/app.js这种以斜杠开头的路径在文件系统里会解析到盘符根目录。解决把所有资源引用改成相对路径script src./js/app.js或者script srcjs/app.js。如果用了 Vue/React 的 history 路由改成 hash 路由因为 file:// 协议不支持 history API。5.2 任务栏仍然会弹出来现象已经设了fullscreen: true但鼠标移到屏幕底部时任务栏还是冒出来。原因fullscreen只是窗口最大化不是独占模式。Windows 的任务栏有自动弹出机制。解决加上kiosk: true。如果 kiosk 模式下仍有问题检查是否被其他置顶窗口干扰可以再加alwaysOnTop: true和setAlwaysOnTop(true, screen-saver)。5.3 Esc 注册失败或冲突现象按 Esc 没反应或者偶尔能退偶尔不能。原因globalShortcut.register返回 false 表示注册失败通常是被其他程序占用了 Esc 键。另外如果在app.whenReady之前注册也会失败。解决在whenReady之后注册并检查返回值const ret globalShortcut.register(Escape, () { mainWindow.close(); }); if (!ret) { console.log(Esc 注册失败可能被占用); }如果确实被占用换用before-input-event方式。5.4 打包体积过大现象一个简单的 HTML 页面打包出来 200MB。原因Electron 本身包含 Chromium 和 Node.js基础体积就在 150MB 左右。这是正常现象不是配置问题。解决如果对体积敏感可以考虑用 Tauri 或 Neutralino 等更轻量的方案但它们对系统 API 的支持不如 Electron 全面。对于展厅场景200MB 完全可以接受。5.5 安装后桌面快捷方式名称不对现象exe 文件名对了但桌面快捷方式还是显示英文名。原因nsis.shortcutName没配置或者productName和shortcutName不一致。解决在 build.nsis 里显式设置shortcutName为中文名和productName保持一致。6. 二次开发与改造从固定页面到可配置 kiosk 框架如果你只是封装一个固定页面上面的代码已经够用了。但实际项目里往往需要更灵活页面地址可配置、退出快捷键可改、窗口尺寸自适应不同分辨率。我一般会把配置抽出来放到一个config.json里主进程启动时读取// config.json { url: index.html, exitKey: Escape, exitCount: 3, fullscreen: true, kiosk: true }主进程里读取并应用const fs require(fs); const config JSON.parse(fs.readFileSync(path.join(__dirname, config.json), utf-8)); function createWindow() { mainWindow new BrowserWindow({ fullscreen: config.fullscreen, kiosk: config.kiosk, frame: false, skipTaskbar: true, webPreferences: { preload: path.join(__dirname, preload.js) } }); mainWindow.loadFile(config.url); let count 0; globalShortcut.register(config.exitKey, () { count; if (count config.exitCount) mainWindow.close(); }); }这样改页面地址不用动代码改退出键也不用重新打包逻辑。更进一步可以把 config.json 放在 exe 同级目录打包时排除它这样部署后还能改。另一个常见改造是加一个本地 HTTP 服务让页面通过http://localhost:port加载而不是file://。这样做的好处是可以用 fetch 请求本地接口、可以用 Service Worker、可以避免 file 协议的各种限制。用 Node 的 http 模块起一个静态服务就行const http require(http); const serveStatic require(serve-static); // 需要 npm install serve-static const serve serveStatic(path.join(__dirname, www)); const server http.createServer((req, res) { serve(req, res, () { res.statusCode 404; res.end(Not found); }); }); server.listen(0, 127.0.0.1, () { const port server.address().port; mainWindow.loadURL(http://127.0.0.1:${port}/index.html); });listen(0)表示随机分配一个空闲端口避免和本机其他服务冲突。serve-static指向你的页面目录。这样页面里就可以正常用 fetch、localStorage、IndexedDB 等浏览器 API不受 file 协议限制。验证改造是否成功我一般走三步先npm start看开发模式是否正常再npm run dist打包看 exe 是否正常最后把 exe 拷到另一台没装开发环境的机器上跑一遍。第三步最关键很多路径问题和依赖缺失只有在干净环境里才会暴露。从那以后我每次做 Electron 封装都会在打包后把 exe 拷到一台没装 Node 的机器上跑一遍确认图标、全屏、退出、资源加载全部正常才交付。这个习惯帮我拦住了至少三次「开发机正常、客户机白屏」的事故。希望帮到你。本文还有配套的精品资源点击获取