Electron 在鸿蒙 PC 上跑 webview,我是怎么把首屏从 4.2s 干到 1.1s 的

Electron 在鸿蒙 PC 上跑 webview,我是怎么把首屏从 4.2s 干到 1.1s 的


一、事情是这样的

我手头在做一个 App,叫雷达鸭,鸿蒙 PC 版是直接用 Electron 套壳跑的。同一份代码,我同事在 Windows 上点开是 800ms 出首页,我在他那台鸿蒙 PC 伴侣上点开是......你看:

复制代码
[Windows 11]   首屏渲染:812ms
[鸿蒙 PC]      首屏渲染:4237ms

差了 5 倍。我当时第一反应是鸿蒙的 Chromium 内核版本老,于是查了一遍:

  • Electron 28.0 内置 Chromium 120
  • 鸿蒙 PC 的 WebView 内核(ArkWeb)版本号 120+ ✓

内核是新的。那为啥慢?

我用 app.getAppMetrics() 把进程指标全打出来,又用 Chrome DevTools 远程调试挂上去看 Network 面板,发现一个反常的现象:

所有静态资源(JS/CSS/图片)在鸿蒙 PC 上的 Waiting (TTFB) 时间几乎是 Windows 的 3 倍。

但这些资源都是 file:// 协议从本地读的,根本不应该有 TTFB。我盯着 DevTools 看了十分钟,差点把屏幕戳穿。

二、被我忽略的"假后台"机制

直到我翻到 Electron 官方一份边角文档------就是 Electron 性能优化建议 里第四小节,提到一个东西:

When a window is in the background, Electron will throttle timers and animations to reduce CPU usage. This can be useful for power consumption, but it can also cause performance issues if your app does heavy work in the background.

说白了,Electron 默认开启了一个后台节流机制。如果你的窗口在用户视角里是"非激活状态",它会自动降频渲染、节流 JS 定时器。

我一看,鸿蒙 PC 上我的应用窗口启动时的 z-order 是低于系统桌面图标的。我点开应用的瞬间,窗口从"后台"切到"前台",但 Electron 内部的状态机有约 200ms 的延迟才反应过来。

这 200ms 里,首屏的 HTML 已经加载完毕,但 JS 执行被节流,渲染管线被卡住。等到节流解除,开始绘制,4 秒就这么过去了。

三、那个被雪藏的参数

解决方案出乎意料地简单------关掉这个节流:

javascript 复制代码
// main.js
const { app, BrowserWindow } = require('electron')

app.commandLine.appendSwitch(
  'disable-renderer-backgrounding',
  '--disable-renderer-backgrounding'
)
app.commandLine.appendSwitch(
  'disable-background-timer-throttling',
  '--disable-background-timer-throttling'
)
app.commandLine.appendSwitch(
  'disable-backgrounding-occluded-windows',
  '--disable-backgrounding-occluded-windows'
)

app.whenReady().then(() => {
  const win = new BrowserWindow({
    width: 1280,
    height: 800,
    webPreferences: {
      backgroundThrottling: false,  // 这一行才是关键
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false,
    },
  })
  win.loadFile('index.html')
})

webPreferences.backgroundThrottling: false 是显式关闭窗口级节流,前面三个 appendSwitch 是从 Chromium 命令行层兜底。两个都得加,只加一个实测会漏掉一部分场景。

我改完打包,跑了一轮:

优化项 首屏
原始版本 4237ms
+ backgroundThrottling:false 2180ms
+ 三个 appendSwitch 1143ms
+ preload 拆分(见下) 1080ms

backgroundThrottling 这一项就吃掉了 50% 的优化空间。文档里就一句话带过,鬼知道踩坑的人有多少。

四、preload 拆分的隐藏收益

顺手再提一个相关技巧。Electron 的 preload 脚本默认是同步加载的,会阻塞首屏渲染。如果你的 preload 里有 require 了一堆 npm 包(特别是带 native binding 的),首屏会更慢。

我当时的 preload 里有个 require('sqlite3'),光初始化就 600ms。拆成异步加载:

javascript 复制代码
// preload.js
const { contextBridge } = require('electron')

// 同步的、必须阻塞的(少量)
contextBridge.exposeInMainWorld('appReady', true)

// 异步加载的(用 ipc 走)
contextBridge.exposeInMainWorld('db', {
  query: (sql) => window.electronAPI.query(sql),
})
javascript 复制代码
// main.js
ipcMain.handle('db:query', async (event, sql) => {
  const sqlite3 = require('sqlite3')  // 懒加载
  // ...
})

这样 sqlite3 不会在首屏阶段同步阻塞 window.appReady 的暴露。优化后能再砍 60-80ms,蚊子腿也是肉。

