鸿蒙PC:marktext-develop鸿蒙适配全记录
本文记录了将Electron应用MarkText适配到鸿蒙系统的完整过程。通过分析原始桌面版和适配后的鸿蒙版差异,总结了关键改造步骤:首先构建鸿蒙工程壳和打包链,使应用能在鸿蒙环境运行;然后修改主进程以识别鸿蒙平台并禁用不稳定功能;接着降级桌面端特有功能,保护原生依赖;最后重构渲染进程入口,优化启动流程并增强错误处理。这些改动组合解决了应用在鸿蒙环境下的白屏问题,使其能够正常启动运行。文章提供了详
欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区 :https://harmonypc.csdn.net/
一、写在前面
这篇文章记录的是一次比较完整的 Electron 应用鸿蒙适配实践,主角就是从 GitHub 拉下来的 marktext-develop 项目本身。
项目开源地址 :https://AtomGit.com/lqjmac/marktext-ohos
这次工作不是“从零开发一个鸿蒙应用”,而是一次典型的迁移改造:
把一个面向桌面端的复杂 Electron 工程,逐步改造成一个可以在鸿蒙 Electron 运行环境中启动和运行的版本。
为了把这个过程讲清楚,本文会把 marktext-develop 放在两个状态下进行分析:
- 桌面原始态:刚从 GitHub 拉下来的标准 Electron 工程
- 鸿蒙适配态:完成 OpenHarmony / HarmonyOS 兼容改造后的工程
本文不会只讲“最后改了哪些文件”,而是会按照 变化过程 来分析:
- 原始版本是什么状态
- 适配后的版本增加了什么
- 哪些修改是工程层面的
- 哪些修改是运行时兼容层面的
- 哪些修改是为了解决真正的白屏问题
- 为什么这些改动组合起来最终让应用跑了起来
同时,文末会明确标出截图放置位置说明,方便后续排版发布。
二、项目背景
MarkText 是一个成熟的 Markdown 编辑器,技术栈并不轻:
- Electron
- Vue 2
- Vuex
- Vue Router
- Element UI
- Muya 编辑器内核
- 大量本地文件、窗口、菜单、快捷键、剪贴板、字体、编码检测等桌面能力
这类项目的特点是:
- 功能完整
- 启动链复杂
- 原生依赖较多
- 对 Electron 运行时的默认能力假设很强
而鸿蒙下的 Electron 运行环境,与 Windows / macOS / Linux 桌面端并不完全一致。
这就导致一个很典型的问题:
项目在桌面端完全正常,但搬到鸿蒙环境后,应用能起,窗口能出,界面却卡在加载动画。
这次适配的目标,就是把 marktext-develop 从原始桌面工程,逐步演化成最终可在鸿蒙 Electron 运行环境中正常启动和运行的版本。
三、总体变化概览
在排除 node_modules、构建产物和 IDE 缓存后,marktext-develop 从桌面原始态演进到鸿蒙适配态的变化,大致可以概括为:
3.1 变化规模
- 新增文件:约
240个 - 修改文件:
28个 - 删除文件:
0个
这个数字本身已经说明:
这不是一个“改 3 个配置文件就结束”的适配,而是一套完整的迁移工程。
3.2 变化层次
这些变化基本可以分成三层:
-
工程层
- 增加鸿蒙 HAP 工程壳
- 增加打包与同步脚本
- 增加适配说明文档
-
主进程兼容层
- 增加 Harmony 平台识别
- 降级 GPU 和桌面端特有能力
- 保护原生依赖
- 加强主进程对渲染层的诊断
-
渲染进程启动层
- 重构 renderer 入口
- 缩短首屏同步依赖链
- 增加错误可视化兜底
- 对不稳定 native / remote 能力做兼容处理
如果要一句话总结:
marktext-develop的原始桌面版是标准 Electron 应用,而鸿蒙适配版则是在保留原有业务结构的前提下,补出了一整套鸿蒙运行时兼容层。

