当前位置: 首页 > news >正文

Electron 跨平台移植实战:从 Windows 到 macOS 的适配与 DMG 打包全记录

Electron 跨平台移植实战:从 Windows 到 macOS 的适配与 DMG 打包全记录

作者:tenxiaodao.top

本文详细记录了将 Windows 版 Electron 应用 Mineradio 适配到 macOS 并打包为 .dmg 安装包的完整过程,涵盖全屏功能修复、图标生成、手动打包等关键环节,附带常见踩坑经验。


写在前面

最近把一个基于 Electron 的沉浸式音乐播放器 Mineradio 从 Windows 搬到了 macOS 上,整个过程比预想中曲折一些——主要是 macOS 上transparent + frameless窗口的全屏限制,以及 electron-builder 在国内网络环境下的 DMG 打包问题。这篇文章把踩过的坑和解决方案完整记录下来,希望对正在做 Electron 跨平台适配的朋友有帮助。

我的个人网站 tenxiaodao.top 上会持续更新更多技术实践,欢迎访问。


1. 项目分析

Mineradio 是一款基于Electron的沉浸式音乐播放器,原始版本仅提供 Windows NSIS 安装包。

核心文件结构

Mineradio-1.0.10/ ├── desktop/ │ ├── main.js # Electron 主进程入口 │ ├── preload.js # 预加载脚本 │ └── overlay-preload.js # 覆盖层预加载脚本 ├── build/ │ ├── icon.ico # Windows 图标 │ ├── icon.png # PNG 图标(可用于生成 macOS 图标) │ └── installer.nsh # NSIS 安装脚本 ├── public/ # 前端静态资源 ├── server.js # 本地 HTTP 服务器 ├── dj-analyzer.js # 节奏分析模块 └── package.json # 项目配置

为什么可以在 macOS 上运行

Electron 本身是跨平台框架(Chromium + Node.js),所以只要项目结构是标准 Electron 架构,搬到 macOS 上跑就有先天基础:

  • package.json"main": "desktop/main.js"是标准 Electron 入口,与平台无关
  • 代码中已有平台判断逻辑,例如main.js中的if (process.platform !== 'darwin') app.quit()
  • build/icon.png可以直接用来生成 macOS 所需的.icns图标

2. 环境准备

检查 Node.js 版本

node--version# 需要 v18+npm--version# 需要 v8+

如果没有安装 Node.js,前往 nodejs.org 下载 LTS 版本。

设置国内镜像(加速依赖下载)

国内网络环境下,Electron 二进制文件和 npm 包下载可能很慢,建议配置镜像加速:

# npm 镜像(可选)npmconfigsetregistry https://registry.npmmirror.com# Electron 二进制镜像(必须,否则下载会卡住)exportELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"

注意:每次打开新终端都需要重新执行export,建议将其写入 shell 配置文件:

echo'export ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"'>>~/.zshrc

3. 安装依赖

cd~/Downloads/Mineradio-1.0.10# 确保 Electron 镜像已设置exportELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"# 安装所有依赖(包括 Electron)npminstall

验证安装

# 检查 Electron 二进制是否下载完成catnode_modules/electron/path.txt# 应该输出类似:node_modules/electron/dist/Electron.applsnode_modules/electron/dist/Electron.app# 应该存在 Electron.app 目录

快速验证

npmstart

如果一切正常,Mineradio 窗口将会弹出。


4. macOS 全屏功能修复

问题描述

macOS 上 Electron 窗口如果同时设置transparent: trueframe: false,调用win.setFullScreen(true)静默失败,窗口无法进入全屏。这是 Electron 在 macOS 上的一个已知限制。

Mineradio 的主窗口创建代码如下:

