OpenHarmony 跨端开发实践:React Native 环境搭建与真机踩坑记录
先交待一下背景OpenHarmony不是一个模拟层也不是套了个WebView壳的兼容方案。它有自己的分布式架构、自己的UI框架ArkUI和自己的编译工具链。而React Native for OpenHarmony社区里一般叫RNOH要做的是把你写的React组件翻译成OpenHarmony原生渲染指令让JS逻辑跑在ArkTS原生运行时上。这个思路决定了整个环境搭建不是“装个Node就能跑”你得把原生侧的工具链、签名、SDK版本、依赖管理全部对齐。这篇文章就是把我从零开始部署RNOH的开发环境、踩平各种坑的全过程拆开讲清楚适合想在这个生态里做跨端业务、又不想被原生细节绊住手脚的团队参考。看完你会知道DNOH这套环境到底依赖哪些东西、每一步该怎么选怎么配以及真机上出白屏、编译不过时去哪查。1. 环境准备先搞懂RNOH在跑什么1.1 RNOH的运行时架构与部署目标很多人上来就问“RNOH是不是就是React Native加了个鸿蒙适配”这句话只对了一半。RNOH不是简单地包了一层桥接它是一个重新实现的渲染后端。你写的View、Text、ScrollView这些组件最终会走到OpenHarmony的ArkUI组件上而不是像浏览器里那样操作DOM。这意味着你在Android/iOS上那一套原生Module的写法在RNOH里要重新用ArkTS写一遍原生侧的逻辑。架构上RNOH大致分三层最上层是你熟悉的React JS业务代码中间是通过JS引擎默认是ArkTS运行时自带的JS引擎执行React Fiber的调度和组件渲染逻辑最下层是原生侧的RNInstance、RNComponent这些C/ArkTS桥接层负责把React的虚拟节点映射成ArkUI的Node。这层映射很关键因为ArkUI的布局系统有自己的规则RNOH做了大量对齐工作比如flex布局的差异处理、滚动容器的嵌套限制这些在后续部署真机时都可能成为排查点。部署目标上你要清楚RNOH目前支持的是OpenHarmony的标准系统设备不是轻量级物联网设备。官方和社区活跃适配的版本集中在OpenHarmony 4.0/4.1API 10/11同时要求设备有足够的RAM和存储来跑完整JS引擎。单次部署验证用模拟器就行但如果做性能测试建议直接上真机——模拟器的GPU和内存分配逻辑跟真机差太多实测下来首帧渲染时间能差出一倍以上。1.2 开发机与操作系统要求RNOH的部署链路比较长开发机建议直接上LinuxUbuntu 20.04/22.04或者macOSWindows也能跑但要小心一些路径和权限问题。我自己主力机是Ubuntu 22.04配合DevEco Studio的Linux版本一路下来最顺。如果你在Windows上做注意不要用中文用户名和中文路径ohpm和hvigor这类工具对路径编码很敏感踩过的人应该都懂那个“无法解析符号”的玄学报错。内存方面别低于16GB。构建RNOH工程时hvigor会启动多个Worker并行做编译和打包8GB的机器跑一次完整构建直接卡到风扇狂转。磁盘剩余空间至少留20GB因为OpenHarmony SDK、DevEco Studio、Gradle缓存RNOH仍会依赖部分Gradle生态的构建插件加一起非常占空间。CPU方面i5/R5级别的4核以上都能胜任但编译时长会明显拉开差距——我实测在8核机器上全量构建大概需要8到10分钟4核机器可能要20分钟以上。注意不要把“OpenHarmony SDK”和“HarmonyOS SDK”搞混。RNOH面向的是OpenHarmony开源生态某些版本的DevEco Studio默认会引导你装HarmonyOS的SDK那个包含的是华为的商业闭源能力编译目标不同直接用于RNOH工程会报一堆API不存在或签名不匹配的错误。装SDK时看清楚勾选的是OpenHarmony分支。2. 工具链盘点装好这些再开工2.1 DevEco Studio与OpenHarmony SDK的版本搭配RNOH的原生侧工程是用DevEco Studio管理的它可以理解成“OpenHarmony版的Android Studio”负责ArkTS编译、资源打包和设备安装。版本选择上建议使用DevEco Studio 4.0及以上配套OpenHarmony SDK选API 10或API 11。为什么强调版本匹配因为RNOH的原生桥接库编译时依赖SDK里的ohos.arkui和ohos.base这些系统声明文件版本差一个主版本就会出现大量TS类型不匹配。装完DevEco Studio后里面会自带SDK Manager勾选OpenHarmony SDK时注意它不会自动帮你装全部的platform版本你需要看工程里build-profile.json5声明的compileSdkVersion是几就装几。比如声明的是11你只装了10构建时hvigor会尝试自动下载但如果网络不通尤其在国内环境会一直卡在“SDK not found”。2.2 Node、ohpm、hvigor的版本搭配RNOH的JS侧还是标准的React Native工程所以Node是必须的。建议Node 18或20不要用最新的22因为部分老版本metro的依赖在Node 22下会有兼容告警。npm源建议配成国内镜像否则react-native-ohos/cli这类的包下载会慢到怀疑人生。ohpm是OpenHarmony的包管理器类似npm但面向ArkTS生态。它在DevEco Studio里内置了但命令行工具需要单独启用——通常在sdk/default/openharmony/toolchains/ohpm/bin目录下。装完后配置全局环境变量否则你在终端里敲ohpm会找不到命令。hvigor则是OpenHarmony的构建引擎类似Gradle负责执行编译、链接、打包任务。你不需要单独装hvigorDevEco Studio会在首次构建时自动下载指定版本但有一点要注意工程里的hvigor/hvigor-config.json5会锁版本号如果这个版本在你这台机器上缓存失效构建时会报“hvigor version not found”解决办法是把版本换成SDK自带的那个别去手动升级。2.3 网络与依赖源配置部署RNOH环境不可避免要跟多个仓库打交道npm源管JS依赖ohpm源管ArkTS依赖还有hvigor源管构建插件。这三个源最好都换成国内可访问的镜像地址。ohpm的默认源在国外直接跑ohpm install经常超时在~/.ohpmrc里配置registryhttps://镜像地址能省一大半时间。有个容易被忽略的点RNOH工程里往往同时存在package.json和oh-package.json5两个包管理文件。前者是npm的后者是ohpm的。很多新手只跑到npm install就以为依赖装完了结果在DevEco Studio里一同步就报“native module not found”。要在工程根目录分别执行npm install、ohpm install缺一不可。3. 快速部署实操从初始化到跑通“Hello World”3.1 两种项目初始化路线初始化一个RNOH工程有两种常见路线用脚手架命令自动生成或者手动给已有React Native工程接入RNOH。脚手架命令最省事大致流程是先全局安装react-native-ohos/cli然后执行类似npx react-native-ohos/cli init AwesomeProject的命令。它会拉取一个标准模板模板里已经包含了react-native-harmony这个核心原生包并且帮你把entry模块、build-profile.json5、oh-package.json5都配好了。我建议新项目首选用这个方式因为团队维护的模板会持续跟进API变更你自己手写配置很容易漏。手动接入则适合已有RN 0.72以上版本工程、不想推翻重来的情况。具体做法是把react-native-harmony加到原生依赖然后在工程根目录新增oh-package.json5声明依赖和原生模块的路径。听起来简单但实际要处理的细节非常多entry模块的module.json5要申请网络权限和媒体权限RN的Image组件底层会走原生网络栈、build-profile.json5要声明signingConfigs还有MainAbility的配置要把RNApp初始化的部分从MainAbility生命周期里钩进来。第一次手动接入最好直接对照脚手架的模板改别自己凭空想。3.2 关键文件配置详解build-profile.json5是原生侧构建的核心里面至少有三个字段要关注app.products定义了构建目标比如default里要声明signingConfigs的名称Debug和Release都指向同一个签名配置文件。modules引用entry模块并指定srcPath。runtimeOS声明为OpenHarmony这个写错就会出现“device type mismatch”。oh-package.json5则像一把钥匙它决定了SDK和依赖从哪个仓库拉取。里面的dependencies要声明react-native-harmony和ohos/react-native这类原生桥接包注意这些包的版本号必须跟JS侧package.json里的react-native版本对齐比如JS侧是0.72.5原生侧就对应0.72.5-x的特定发布版。这个“x”后缀是RNOH社区自己的补丁版本别手滑升级成了不兼容的版本。entry/src/main/module.json5里需要检查requestPermissions是否有ohos.permission.INTERNET。RN上个请求图片、加载字体图标、发网络请求都会用到。另外建议在Debug模式下把debuggable设为true这样能启用ArkTS的调试通道方便后续在DevEco里断点调试原生侧代码。3.3 构建产物与签名配置DevEco Studio构建时默认产物是一个.hap文件这玩意儿对应的是“OpenHarmony Ability Package”。你在IDE里点“Run”时其实做了两步先构建出.hap再部署到设备。签名上RNOH工程在打包阶段默认走的是“自动签名”路径即DevEco Studio通过本地的HarmonyOS.p12和HarmonyOS.p7b材料生成调试证书。如果你要真机长期调试建议去OpenHarmony官网申请正式的调试证书然后把.cer、.p7b、.p12配到build-profile.json5的signingConfigs里。这一步没有捷径配错了会出现“Signing certificate not found”或“Code signing failed”。提示如果工程里同时存在多个模块比如你加了library类型的共享模块签名配置要保证一致否则就算编译通过.hap装到设备上也会因为签名校验失败直接闪退log里只给一句很模糊的install failed排查非常费劲。4. 真机联调让应用真正跑起来4.1 设备连接与准备工作RNOH应用最终要落在真机上跑模拟器虽然能验证逻辑但很多跟图形渲染和CPU调频相关的问题模拟器根本复现不了。连接真机时先确认设备开启了“开发者模式”并授权USB调试。OpenHarmony设备的开发者模式入口在不同版本位置略不同但逻辑都是去“设置”里连续点击“关于本机”的版本号。连接后在终端执行hdc list targetshdc就是OpenHarmony的设备连接工具类似adb。如果有设备但状态显示offline八成是USB调试授权弹窗没点确认或者驱动不对。Linux下还容易遇到udev规则问题表现为设备插入后系统识别为Unknown device需要在/etc/udev/rules.d/里加一条设备厂商ID的规则然后重载。设备联调时我习惯把无线调试也打开。RNOH开发过程中频繁改代码、重新构建、再部署有线连接虽然稳定但人不能一直守在开发机旁边。用无线模式跑一遍有助于找问题前提是手机和电脑在同一局域网。4.2 把应用装上去hvigor命令与IDE按钮在DevEco Studio里点右上角的“Run”IDE会自动执行hvigor的assembleHap任务并触发hdc install。如果你在CI里部署就绕不开纯命令行方式。常用命令是hvigorw assembleHap --mode module -p productdefault在工程根目录执行产物路径一般在entry/build/default/outputs/default/entry-default-signed.hap。然后用hdc install entry/build/default/outputs/default/entry-default-signed.hap直接把.hap推送到已连接的设备上安装。装完别急着点图标。RNOH应用第一次启动时JS bundle还在本地打包阶段首帧会比较慢这是正常的。你可以通过log来判断启动是否进入正常状态。查看日志的方法是hdc hilog主要关注RNOH_JS、RNInstance这几个TAG能看到RNInstance created和React Native loaded基本就说明运行时初始化成功了。如果一直停留在download bundle或者load from asset那就要去检查JS bundle是否真的打包进了.hap资源目录。一个常见的坑是DevEco Studio的“Run”默认走的是Debug配置Debug不会把JS bundle打进.hap而是期望从开发服务器的metro拉取。如果你没启动npm start应用启动后就卡在加载页然后超时白屏。4.3 Debug模式下的metro服务器配置RNOH在Debug模式下依然可以复用metro开发服务器。你在IDE里点“Run”后控制台会提示“Waiting for bundle URL from metro”。此时需要保证真机能访问到开发机上metro的地址默认地址是http://localhost:8081但设备访问这个地址等于访问它自己需要在DevEco Studio的“Run Configuration”里把Bundle Server Host改成开发机的局域网IP。改完后重启应用应用会在启动时主动从metro拉取最新的JS bundle实现接近“热更新”的开发体验。我强烈建议在拿到了稳定的met网络之后再大量修改代码否则每次手改配置IP都会消耗很多时间。还有个小技巧如果你改了原生侧代码比如新增了一个RNOH原生Modulemetro的热更新是不生效的必须重新构建.hap并安装。而如果只是改了JS组件、样式、状态逻辑则完全不需要重新构建原生侧刷新metro缓存即可。5. 坑点速查我踩过、也帮别人排过的那些问题5.1 编译期报错的常见出口“Could not resolve com.facebook.react:react-native”这类Gradle错误往往不是缺依赖而是你的工程里残留了Android的原生依赖脚本。RNOH不需要Gradle去拉React Native Android的aar它在原生侧完全走ohpm。把android目录和react-native的Gradle依赖清理干净即可。“ArkTS: ArkTS can not use JavaScript syntax”这类编译警告是RNOH原生桥接代码里用了一些JS动态特性比如any类型或Object.defineProperty。解决方法是把对应模块的编译模式从strict改成lite在build-profile.json5里相关模块添加arkOptions: { ruleSet: lite }。这个方法能解决大部分ArkTS严格类型下的编译时报错。构建时下载ohpm依赖非常慢先检查oh-package.json5声明的包版本是否存在如果版本号写错多写一个0或少写一个patchohpm会反复尝试远端索引超时后就报“package not found”。确认版本号正确后再检查~/.ohpmrc里有没有配镜像源。5.2 运行期白屏/加载卡死类问题白屏是RNOH部署过程中最常被问的。我遇到过的白屏原因大致有几类第一类Debug模式下没连接metro。症状是应用启动后一直显示一个空白的加载页几秒后超时。排查方式很简单看hilog里有没有“bundle load failed”。如果确认是拉不到bundle就把开发机和真机连到同一WiFi并且确认Bundle Server Host填的是局域网IP而不是localhost。第二类JS引擎初始化失败。有些低内存设备上RNOH启动时需要一次性加载ArkTS Runtime JS引擎内存吃紧会直接杀掉进程。在设备上给应用授权“后台运行”或“忽略电池优化”有时候能缓解但根治还是要换更高配置的测试机。第三类ArkUI组件渲染异常导致白屏。比如你用了Modal组件RNOH早期版本对Modal的ArkUI映射支持不完整弹窗会出现但背后一层全是黑的。这类白屏不会崩溃也不会报红屏只能通过hilog里的组件警告来判断。建议尽量使用RNOH官方组件列表里标记为“已验证”的原生组件比如View、Text、Image、ScrollView、FlatList这些基础组件其余组件先在target设备上做兼容性验证。5.3 模块不兼容与版本对齐问题RNOH的社区迭代非常快JS侧一个react-native小版本升级原生侧的react-native-harmony可能就要跟着升。如果你看到报错是“Native module cannot be null”或者“Missing native component”大概率是JS和原生的版本错位。我一般用这个顺序来定位先看package.json里的react-native版本再查oh-package.json5里的react-native-harmony版本两者的主版本号要一致。比如JS是0.72.x原生也必须是0.72.x。如果项目里引入了第三方RN组件库比如react-native-screens或react-native-gesture-handler这些事情更麻烦——这些库在RNOH生态里往往需要额外使用react-native-ohos/前缀的套件。平route下来最可靠的方式是去查询该组件的RNOH适配版本不要直接用在Android/iOS上验证过的原版。调试上还有个通用技巧把react-native-harmony的日志级别调到Verbose在module.json5里给meta-data加一条reactNativeLogLevel为verbose然后复现问题hilog会给出非常详细的JS和原生侧调用栈。这个技巧帮我在定位FlatList滚动停顿和Image加载失败时节省了大量时间。根据我这阵子的实际体验RNOH的环境部署难度并不在某个单点上而是链路长、版本多、每一步都可能埋雷。只要把Node、ohpm、hvigor、SDK这条链路的版本对齐摸清楚再记住“Debug靠metro、Release靠打包后内嵌bundle”这个心法跑通完整的开发环境基本一两小时能搞定。最后再分享一个我自己的习惯每次升级RNOH版本前先把当前工程里的lock文件统统留个备份这样出了问题还可以快速退回到可运行状态。这个习惯救了我好几次。

