当Electron应用从Windows迁移到鸿蒙PC时,底层Chromium的华为定制版会导致一些你天天用的API行为完全偏离预期,甚至造成白屏或连接中断。这些问题在官方文档、鸿蒙开发者文档和Chromium定制说明中都找不到任何记载。以下是我们团队花了两天排查出的四个典型差异及对应的适配方案。
- import { nativeTheme, shell, powerMonitor } from 'electron'
复制代码
坑1:process.platform返回'linux',平台判断全错
鸿蒙PC的内核基于Linux,Chromium的platform检测只看内核,所以process.platform返回'linux',而不是'win32'。这意味着你所有if (process.platform === 'win32')的分支都会走错路径——窗口尺寸计算、字体渲染策略、文件路径拼接全都与预期不符。最典型的是文件路径:Windows用反斜杠,Linux用正斜杠,如果代码里写了path.join(__dirname, 'data\config.json'),在鸿蒙上会得到混搭路径/data\config.json,直接导致文件找不到。
解决方案:在应用启动时通过读取/etc/os-release或环境变量HARMONYOS_VERSION判断真实平台,然后统一用正斜杠拼接路径。
- import * as fs from 'fs';
- import * as path from 'path';
- function getRealPlatform(): 'windows' | 'macos' | 'harmonyos' | 'linux' {
- if (process.platform === 'linux') {
- // 检测是否为鸿蒙
- const isHarmonyOS = !!process.env.HARMONYOS_VERSION ||
- !!process.env.HOS_DEVICE_TYPE ||
- (() => {
- try {
- return fs.readFileSync('/etc/os-release', 'utf8').includes('HarmonyOS');
- } catch { return false; }
- })();
- return isHarmonyOS ? 'harmonyos' : 'linux';
- }
- if (process.platform === 'win32') return 'windows';
- if (process.platform === 'darwin') return 'macos';
- return 'linux';
- }
复制代码
坑2:nativeTheme返回undefined,暗色模式失效
在Windows/macOS上,nativeTheme.themeSource和nativeTheme.shouldUseDarkColors能正确读取系统暗色模式状态。鸿蒙PC上它们都返回undefined,且nativeTheme.on('updated')永远不会触发。原因是鸿蒙的“深色模式”是通过系统级资源包切换实现的,Chromium的nativeTheme模块没有对接这套机制。结果你的UI可能一直卡在浅色状态,用户切换暗色模式后毫无反应。
解决方案:对于鸿蒙,通过轮询系统属性getprop persist.sys.dark_mode来检测暗色模式状态,并每5秒检查一次变化。
- function getSystemTheme(): 'light' | 'dark' {
- if (getRealPlatform() === 'harmonyos') {
- try {
- const result = cp.execSync('getprop persist.sys.dark_mode').toString().trim();
- return result === '1' ? 'dark' : 'light';
- } catch { return 'light'; }
- }
- return nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
- }
- function watchThemeChange(callback: (theme: 'light' | 'dark') => void): void {
- if (getRealPlatform() === 'harmonyos') {
- let lastTheme = getSystemTheme();
- setInterval(() => {
- const current = getSystemTheme();
- if (current !== lastTheme) {
- lastTheme = current;
- callback(current);
- }
- }, 5000); // 5秒轮询,CPU开销极低
- return;
- }
- nativeTheme.on('updated', () => {
- callback(nativeTheme.shouldUseDarkColors ? 'dark' : 'light');
- });
- }
复制代码
坑3:shell.openExternal静默失败,URL打不开
在Windows上,shell.openExternal('https://example.com')会调用系统默认浏览器打开URL。鸿蒙PC上它返回undefined,什么也不发生,没有错误抛出。原因是鸿蒙PC的“浏览器”概念是系统内置的Web Viewer,shell.openExternal底层的xdg-open在定制环境中找不到浏览器入口。
解决方案:对鸿蒙使用am start命令调用系统Web Viewer(继承自Android意图机制)。
- function openUrl(url: string): Promise<void> {
- if (getRealPlatform() === 'harmonyos') {
- return new Promise((resolve, reject) => {
- cp.exec(`am start -a android.intent.action.VIEW -d "${url}"`, (err) => {
- err ? reject(err) : resolve();
- });
- });
- }
- return shell.openExternal(url) as Promise<void>;
- }
复制代码
坑4:powerMonitor的suspend对应息屏而非睡眠
Windows上powerMonitor.on('suspend')在系统进入睡眠(S3)时触发,CPU暂停、网络断开、定时器冻结。鸿蒙PC上,用户按电源键息屏(屏幕关闭但CPU不停)就会触发suspend,亮屏触发resume。如果你在suspend事件中做了“停止定时上报”或“断开WebSocket”,用户只是息屏3秒,你的应用就断网了,体验极差。
解决方案:在鸿蒙上记录suspend开始时间,当resume触发时,判断息屏持续时间是否超过阈值(例如30秒),只有超时才视为真正的睡眠。
- function onSystemSleep(callback: () => void, thresholdMs = 30000): void {
- if (getRealPlatform() === 'harmonyos') {
- let suspendTime = 0;
- powerMonitor.on('suspend', () => { suspendTime = Date.now(); });
- powerMonitor.on('resume', () => {
- const duration = Date.now() - suspendTime;
- if (duration > thresholdMs) {
- callback(); // 确认是睡眠而非短暂息屏
- }
- });
- return;
- }
- powerMonitor.on('suspend', callback);
- }
复制代码
统一兼容方案:将上述四个函数封装到一个兼容层文件中,应用启动时加载一次即可。主代码中不再需要写if (platform === 'harmonyos')判断,直接调用getSystemTheme()、openUrl()、onSystemSleep()等函数。这份经验告诉我们,当跨平台时,尤其是面对华为定制的Chromium,所有系统行为相关的API都应经过兼容层,避免直接依赖原生接口。保守一点,总比上线后才发现白屏或断连要好。 |