网页包装为桌面应用(electron版)

该框架通过整合Chromium内核和Node.js运行时环境,允许开发者使用JavaScript、HTML及CSS构建兼容Windows、macOS与Linux系统的桌面应用,架构采用主进程处理系统级交互、渲染进程依托Chromium展示界面及IPC通信机制。(说白了就是包装)

一、项目概述

使用 Electron 将在线网页封装成独立的 Windows 桌面应用程序 Dr.aiVoss。

二、搭建electron

第一步(文件初始化)

新建一个空的文件夹,编辑器中打开这个文件夹(例如:my-electron-app)

cd 你新创建的文件

复制代码
npm init

初始化时可以一站式回车,当然也可以填入相应的内容信息。执行完之后会生成package.json文件。

第二步(安装electron)

复制代码
npm install --save-dev electron

安装之后

html 复制代码
npm install
html 复制代码
npm install --save-dev electron-builder

主要是运行整个项目以及打包整个项目生成exe文件。

安装的过程中可能会比较慢,影响因素是文件过大以及网络问题。

第三步(package.json 关键配置)

html 复制代码
{
  "name": "my-electron-app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "electron .",
    "build:win": "electron-builder --win"
  },
  "build": {
    "appId": "com.hithinker.voss",
    "productName": "Dr.aiVoss",
    "files": ["index.js", "package.json"],
    "win": {
      "target": [{"target": "nsis", "arch": ["x64"]}],
      "icon": "public/icon/icons.ico"
    }
  },
  "devDependencies": {
    "electron": "^39.2.7",
    "electron-builder": "^26.0.12"
  }
}

第四步(编写文件)

根目录创建 index.html

html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'none'">
    <title>Hello World! - My Electron App</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.
  </body>
</html>

根目录创建 index.js(我在原有基础配置上加上了屏幕的缩放以及控制台显示)

html 复制代码
// 在文件头部引入 Node.js 中的 path 模块
const path = require('path')
// app 它控制应用程序的事件生命周期。
// BrowserWindow 模块,它创建和管理应用程序 窗口。
const { app, BrowserWindow, globalShortcut } = require('electron');

// 保存主窗口引用
let mainWindow = null

// 创建浏览器窗口
function createWindow () {
    //__dirname 字符串指向当前正在执行脚本的路径 (在本例中,它指向你的项目的根文件夹)
    mainWindow = new BrowserWindow({
        width: 1000,
        height: 800,
        resizable: true,
        minWidth: 800,
        minHeight: 600,
        backgroundColor: '#fff',
        icon: path.join(__dirname, 'public/icon/icons.ico'),
        webPreferences: {
            nodeIntegration: true
        }
      })

      // 本地文件
      //mainWindow.loadFile('index.html')
      // 线上页面
       mainWindow.loadURL('https://www.csdn.net/')

      // 打开控制台
      // mainWindow.webContents.openDevTools()

      // 启用页面缩放功能
      mainWindow.webContents.on('did-finish-load', () => {
          // 设置缩放级别限制
          mainWindow.webContents.setVisualZoomLevelLimits(1, 5)
          // 启用视觉缩放
          mainWindow.webContents.setZoomFactor(1.0)
      })

      // 监听鼠标滚轮缩放
      mainWindow.webContents.on('zoom-changed', (event, zoomDirection) => {
          const currentZoom = mainWindow.webContents.getZoomFactor()
          if (zoomDirection === 'in') {
              // 放大
              mainWindow.webContents.setZoomFactor(Math.min(currentZoom + 0.1, 5.0))
          } else {
              // 缩小
              mainWindow.webContents.setZoomFactor(Math.max(currentZoom - 0.1, 0.25))
          }
      })

      // 注册缩放快捷键
      registerZoomShortcuts()

      // 监听窗口内的键盘事件(用于 F12 等按键)
      mainWindow.webContents.on('before-input-event', (event, input) => {
          // F12 打开/关闭开发者工具
          if (input.key === 'F12') {
              mainWindow.webContents.toggleDevTools()
          }
      })

};

// 保存当前缩放级别
let zoomLevel = 1.0

// 注册缩放快捷键
function registerZoomShortcuts() {
    // Ctrl + = 放大
    globalShortcut.register('CommandOrControl+=', () => {
        if (mainWindow) {
            zoomLevel = Math.min(zoomLevel + 0.1, 5.0)
            mainWindow.webContents.setZoomFactor(zoomLevel)
        }
    })

    // Ctrl + Shift + = 放大(针对不需要按 Shift 就能按 + 的键盘)
    globalShortcut.register('CommandOrControl+Shift+=', () => {
        if (mainWindow) {
            zoomLevel = Math.min(zoomLevel + 0.1, 5.0)
            mainWindow.webContents.setZoomFactor(zoomLevel)
        }
    })

    // Ctrl + - 缩小
    globalShortcut.register('CommandOrControl+-', () => {
        if (mainWindow) {
            zoomLevel = Math.max(zoomLevel - 0.1, 0.25)
            mainWindow.webContents.setZoomFactor(zoomLevel)
        }
    })

    // Ctrl+Shift+I 打开/关闭开发者工具
    globalShortcut.register('CommandOrControl+Shift+I', () => {
        if (mainWindow) {
            mainWindow.webContents.toggleDevTools()
        }
    })

    // F5 或 Ctrl+R 刷新页面
    globalShortcut.register('F5', () => {
        if (mainWindow) {
            mainWindow.reload()
        }
    })

    globalShortcut.register('CommandOrControl+R', () => {
        if (mainWindow) {
            mainWindow.reload()
        }
    })

    // Ctrl+Shift+R 强制刷新(清除缓存)
    globalShortcut.register('CommandOrControl+Shift+R', () => {
        if (mainWindow) {
            mainWindow.webContents.reloadIgnoringCache()
        }
    })
}

// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
app.on('window-all-closed', function () {
	if (process.platform !== 'darwin') app.quit()
})

// 应用退出时注销所有快捷键
app.on('will-quit', () => {
    globalShortcut.unregisterAll()
})

// 部分 API 在 ready 事件触发后才能使用。
// 在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口
app.whenReady().then(() => {
    createWindow()
    app.on('activate', function () {
      // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他打开的窗口,那么程序会重新创建一个窗口。
      if (BrowserWindow.getAllWindows().length === 0) createWindow()
    })
})

三、项目启动与打包

终端执行之后会直接打开网页

html 复制代码
npm start
html 复制代码
npm run build
或者
npm run build:win

直接拖拽到桌面或直接创建快捷方式(直接点击打开即可)

四、实现的功能

1. 窗口管理

  • ✅ 自定义窗口大小(1000x800)
  • ✅ 可调整窗口大小
  • ✅ 最小尺寸限制(800x600)
  • ✅ 自定义应用图标

2. 缩放功能

  • ✅ Ctrl + 鼠标滚轮 - 页面缩放
  • ✅ Ctrl + = - 放大(每次 +10%)
  • ✅ Ctrl + - - 缩小(每次 -10%)
  • ✅ Ctrl + 0 - 重置到 100%
  • ✅ 缩放范围:25% ~ 500%

3. 开发者工具

  • ✅ F12 - 打开/关闭控制台
  • ✅ Ctrl + Shift + I - 打开/关闭控制台

4. 页面刷新

  • ✅ F5 - 刷新页面
  • ✅ Ctrl + R - 刷新页面
  • ✅ Ctrl + Shift + R - 强制刷新(清除缓存)

五、注意事项

1. 图标要求

  • .ico 文件必须包含至少 256x256 像素
  • 推荐包含多尺寸:16x16, 32x32, 48x48, 256x256

https://www.ico51.cn/ 在线转换

实现自定义图标

问题分析:

  1. npm start 显示了自定义图标 ✅
  2. 打包时因为网络问题无法下载签名工具,导致图标无法嵌入到 exe 文件中 ❌

解决方案有几个:

方案1:手动下载签名工具缓存

复制代码
# 手动下载 winCodeSign-2.6.0.7z 并放到缓存目录

方案2:使用 ResHacker 或其他工具手动修改图标

方案3:暂时接受现状 (虽然打包失败,但 exe 文件已经生成在 dist/win-unpacked/Dr.aiVoss.exe,只是图标是默认的)

方案4:在能连接 GitHub 的环境下重新打包

由于网络问题,当前情况下:

  • 开发模式 (npm start) - 图标正常 ✅
  • 打包后 (dist/win-unpacked/Dr.aiVoss.exe) - 使用默认图标 ❌

建议:

  1. 先使用 dist/win-unpacked/Dr.aiVoss.exe 这个可执行文件
  2. 或者使用代理/VPN 后重新运行 npm run build:win
  3. 或者使用工具如 Resource Hacker 手动替换 exe 图标

2. 开发 vs 打包

  • 开发模式 :通过代码中的 icon 属性显示图标
  • 打包模式 :通过 package.json 的 build.win.icon 配置

3. 网络问题

  • 打包时需要下载签名工具(winCodeSign)
  • 如遇网络问题,可使用 dist/win-unpacked/ 中的 exe 文件

六、总结

通过 Electron 成功实现了:

  1. 将在线网页封装为独立桌面应用
  2. 完整的窗口管理功能
  3. 用户体验优化(缩放、刷新等)
  4. 自定义应用图标和名称
  5. 生成可分发的 .exe 文件

这种方式非常适合将现有的 Web 应用快速打包成桌面应用,无需重写代码。

相关推荐
HSunR26 分钟前
ruoyi 若依 自定义注解 参数校验
java·前端·数据库
拆房老料1 小时前
ONLYOFFICE也能像Microsoft Word和WPS一样分别设置中西文字体
前端·html·word·开源软件·wps
Solis程序员2 小时前
Stripe 幂等表:把「不确定的重试」变成「查表返回」
前端·安全·bootstrap·php·agent
李纲明2 小时前
WordPress 站点变慢:按 TTFB → 缓存 → 查询 → 前端 分层排查(含配置注意)
前端·缓存·性能优化·wordpress·后端开发
程序人生8882 小时前
PDF 转 Word 版式错乱、表格丢失?DocConverter Web 格式互转引擎的保真实践
前端·人工智能·opencv·机器学习·pdf·word
粥里有勺糖3 小时前
安利一下最近用的桌面“Agent” | T3 Code
前端·github·ai编程
xy34533 小时前
Axure 9.0 动态面板基本操作
前端·ui·html·axure·原型·产品设计
不停喝水3 小时前
【前端转全栈java速通课】 项目实战④7-11节 操作数据库-登录-注册-修改密码-注销-mubatis-plus简化crud-
java·前端·数据库
一水鉴天4 小时前
统一对象拓扑(UOT)设计全链定稿:从三件事分离到三继承 之1 20261010(豆包“查看聊天内容”继续)
前端·css
栖凤4 小时前
Java 的 try-catch 在 Agent 里失效了,我用了 5 个模式才兜住
java·前端·python