相关新闻

ComfyUI DynamicNoise节点详解:动态噪声调度让扩散模型采样更可控

ComfyUI DynamicNoise节点详解:动态噪声调度让扩散模型采样更可控

关于Diffusion模型采样时噪声应该怎么给,很多人都踩过同一个坑:想让构图稳一点,就把种子固定;想让细节丰富一点,就把CFG或步数拉高。但试过一轮就知道,固定强度的噪声最多只能“在安全区里微调”&#xff0…

2026/10/1 11:42:21 阅读更多 →
莱布尼茨积分规则详解:积分号下求导与参数化积分技巧

莱布尼茨积分规则详解:积分号下求导与参数化积分技巧

做积分做到一半,突然发现被积函数里还挂着一个参数,这时候摆在你面前的路通常只有两条:要么硬着头皮先把积分算出来,得到一个关于参数的表达式,再回头对这个表达式求导;要么想办法把求导这个动作直接挪到积…

2026/10/1 11:41:21 阅读更多 →
Madeira 跨架构兼容层实战:FEX-Emu + Wine + DXMT 运行 Windows 程序

Madeira 跨架构兼容层实战:FEX-Emu + Wine + DXMT 运行 Windows 程序

1. 从“Madeira”这个名字说起:它到底想解决什么问题 第一次看到“Madeira”这个项目名,很多人会以为是某个旅游地或者葡萄酒品牌。但把标题和热搜词放在一起看——FEX-Emu、Wine、DXMT、iOS、x86-64——方向就非常清楚了:这是一个围绕 跨架…