五、我的反思

官方文档不一定写得对 ------"backgroundThrottling 默认开启"这个行为,在桌面端是合理的(你切走窗口时省电),但在鸿蒙 PC 这种窗口 z-order 行为跟 Windows 不一样的环境里,就变成了性能陷阱。

我后来还发现另一个类似的情况:鸿蒙 PC 的 Electron 窗口在用户最小化后 会自动进入类后台状态,但恢复时不会主动触发 visibilitychange 事件 。这导致我代码里的 document.addEventListener('visibilitychange', ...) 在鸿蒙 PC 上根本没反应。Windows 上没问题,鸿蒙上死活不触发。

最后没办法,我自己加了个轮询:

javascript 复制代码
let lastHidden = document.hidden
setInterval(() => {
  if (lastHidden !== document.hidden) {
    document.dispatchEvent(new Event('visibilitychange'))
    lastHidden = document.hidden
  }
}, 500)

丑是丑了点,但管用。

六、一个我最近开始犹豫的方案

最后说点掏心窝的:如果你正在考虑要不要上 Electron 跑鸿蒙 PC,我个人更建议你直接用鸿蒙原生 ArkWeb(WebView)。

原因有三条:

  1. 性能差距抹不平。即使我优化到 1.1s,鸿蒙原生 WebView 同样页面是 400ms 出头。这是底层 IPC 的差距,不是代码能解决的。
  2. 安装包体积。Electron 打出来 180MB,ArkWeb 套壳的元服务只有 30MB。用户在华为应用市场看到 180MB 的安装包,下载意愿会打折扣。
  3. 审核麻烦。雷达鸭上架时,审核员问了一句"为什么需要 Electron",我解释了半天跨端代码复用,最后被打回要求补充说明。

我现在新做的页面已经默认 ArkTS + ArkWeb 写原生了,Electron 只用来跑老的历史包袱。除非你有强需求(比如必须用某个 Electron 专属的 Node 模块),否则真的别硬上 Electron。


写到这里有点累了。文章里 1.1s 那个数字是我在 5 次跑分中取的中位数,最快一次 980ms,最慢 1280ms。如果你想自己复现我的测试环境,可以装个 electron-to-chromium 工具看版本对齐情况。

你最近在鸿蒙 PC 上跑 Electron 踩过什么坑?留言聊聊。


关于作者:十年以上软件开发老兵,软件设计师 + 注册人工智能工程师 + agent 工程师,平时主要写鸿蒙 ArkTS 北向开发和 Web 前端,最近在折腾 AI 自动化提效。不定期在 CSDN 分享一些鸿蒙 / AI 方向的实战笔记。

本文遵循 MIT 协议,转载请注明出处。

相关推荐
贾伟康6 小时前
HarmonyOS API 26 Navigation 跨包路由:router_map、HSP 按需加载与失败回落
harmonyos·arkts·模块化·navigation·hsp
KevinChaos6 小时前
阳台小花园开发手记 04|HarmonyOS 7.0 互动卡片:倾斜手机时的卡片效果
华为·harmonyos
威哥爱编程10 小时前
模拟面试智能体开发实战 04:面试聊到哪算哪?用状态机把整场面试编排起来
python·agent·harmonyos
威哥爱编程10 小时前
模拟面试智能体开发实战 03:AI 面试官太客气?人设 Prompt 让它像个真考官
python·agent·harmonyos
北城笑笑14 小时前
Vue 109 ,从零搭建 Vue3 + Electron 工程:生态、安全桥接与打包选型( Vue + Electron )
前端·javascript·vue.js·安全·electron·vue
码间留白15 小时前
六轮真机实验后,我把鸿蒙直播推流的 UI 全逼成了原生——uni-app 适配 HarmonyOS NEXT 完整复盘
uni-app·鸿蒙·harmonyos next·直播推流·arkts/arkui
贾伟康15 小时前
HarmonyOS API 26 Navigation 导航栏位置:navBarPosition、左右布局与折叠屏适配
harmonyos·arkts·arkui·navigation·折叠屏
贾伟康1 天前
HarmonyOS API 26 DevEco Code 需求到 ArkTS 骨架的提示词分层
harmonyos·arkts·ai coding·deveco code·工程提效
威哥爱编程1 天前
模拟面试智能体开发实战 02:回答等半分钟,谁还愿意练?把服务端和流式输出搭起来
python·agent·harmonyos
威哥爱编程1 天前
模拟面试智能体开发实战 01:面试挂了都不知道挂在哪?用 Python 给自己造一个随叫随到的 AI 面试官
python·agent·harmonyos