Electron 踩坑及优化记录

547 阅读6分钟

介绍了 Electron+react 开发桌面端应用时的一些问题和解决方法,例如:自定义导航栏、打包排除 node_modules 文件夹、解决音视频自动播放问题、解决 antd modal 弹框鼠标穿透事件等。

自定义导航栏

windows 默认的导航栏事件有:最大化、最小化、关闭,默认显示在右上角。

但项目中不需要最小化,不然 UI 适配有问题,而且这个样式也不太适配项目主题,因此自定义的实现是必要的了

ele-1.png

  1. 首先需要隐藏导航栏,在 main.js 中配置:frame: false,同时需要注释titleBarOverlay: true的配置,不然不起作用
const createWindow = () => {
  // 创建浏览窗口
  win = new BrowserWindow({
    show: false, // 默认不显示
    titleBarStyle: "hidden", // mac隐藏导航栏
    // titleBarOverlay: true, // win系统隐藏导航栏
    autoHideMenuBar: true, // 自动隐藏菜单栏
    frame: false, // window隐藏导航栏 titleBarOverlay同时也要隐藏
    // 预加载
    webPreferences: {
      // 需要在渲染进程中使用node
      nodeIntegration: true,
      contextIsolation: false,
      // 解决报错:DOMException: play() failed because the user didn't interact with the document first.
      autoplayPolicy: "no-user-gesture-required", // 禁用自动播放限制
    },
  });

  // 打开后最大化显示
  win.maximize();
  win.show();

  // electron-builder打包启动文件
  win.loadFile(path.join(__dirname, "./build/index.html"));

  // 打开开发工具
  // win.webContents.openDevTools();
};
  1. 自定义关闭事件实现:通过 ipcRendereripcMain 来实现自定义的关闭事件。

(1)在渲染进程(index.tsx)中发送关闭事件: 在渲染进程中,当用户触发关闭操作时,通过 ipcRenderer 发送一个自定义的关闭事件。

// 定义ipcRenderer
let ipcRenderer: any;
if (window.require) {
  const electron = window.require("electron");
  ipcRenderer = electron.ipcRenderer;
}

// 发送 defineClose 事件通知
if (ipcRenderer) {
  // 发送 defineClose 事件
  ipcRenderer.send("defineClose");
}

(2)在主进程中,添加一个监听器来处理 defineClose 事件,并执行相应的关闭操作。

// main.js文件
const {
  app,
  BrowserWindow,
  ipcMain, // 导入 ipcMain 模块
} = require("electron");

let win;

const createWindow = () => {
  // 创建浏览窗口
  win = new BrowserWindow({});
};

// 只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口
app.whenReady().then(() => {
  // 调用该函数来打开窗口
  createWindow();

  // 监听 defineClose 事件
  ipcMain.on("defineClose", () => {
    // 执行关闭操作
    if (win) {
      win.close(); // 关闭窗口
    }
  });
});

打包排除 node_modules 文件夹

默认情况下,使用 electron-builder 打包 Electron 应用时,打包后的应用会包含 node_modules 文件夹,这会导致打包后的应用体积过大,而且打包时间也会很漫长。因此,我们需要排除 node_modules 文件夹,只打包我们的项目代码。

ele-2.PNG

如上图所示:会将 node_modules 打包到win-ia32-unpacked/resource/app下。要解决这个问题,需要在 package.json 中添加如下配置:"files": ["!node_modules"],排除该文件夹