2026/10/1 11:41:21 阅读更多 →

最新新闻

多智能体系统上下文工程:透明架构引擎设计与实践

多智能体系统上下文工程:透明架构引擎设计与实践

刚开始做多智能体系统的时候,我被一个问题折磨了很久:明明每个Agent的提示词都写得非常完整,该定义的规则定义了,该给的示例给了,但一旦把三五个Agent串起来,整个系统的行为就变得像随机游走——今天能跑通…

2026/10/2 15:04:18 阅读更多 →
AI编码代理上下文工程实战:ChatMemory滑动窗口与Context-mode MCP全解析

AI编码代理上下文工程实战:ChatMemory滑动窗口与Context-mode MCP全解析

跟AI编码代理打交道这两年,最让我崩溃的从来不是模型不够聪明,而是它“记性”太差。上下文工程这个词,最初听起来像学术黑话,直到我在一个连续重构的场景里被同一个坑绊倒三次,才意识到上下文不是一个容量问题&#xf…

2026/10/2 15:04:18 阅读更多 →
OpenCode实战指南:终端里的AI结对编程代理完全解析

OpenCode实战指南:终端里的AI结对编程代理完全解析

最近不少朋友在问我OpenCode到底怎么样,尤其是那些已经在用Cursor、GitHub Copilot,又想试试终端AI工作流的人。我在自己的项目里跑了几个月,最大的感受是:OpenCode不是又一个IDE插件,它是一个真正“住在终端里的AI结对…