四、原始桌面版 marktext-develop 的状态
原始项目的特点非常鲜明:
4.1 默认只考虑桌面端平台
在 src/main/index.js 中,原始逻辑只允许这些平台:
darwinwin32linux
这意味着如果直接在鸿蒙环境运行,主进程在平台识别阶段就已经处于不被支持的状态。
4.2 启动链默认依赖“桌面 Electron 全能力”
原始项目默认假设以下能力都可正常工作:
- GPU 加速
@electron/remotenative-keymapkeytarcedfontmanager-redux- 桌面端标题栏、窗口偏移等接口
在桌面端这是合理的,但在 HarmonyOS 环境中,这种假设会让很多启动路径变得脆弱。
4.3 渲染进程入口是重同步启动
原始 src/renderer/main.js 在文件顶层就同步导入了:
- Vue
- VueRouter
- Element UI
- Store
- Router
- 样式
- 服务模块
- 一整套渲染入口依赖
而 App.vue 又继续同步引入:
EditorWithTabsSideBarCommandPaletteAboutDialogImportModalTweet- 更多弹窗和上下文能力
这个结构对桌面环境没有问题,但对鸿蒙环境来说,意味着:
只要首屏依赖链中有任意一个模块在鸿蒙运行时不兼容,整个应用就会在 Vue 根实例挂载前失败。
4.4 错误暴露能力不足
原始项目虽然有自己的异常处理体系,但在鸿蒙环境下,渲染层日志并不一定能自然透出到可见位置。
这就会导致一个现实问题:
- 主窗口已经出来了
- 页面却还停在 loading
- 但你看不到真正的渲染层报错
这也是这次适配初期最难受的地方。

五、鸿蒙适配版新增了什么
5.1 新增鸿蒙工程壳 ohos_hap/
这是本次变化里最直观的一部分。
适配后的项目新增了完整的:
ohos_hap/AppScopeohos_hap/electronohos_hap/web_engineohos_hap/hvigor- 模块配置
- 资源目录
- HAP 构建脚本
- OpenHarmony 相关说明和依赖资源
这说明 marktext-develop 不再只是一个普通 Electron 源码仓库,而是已经具备了:
“桌面应用资源 + 鸿蒙壳工程 + 资源注入打包链路”
5.2 新增适配脚本
相较于原始桌面版,package.json 中新增了三个关键命令:
build:ohosohos:syncohos:build
它们分别承担:
- 构建鸿蒙需要的应用资源包
- 把产物同步到鸿蒙工程资源目录
- 进一步触发 HAP 构建
这一步是从“源码项目”走向“可交付鸿蒙包工程”的关键桥梁。
5.3 新增适配文档
新增文件:
OHOS_ADAPTATION.md
这代表适配过程已经不再只是临时试验,而是开始沉淀成可复用的方法和流程。