mainWindow=newBrowserWindow({...initialBounds,frame:false,// 无边框窗口transparent:true,// 透明背景// ...});

这两个属性的组合导致原生setFullScreen()在 macOS 上失效。

解决方案

使用setSimpleFullScreen()替代setFullScreen()setSimpleFullScreen不依赖 macOS 原生全屏动画,可以绕过透明无边框窗口的限制。

具体修改

1.toggleFullscreen函数

为 macOS 单独走setSimpleFullScreen逻辑:

functiontoggleFullscreen(win){if(!win||win.isDestroyed())return;if(win.isFullScreen()||windowFullscreenActive){exitFullscreenToWindow(win);return;}windowFullscreenActive=true;if(process.platform==='darwin'){// macOS: transparent frameless windows 无法使用原生 setFullScreenwin.setSimpleFullScreen(true);// 手动扩展窗口覆盖整个屏幕constdisplay=screen.getDisplayMatching(win.getBounds());win.setBounds(display.bounds,false);}else{win.setFullScreen(true);}sendWindowState(win);}
2.exitFullscreenToWindow函数

退出时对应使用setSimpleFullScreen(false)

functionexitFullscreenToWindow(win){if(!win||win.isDestroyed())return;windowFullscreenActive=false;if(process.platform==='darwin'&&win.isSimpleFullScreen()){win.setSimpleFullScreen(false);applyWindowedBounds(win);return;}if(!win.isFullScreen()){applyWindowedBounds(win);return;}letapplied=false;constapplyOnce=()=>{if(applied||!win||win.isDestroyed()||win.isFullScreen())return;applied=true;applyWindowedBounds(win);};win.once('leave-full-screen',()=>setTimeout(applyOnce,50));win.setFullScreen(false);setTimeout(applyOnce,500);}
3. Escape 键退出全屏
mainWindow.webContents.on('before-input-event',(event,input)=>{if(input.type==='keyDown'&&(input.key==='Escape'||input.code==='Escape')){constisMacFullScreen=process.platform==='darwin'&&(windowFullscreenActive||mainWindow.isSimpleFullScreen());if(mainWindow.isFullScreen()||isMacFullScreen){event.preventDefault();exitFullscreenToWindow(mainWindow);}}});

原代码只判断mainWindow.isFullScreen(),macOS 上使用simpleFullScreen时该值为false,需要额外判断windowFullscreenActiveisSimpleFullScreen()

4. HTML 全屏退出时避免冲突
mainWindow.on('leave-html-full-screen',()=>{htmlFullscreenActive=false;if(process.platform!=='darwin'||!windowFullscreenActive){setTimeout(()=>applyWindowedBounds(mainWindow),50);}});

macOS 上如果处于simpleFullScreen状态,HTML 全屏退出时不应重置窗口尺寸,否则会与简单全屏冲突。

验证

npmstart

启动后点击全屏按钮,确认窗口能正常进入和退出全屏。


5. 打包为 .dmg

生成 macOS 图标

macOS 应用需要.icns格式的图标,可以使用系统自带的iconutil工具从 PNG 生成:

cd~/Downloads/Mineradio-1.0.10# 创建 iconset 目录mkdir-pbuild/icon.iconset# 使用 sips 生成所有需要的尺寸sips-z1616build/icon.png--outbuild/icon.iconset/icon_16x16.png sips-z3232build/icon.png--outbuild/icon.iconset/icon_16x16@2x.png sips-z3232build/icon.png--outbuild/icon.iconset/icon_32x32.png sips-z6464build/icon.png--outbuild/icon.iconset/icon_32x32@2x.png sips-z128128build/icon.png--outbuild/icon.iconset/icon_128x128.png sips-z256256build/icon.png--outbuild/icon.iconset/icon_128x128@2x.png sips-z256256build/icon.png--outbuild/icon.iconset/icon_256x256.png sips-z512512build/icon.png--outbuild/icon.iconset/icon_256x256@2x.png sips-z512512build/icon.png--outbuild/icon.iconset/icon_512x512.png sips-z10241024build/icon.png--outbuild/icon.iconset/icon_512x512@2x.png# 转换为 .icnsiconutil-cicns build/icon.iconset-obuild/icon.icns# 验证ls-labuild/icon.icns

配置 package.json

package.json中添加 macOS 构建脚本和配置:

"scripts":{"start":"electron .","build:win":"electron-builder --win nsis","build:win:dir":"electron-builder --win dir","build:mac":"electron-builder --mac dmg","build:mac:dir":"electron-builder --mac dir"}

build字段中添加macdmg配置:

"mac":{"executableName":"Mineradio","icon":"build/icon.icns","category":"public.app-category.music","hardenedRuntime":true,"gatekeeperAssess":false,"target":[{"target":"dmg","arch":["arm64","x64"]}]},"dmg":{"title":"${productName} ${version}","icon":"build/icon.icns","contents":[{"x":130,"y":220},{"x":410,"y":220,"type":"link","path":"/Applications"}]}

配置要点:

字段说明
executableName可执行文件名
iconmacOS 图标路径(.icns 格式)
categoryApp Store 分类,音乐类为public.app-category.music
hardenedRuntimemacOS 安全特性
arch同时支持 Apple Silicon(arm64)和 Intel(x64)

执行打包

cd~/Downloads/Mineradio-1.0.10exportELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"npmrun build:mac

打包过程中 electron-builder 会:

  1. 下载 macOS 版 Electron 运行时(约 113MB)
  2. 将代码和运行时组合为.app
  3. 执行 ad-hoc 签名(无需 Apple 开发者账号)
  4. 生成.dmg安装包

如果 electron-builder DMG 制作失败

国内网络环境下,electron-builder 的dmg-builder工具可能下载失败(404 错误)。这时可以用分步方案:

第一步:只打包 .app

npx electron-builder--macdir

dist/mac-arm64/下生成Mineradio.app

第二步:使用 hdiutil 手动创建 DMG

cd~/Downloads/Mineradio-1.0.10/dist# 创建临时目录mkdir-pdmg_temp# 复制 .appcp-Rmac-arm64/Mineradio.app dmg_temp/# 创建 Applications 快捷方式ln-s/Applications dmg_temp/Applications# 生成 DMGhdiutil create-volname"Mineradio 1.0.10"\-srcfolderdmg_temp\-ov-formatUDZO\Mineradio-1.0.10-arm64.dmg# 清理临时文件rm-rfdmg_temp

参数说明:

参数说明
-volnameDMG 挂载后显示的卷名
-srcfolder源文件夹路径
-ov覆盖已有文件
-format UDZOzlib 压缩格式

最终产物位于dist/Mineradio-1.0.10-arm64.dmg,约 141MB。


6. 安装与使用

安装步骤

  1. 双击Mineradio-1.0.10-arm64.dmg
  2. Mineradio拖到Applications文件夹
  3. 等待复制完成

首次启动注意事项

macOS 会提示"无法验证开发者",因为应用没有经过 Apple 公证签名。解决方法:

方案一:右键打开

右键点击 Mineradio.app → 选择「打开」→ 在弹出的对话框中再次点击「打开」

方案二:系统设置

系统偏好设置 → 隐私与安全性 → 找到被阻止的 Mineradio → 点击「仍要打开」

首次确认后,后续启动不再提示,也可以使用 Spotlight 搜索 “Mineradio” 快速启动。


7. macOS 上的功能限制

功能限制原因影响说明
桌面壁纸模式使用 WindowsWorkerWAPI(user32.dll无法将播放器嵌入桌面壁纸层
桌面歌词鼠标穿透依赖 WindowsGetAsyncKeyState中键切换锁定/解锁不可用
桌面快捷方式自动创建使用 Windows.lnk文件无影响,拖拽到 Applications 即可

正常运行的功能

  • 音乐播放、搜索、歌单管理
  • 网易云音乐登录与播放
  • QQ 音乐登录与音源补充
  • 歌词显示
  • 全屏模式(已修复)
  • 粒子视觉 / Emily 播放态
  • 3D 歌单架
  • 天气电台
  • 更新检测

附录:完整修改文件清单

文件修改内容
desktop/main.jsmacOS 全屏逻辑修复(4 处修改)
package.json添加build:mac/build:mac:dir脚本和mac/dmg构建配置
build/icon.icns新增,从icon.png转换生成的 macOS 图标

本文发布于 tenxiaodao.top,更多技术实践欢迎关注。

http://www.gsyq.cn/news/1590234.html

相关文章:

  • 大语言模型推理加速:SPEQ位共享量化技术解析
  • 西宁砂石料能送到周边县城吗
  • DRAM、NAND Flash、HBM 未来发展前景
  • 5分钟搞定FanControl中文设置:Windows风扇控制彻底汉化指南
  • Appium跨界Windows桌面自动化测试:统一技术栈实战指南
  • 遗传算法第二部分:选择压力、交叉算子与自适应变异机制解析
  • 2026深度实测|Cursor高性价比平替实测!中文Vibe Coding迭代能力全对比
  • 当下即是:当手机成为此刻
  • 空间计算驱动的企业GEO实践:佛山园区与中山制造案例的技术路径分析
  • 01_visual_studio环境配置及C++基本概念入门
  • Docker第3天:Dockerfile、Compose、Swarm、Machine学习整理
  • 机器学习新手生存指南:从环境配置到模型部署的实操路径
  • 深度评测:企业采购Token服务商,一张表打满5个维度
  • 导师推荐!2026年首选推荐的专业降AI率工具
  • Qwen2.5-VL本地部署实战:边缘多模态推理全链路指南
  • 2026深度实测:vibe coding优势全解析——企业级AI开发选型实战指南
  • DolphinDB工业数据质量:完整性检查与修复
  • 动图魔方技术拆解 10:GIF 多帧重编辑的 ImageSource 与 PixelMapList 实践
  • 铁电MEMS突触技术:神经形态计算新突破
  • MuleSoft企业级AI编排:LLM安全接入核心系统的实战方法论
  • 2026实测:两款主流AI编程工具全流程vibe coding体验对比
  • LSTM股票方向预测:分类建模与置信度输出实战
  • VMware虚拟机从入门到精通:完整安装指南
  • 用pytest构建AI应用测试体系:从语义断言到CI/CD集成
  • 线性代数直觉:用Python形状思维打通机器学习矩阵运算
  • 深度学习图像去重算法:3大技术方案实现高效重复图片检测
  • 模板驱动文档自动化:结构化内容注入与四层引擎设计
  • 如何深度解析QQ数据库加密机制:专业级跨平台解密实战指南
  • Android性能测试实战:Monkey与SoloPi工具组合使用指南
  • 企业级应用SQL注入漏洞深度剖析:从原理到实战复现