2026/10/2 15:04:18 阅读更多 →
自动化测试结果监控全攻略:从采集到告警的精细化实践

自动化测试结果监控全攻略:从采集到告警的精细化实践

在自动化测试这条路上,我见过太多团队把精力花在“写脚本”上,脚本一多、跑起来一片绿就觉得万事大吉。等工作半年、用例上千,问题就开始露头了:昨天还全线通过的用例,今天莫名其妙挂了一片,是代码改了&…

2026/10/2 15:04:18 阅读更多 →
Linux ptrace调试机制深度解析:从childRE看进程控制权本质

Linux ptrace调试机制深度解析:从childRE看进程控制权本质

1. 这不是一道“普通”的逆向题:从BUUCTF [2019红帽杯]childRE看真实CTF逆向的底层逻辑你点开BUUCTF,搜到[2019红帽杯]childRE,点进去看到一个Linux ELF文件,名字叫childre——注意,是小写re,不是RE。很多人…

2026/10/2 15:04:18 阅读更多 →
百度云分享链接批量检测:用代码快速筛出失效链接

百度云分享链接批量检测:用代码快速筛出失效链接

管理一个攒了好几年的网盘链接库,迟早会碰到同一个扎心场景:点开收藏夹里几十条分享链接,一条条点过去,一半以上提示"你访问的页面不存在"或者"链接已失效"。手动排查一遍,半小时没了,…