六、marktext-develop 的鸿蒙适配核心演进路径
如果按适配过程来理解,这次演进大致可以拆成下面六步。
七、第一步:补齐鸿蒙工程与打包链
7.1 为什么要先做这一步
如果没有鸿蒙工程壳,桌面端 Electron 产物根本没有地方挂载到 HarmonyOS 的运行环境中。
所以第一步不是修白屏,而是先让整个工程拥有进入鸿蒙壳的能力。
7.2 关键变化
在 marktext-develop 中新增:
scripts/build-ohos-package.jsscripts/build-ohos-hap.jsohos_hap/工程目录
7.3 这一层改造的本质
原始项目只有 Electron 应用本身。
适配后项目则多了一套“鸿蒙承载层”。
也就是说:
marktext-develop关心的是“如何打包桌面应用”marktext-develop还要关心“如何把桌面应用资源投喂给鸿蒙 Electron 壳”
7.4 这一阶段的结果
最终 MarkText 的桌面产物会被同步到:
ohos_hap/web_engine/src/main/resources/resfile/resources/app
这个目录是鸿蒙侧 Electron 壳实际读取应用资源的位置。
八、第二步:让主进程承认 HarmonyOS 平台存在
8.1 原始状态
marktext-develop 在 src/main/index.js 中只允许 darwin / win32 / linux。
8.2 适配后的变化
在 src/main/config.js 中新增:
isHarmony
并在 src/main/index.js 中将原来的平台判定改为:
- HarmonyOS 作为可接受平台
- HarmonyOS 启动时自动禁用 GPU
8.3 这一步的意义
这一步解决的是一个最基础的问题:
项目不能一上来就把鸿蒙判成“不支持的平台”。
同时,自动禁用 GPU 也是非常重要的保守策略。因为在鸿蒙日志中可以明显看到:
- GPU 进程会反复拉起
- 图形栈会降级
use-gl=angle和use-gl=disabled会切换
这意味着 GPU 在鸿蒙环境中并不稳定。
所以在 marktext-develop 里,主进程会主动把这类风险前置规避掉。
九、第三步:把桌面端特有能力降级掉
这一阶段是从“能启动”走向“别轻易被桌面能力拖死”。
9.1 src/main/app/index.js 的变化
相对于原始桌面版,鸿蒙适配版在这个文件里做了几类保护:
- HarmonyOS 下不再依赖 native theme 的完整行为
- HarmonyOS 下跳过 Dock 相关逻辑
- HarmonyOS 下跳过 JumpList 相关逻辑
- HarmonyOS 下不注册拼写检查监听
9.2 为什么这些改动必要
因为这些能力本质上都更偏桌面平台特征:
- macOS Dock
- Windows JumpList
- Chromium / Electron 对桌面环境的主题假设
- 桌面拼写检查链路
如果不做隔离,应用可能在主进程初始化后半段才暴露异常,定位会更困难。
9.3 窗口接口保护
在:
src/main/windows/editor.jssrc/main/windows/setting.js
中,适配版增加了对 setSheetOffset 的保护,并加入 HarmonyOS 条件判断。
这类接口在桌面端正常,在鸿蒙下则不应默认可用。
十、第四步:给原生依赖都加上“能失败”的出口
这一部分是适配过程中非常关键的一层。
原始项目里有不少原生依赖,在桌面端理所当然,但在鸿蒙环境下不一定成立。
10.1 keytar:从硬依赖变为可选依赖
原始版本在 src/main/dataCenter/index.js 中直接引入 keytar。
适配版改成了:
try/catch延迟 require- 加载失败则打印告警
- 安全存储相关能力失效,但整体应用仍可运行
这一步很重要,因为鸿蒙日志中确实出现了:
Cannot find module '../build/Release/keytar.node'
如果不改成可选依赖,应用很容易在数据中心初始化阶段就崩掉。
10.2 native-keymap:从直接使用到回退为 en-US
在 src/main/keyboard/index.js 中,适配版增加了:
- 原生绑定文件存在性判断
- 动态
require('native-keymap') - 失败时回退为
en-US
这意味着:
- 即使鸿蒙环境不支持原生键盘布局读取
- 应用也不会因为快捷键布局模块失败而无法启动
10.3 ced:编码探测从“必须存在”变成“存在更好”
在 src/main/filesystem/encoding.js 中,适配版把 ced 改成了惰性获取。
逻辑从:
- 启动时默认依赖
ced
改成:
- 如果存在就用它猜编码
- 如果不存在,也能继续使用 BOM 或 UTF-8 路径完成文件处理
10.4 fontmanager-redux
在 src/renderer/prefComponents/common/fontTextBox/index.vue 中,适配版对系统字体枚举做了 try/catch。
这意味着:
- 设置页字体能力可能受限
- 但不会因为本地字体枚举失败而拖垮设置页
十一、第五步:解决 @electron/remote 带来的渲染层脆弱性
11.1 原始状态
marktext-develop 的渲染层中,很多地方直接使用:
@electron/remotegetCurrentWindowMenuclipboard
11.2 适配后的变化
适配版新增:
src/renderer/util/remote.js
这个文件专门包了一层安全访问接口:
getCurrentRemoteWindowgetRemoteMenugetRemoteMenuItemgetRemoteClipboardisRemoteAvailable
11.3 这一步的意义
它不是为了“代码更优雅”,而是为了:
当 HarmonyOS 环境里的
@electron/remote行为不完整时,渲染层不至于在顶层求值阶段直接崩掉。
11.4 影响到的典型文件
相较于 marktext-develop,下面这些文件都被改造成走安全 remote 包装:
src/renderer/commands/index.jssrc/renderer/components/titleBar/index.vuesrc/renderer/prefComponents/common/titlebar.vuesrc/renderer/contextMenu/sideBar/index.jssrc/renderer/contextMenu/tabs/index.jssrc/renderer/util/clipboard.js
这说明适配版在做的,不是简单修一个地方,而是在 系统性收缩 remote 风险面。



十二、第六步:对白屏问题动手术
这是整个 marktext-develop 鸿蒙适配过程中,最关键也最“有含金量”的部分。
下图所示:日志中已经可以看到标题设置、窗口显示与激活,说明问题并不在主进程建窗阶段。