{
  "productName": "demo", // 项目名
  "version": "1.0.1", // 项目版本号
  "main": "main.js", // 入口文件
  "homepage": ".", // 解决本地运行白屏问题
  "scripts": {
    "electron-start": "nodemon --exec electron .", // 热启动运行本地服务命令,本地调试使用
    "packOs": "electron-builder --mac", // mac系统打包命令
    "packWin64": "electron-builder --win --x64", // win64位系统打包命令;不能在32位系统中安装,安装时会报错:需要64位操作系统
    "packWin32": "electron-builder --win --ia32" // win32位系统打包命令,可以在64位系统中安装
  },
  "build": {
    "productName": "demo", // 应用名称
    "asar": false, // 是否压缩build文件夹。调试时,可配置为false,查看是否正确打包
    "artifactName": "${productName}-setup-${version}.${ext}", // 自定义生成exe安装包名称
    "directories": {
      "output": "dist-${version}" // 打包输出文件夹
    },
    // 打包文件范围
    "files": [
      "build/**/*", // 项目打包后的文件夹
      "./main.js", // 入口文件
      "!node_modules" // 排除node_modules文件
    ],
    "extraMetadata": {
      "main": "./main.js"
    },
    "mac": {
      "icon": "build/icon.png" // 应用图标 512*521
    },
    "win": {
      "icon": "build/icon.ico" // 应用图标 256*256
    }
  },
  "devDependencies": {
    "electron": "21.4.4", // 指定版本,解决win7打包报错问题
    "electron-builder": "24.6.4"
  }
}

解决音视频自动播放问题

许多现代浏览器出于用户体验和性能考虑,默认情况下会限制视频的自动播放,特别是带有声音的视频。

如果视频没有声音或设置了静音,浏览器可能会允许自动播放。

报错信息如下所示:NotAllowedError: play() failed because the user didn't interact with the document first.

ele-3.png

  1. 在浏览器中,在用户与页面交互后(如点击按钮),浏览器会允许音频或视频自动播放。或者可以配置 muted 属性,先静音播放,然后与用户交互后,再打开声音。
// 定义组件
<video
  ref={videoRef}
  src="./media/bg.mp4"
  autoPlay
  // muted
  onEnded={handleVideoEnd}
  onCanPlay={handleCanPlay}
/>;

// 当视频可以播放时的处理函数
const handleCanPlay = () => {
  videoRef.current
    ?.play()
    .then(() => {
      // 如果视频被静音 显示提示信息
      if (videoRef.current?.muted) {
        handleTip();
      }
    })
    .catch((err: any) => {
      // 自动播放失败 显示提示信息
      handleTip();
    });
};

const handleTip = () => {
  modal.info({
    title: "温馨提示",
    content: (
      <div>
        <p>您的浏览器限制了视频的自动播放</p>
        <p>为了更好的观看体验,视频将默认静音播放</p>
        <p>请点击“知道了”以开始播放视频</p>
      </div>
    ),
    onOk: () => {
      if (!videoRef.current) return;
      // 触发手动播放
      videoRef.current.muted = false;
      videoRef.current.play();
    },
  });
};
  1. 在 Electron 中,如果不配置的话,也需要手动触发音频或视频的播放,才能实现自动播放。

而配置的话,只有一行代码的事,不需要上述的用户交互处理了。在 main.js 中添加如下配置:autoplayPolicy: 'no-user-gesture-required'

const createWindow = () => {
  // 创建浏览窗口
  win = new BrowserWindow({
    webPreferences: {
      autoplayPolicy: 'no-user-gesture-required' // 打开自动播放限制
    },
  });

antd modal 鼠标穿透事件

当 antd 的模态对话框(modal)打开时,默认情况下会在背景上覆盖一层遮罩(通常是半透明的黑色或灰色层)。这层遮罩的作用是防止用户与模态对话框下方的内容进行交互,从而确保用户必须先处理模态对话框中的内容。

这种设计是为了提高用户体验,避免用户在未处理重要信息的情况下继续操作其他内容。

但有时,可能希望用户在模态对话框打开时仍然能够与背景内容进行交互。例如,用户可能需要查看或操作背景中的某些信息,然后再关闭模态对话框。

可以设置 pointer-events 属性为 none,这样,遮罩层将不再拦截鼠标事件,鼠标事件会穿透到背景内容,用户可以继续与背景内容进行交互。

  • pointer-events:控制元素是否响应鼠标事件。当设置为 none 时,元素及其所有子元素都不会接收任何鼠标事件,鼠标事件会穿透到下面的元素。
.ant-modal-wrap {
  // 允许弹框底部操作
  pointer-events: none;
}

其他文章汇总:

  1. electron 打包 react 项目详解
  2. Electron PDF.js 预览文档问题解决
  3. electron 应用自解压打包及安全签名
  4. electron 监听键盘事件