2026/10/2 15:03:18 阅读更多 →

日新闻

从零搭建AI工程化:模型之外的完整闭环

从零搭建AI工程化:模型之外的完整闭环

先搞清楚一件事:从零开始做 AI 工程化,难的从来不是调模型、写提示词,而是把一套原型 Demo 变成长得像是“正经系统”的东西。你手里可能已经有了能跑通的代码,也可能刚读完一些概念,但真到了要把它变成可维护、可观测…

2026/10/2 0:00:20 阅读更多 →
大模型训练显存估计与混合精度训练实战指南

大模型训练显存估计与混合精度训练实战指南

1. 大模型训练显存估计与混合精度训练详解显存不够用,几乎是每个做大模型训练的人都会撞上的第一堵墙。你可能也经历过:模型代码写完了,数据管道跑通了,满心欢喜地按下训练启动脚本,结果几秒钟后终端弹出一行红字——C…

2026/10/2 0:00:20 阅读更多 →
小样本学习数据集选型指南:27个真正可用的高质量数据集

小样本学习数据集选型指南:27个真正可用的高质量数据集

1. 小样本学习的“弹药库”:为什么你总在找数据集,却总找不到真正能用的? 小样本、数据集——这两个词最近半年在我处理的200多个AI项目咨询里,出现频率排进前三。不是模型调不好,不是代码写不对,而是卡在…

2026/10/2 0:00:20 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 6:09:11 阅读更多 →