12.1 白屏问题的本质
初看现象是:
- 窗口已经显示
- 标题已经变成
MarkText - 但界面一直停在绿色加载动画
这说明主进程建窗已经完成,真正的问题发生在:
渲染层入口到 Vue 根实例挂载这段路径上。
12.2 src/index.ejs 的升级
原始版 src/index.ejs 只是一个普通的加载页面。
适配版在这里新增了非常关键的一层壳级诊断:
window.onerrorunhandledrejection- 资源加载失败监听
- 直接把错误渲染回 loading 页面
这个思路非常接近参考项目 electron-markdownify-master:
在应用业务脚本真正跑起来之前,就先让 HTML 壳层具备错误感知能力。
12.3 src/renderer/main.js 的重构
这是变化最大的一处。
原始版做法:
- 顶层同步
import - 一次性把 Vue、Store、Router、Element UI、样式、服务全部拉进来
适配版做法:
- 分阶段
require - 每个阶段带标签打印
- 可选依赖允许失败
- 失败时直接把错误显示到 loading 页
这一步的意义非常大,因为它把原本“黑盒式崩溃”变成了“带阶段信息的可见崩溃”。
12.4 src/renderer/bootstrap.js 的保守化
原始版在文件顶层直接依赖 electron-log。
适配版把它改成惰性加载:
- 能加载则启用
- 失败则退回
console
并增加:
- 渲染层
error日志 unhandledrejection日志- 启动链埋点
12.5 src/renderer/node/paths.js 的容错
原始版直接依赖 vscode-ripgrep。
适配版改成:
try/catch require- 失败时搜索能力降级,但首屏继续
这类改动体现的是一个非常成熟的迁移策略:
非首屏核心能力绝不能阻塞首屏。
十三、第七步:把重组件从首屏同步链路里拆出去
这一步是适配版能真正摆脱加载动画卡死的核心操作之一。
13.1 原始版 App.vue
marktext-develop 在 App.vue 中同步引入了大量重组件:
EditorWithTabsSideBarCommandPaletteAboutDialogExportSettingDialogRenameTweetImportModal
这意味着:
- 只要这些组件树里有任何一个模块在鸿蒙环境下不兼容
- 整个
App根组件都可能在挂载前失败
13.2 适配版 App.vue
适配版做法是:
- 将这些组件改成异步加载
- 增加按组件粒度的失败日志
- 给异步加载保留简单 loading 占位
13.3 这一步的效果
它等于把原本这条链:
App -> EditorWithTabs -> editor.vue -> Muya -> CodeMirror -> 搜索/打印/通知/剪贴板
从“同步硬链”改成了“分段可失败链”。
这意味着:
- Vue 根实例更容易先挂起来
- loading overlay 更容易先被移除
- 后续哪个组件出问题,也更容易单独定位
十四、第八步:把“鸿蒙下看不到 renderer 日志”的问题彻底补上
这一层是鸿蒙适配版相对原始桌面版的另一个质变。
14.1 原始状态
原始项目虽然有异常处理,但并没有专门针对鸿蒙环境做:
console-messagedid-fail-loadrender-process-gonedom-readydid-finish-load
这类事件的统一桥接。
14.2 适配后的做法
在 src/main/windows/base.js 中新增了统一诊断函数:
_attachWebContentsDiagnostics
并在:
src/main/windows/editor.jssrc/main/windows/setting.js
中挂接。
14.3 这一步为什么很值钱
因为在鸿蒙环境下,很多时候问题不是“没有报错”,而是:
报错发生在 renderer,但你根本看不见。
诊断桥接之后:
- renderer 的控制台日志会被带回主进程
- 页面加载失败会被带回主进程
- 渲染进程退出会被带回主进程
这让适配版真正拥有了“可继续调试”的能力。
十五、参考项目的价值:为什么要借鉴 electron-markdownify-master
这次适配过程中,electron-markdownify-master 不是直接被照搬,而是被当成一个“已经在鸿蒙环境中验证过的运行时样板”。
它带来的启发主要有三点:
15.1 鸿蒙下要先做能力判断,再做业务
参考项目先判断:
- 是否为 OHOS
- 哪些能力该关闭
- 哪些模块该保守加载
这个思路直接影响了 marktext-develop 中的:
isHarmony- 可选依赖降级
- 主进程窗口能力保护
15.2 主进程必须桥接 renderer 日志
参考项目显式监听:
console-messagedid-fail-loadrender-process-gone
这直接启发了适配版中的窗口诊断桥。
15.3 首屏一定要少做事
参考项目的 renderer 启动很轻。
这让我们反过来意识到 marktext-develop 的问题并不是“某一个 API 不支持”那么简单,而是:
首屏同步依赖链太长,任何一个节点的不兼容都会被放大成整体白屏。
这也是为什么适配版必须对 renderer/main.js 和 App.vue 动手。

十六、从变化结果看:鸿蒙适配版具体比原始桌面版多了什么能力
如果用“能力清单”的方式总结,适配后的项目相比原始版多了这些能力:
16.1 工程能力
- 能构建鸿蒙资源包
- 能同步资源到 HAP 工程目录
- 能参与 HAP 构建
16.2 平台识别能力
- 能识别 HarmonyOS 平台
- 能按平台启用或禁用不同逻辑
16.3 降级容错能力
keytar不可用时继续运行native-keymap不可用时继续运行ced不可用时继续运行fontmanager-redux不可用时继续运行@electron/remote不完整时继续运行vscode-ripgrep不可用时首屏继续运行
16.4 启动诊断能力
- HTML 壳层能捕获首屏资源与脚本错误
- 主进程能桥接渲染层 console
- 主进程能感知加载失败和 render process 崩溃
16.5 启动链拆分能力
- 首屏重组件异步化
- renderer 入口分阶段加载
- 非核心依赖不再阻塞根实例挂载
十七、为什么原始桌面版不能直接跑,而鸿蒙适配版可以
这部分是全文最值得拿出来做结论的一段。
并不是说 marktext-develop 有“致命 bug”,而是它默认建立在“标准桌面 Electron 环境”的假设上。
而 marktext-develop 能跑起来,是因为它系统性地改变了这些前提:
-
平台前提变了
HarmonyOS 被正式识别为允许平台。 -
能力前提变了
桌面端默认存在的很多能力,在鸿蒙环境下改成了“可选而非必选”。 -
首屏前提变了
首屏不再要求整个重组件树和所有原生依赖同步成功。 -
错误处理前提变了
错误不再沉没在黑盒里,而是会反馈到 UI 或主进程日志。 -
工程前提变了
项目不再只是 Electron 源码项目,而是具备了鸿蒙打包和承载能力。
从这个角度看:
这个版本不是单纯的“修复版”,而是
marktext-develop的鸿蒙迁移版本。

十八、结语
这次 marktext-develop 的鸿蒙适配,本质上不是一次简单修 bug,而是一次典型的桌面 Electron 项目鸿蒙迁移:
- 工程层补齐承载壳
- 平台层补齐识别与降级
- 启动层补齐容错与诊断
原始项目的问题并不是“不够好”,而是它默认站在成熟桌面平台的地板上。
而适配后的项目之所以能跑起来,是因为它为鸿蒙环境重新补上了那块地板。
如果后续还需要把更多 Electron 项目迁到鸿蒙环境,那么这次 marktext-develop 从原始桌面版到鸿蒙适配版的演进过程,本身就已经是一份很有参考价值的实践样本。
Electron 环境”的假设上。
而 marktext-develop 能跑起来,是因为它系统性地改变了这些前提:
-
平台前提变了
HarmonyOS 被正式识别为允许平台。 -
能力前提变了
桌面端默认存在的很多能力,在鸿蒙环境下改成了“可选而非必选”。 -
首屏前提变了
首屏不再要求整个重组件树和所有原生依赖同步成功。 -
错误处理前提变了
错误不再沉没在黑盒里,而是会反馈到 UI 或主进程日志。 -
工程前提变了
项目不再只是 Electron 源码项目,而是具备了鸿蒙打包和承载能力。
从这个角度看:
这个版本不是单纯的“修复版”,而是
marktext-develop的鸿蒙迁移版本。
[外链图片转存中…(img-Y7aR7XwZ-1779347511634)]
十八、结语
这次 marktext-develop 的鸿蒙适配,本质上不是一次简单修 bug,而是一次典型的桌面 Electron 项目鸿蒙迁移:
- 工程层补齐承载壳
- 平台层补齐识别与降级
- 启动层补齐容错与诊断
原始项目的问题并不是“不够好”,而是它默认站在成熟桌面平台的地板上。
而适配后的项目之所以能跑起来,是因为它为鸿蒙环境重新补上了那块地板。
如果后续还需要把更多 Electron 项目迁到鸿蒙环境,那么这次 marktext-develop 从原始桌面版到鸿蒙适配版的演进过程,本身就已经是一份很有参考价值的实践样本。
更多推荐


所有评论(0)