跨端迁移到OpenHarmony:Redux Toolkit选择器避坑与性能优化
最近我把一个基于 React Native 的跨端应用往 OpenHarmony 平台上迁移整个过程中踩坑最多的并不是 UI 组件适配也不是原生模块桥接反而是状态管理里一个看似不起眼的环节——Redux Toolkit 选择器的使用。今天就把这块的实践经验完整梳理一遍从选择器的底层原理讲到 OpenHarmony 适配环境下的具体写法与性能优化希望能帮到正在做同类迁移的开发者。先明确一下这套组合的适用场景如果你的团队已经有 React Native 跨端开发经验现在需要面向 OpenHarmony 设备输出应用又不想完全推倒重来用 ArkTS 写一套原生实现那么通过社区维护的 React Native 适配框架跑在 OpenHarmony 上再搭配 Redux Toolkit 做全局状态管理是一条投入产出比非常高的路径。这套方案适合中小型团队也适合业务逻辑复杂度较高、需要跨 Android、iOS、OpenHarmony 三端统一维护的项目。下面就从实际开发视角把选择器这块彻底讲透。1. 项目背景RN 应用在 OpenHarmony 生态中的落地方式1.1 为什么选择 React Native 而非原生 ArkTS 开发很多团队在决定是否进入 OpenHarmony 生态时第一个纠结的问题就是开发语言选型。原生开发需要掌握 ArkTS 和 ArkUI 声明式范式这和 React 的组件模型虽然理念相似但具体写法、生态工具链、组件库体系完全是另一套东西。对于已经有 React Native 技术沉淀的团队来说选择适配层方案显然更划算——复用现有业务代码、复用 npm 生态中的大量库开发者不需要重新学习一门 UI 框架。目前社区主流的做法是通过某开源 RN 适配框架将 React Native 的 JavaScript 运行时和渲染层映射到 OpenHarmony 的 ArkUI 组件体系上。这个框架的整体架构可以理解为JavaScript 侧运行 React Native 核心逻辑通过 JSI 或者 Bridge 机制与 OpenHarmony 侧的 C 层通信再由 C 层调用 ArkUI 的原生组件完成渲染。对于业务开发而言大多数 React Native 组件代码可以直接跑只有涉及原生模块调用的部分需要额外封装 TurboModule 或 NativeModule 适配层。1.2 Redux Toolkit 在跨端状态管理中的位置在 React Native 项目里做全局状态管理可选方案不少Context useReducer、MobX、Zustand、Redux Toolkit。但放到 OpenHarmony 适配场景下Redux Toolkit 有几个独特优势第一Redux 的模式是单向数据流状态更新路径非常明确这在跨端调试时特别重要。当同一套代码在 Android 和 OpenHarmony 上出现表现不一致时你能很快定位是状态层的问题还是渲染层的问题。第二Redux DevTools 的时间旅行调试在 RN 适配层下依然可以用这比在 ArkUI 原生侧做状态调试要舒服得多。第三Redux Toolkit 内置了 Immer 处理不可变更新消除了手写展开运算符带来的深层嵌套更新错误这在状态结构复杂的业务中能省下大量排错时间。1.3 这套组合适合什么样的团队和项目我个人的判断标准是这样的如果项目需要同时覆盖 Android、iOS、OpenHarmony 三个平台业务逻辑占比高、UI 定制需求相对标准化那这套组合非常合适。反之如果产品是 OpenHarmony 独占的且对交互动效、系统能力调用要求极深那直接用 ArkTS 原生开发体验会更好。此外团队里如果已经有人熟悉 Redux 生态学习成本几乎为零如果是从零开始我建议先花一周时间把 Redux Toolkit 的核心概念吃透尤其是选择器这部分因为它直接关系到应用的长远性能。2. 选择器的运行原理一段必须吃透的源码逻辑2.1 选择器到底是什么从读数据到算数据很多初学者会把选择器简单理解为从 store 里读数据的函数这个理解过于狭窄。我更喜欢把选择器定义为从 state 中提取或派生数据的纯函数。它的核心价值在于把组件对状态的具体读取逻辑从组件内部抽离出来放到一个可复用、可测试、可组合的独立层。举个例子假设 store 里存了一个设备列表每个设备有型号、系统版本、在线状态。组件直接读取时你得在组件里写一堆 filter、sort 逻辑。而用选择器你可以这样组织import { createSlice } from reduxjs/toolkit; const devicesSlice createSlice({ name: devices, initialState: { list: [], filter: all, // all | online | offline }, reducers: { setDevices(state, action) { state.list action.payload; }, setFilter(state, action) { state.filter action.payload; }, }, }); export const { setDevices, setFilter } devicesSlice.actions; export default devicesSlice.reducer;然后在选择器文件里import { createSelector } from reduxjs/toolkit; export const selectAllDevices (state) state.devices.list; export const selectFilter (state) state.devices.filter; export const selectVisibleDevices createSelector( [selectAllDevices, selectFilter], (devices, filter) { switch (filter) { case online: return devices.filter((d) d.online); case offline: return devices.filter((d) !d.online); default: return devices; } } );这里selectVisibleDevices就是一个派生数据选择器。它不直接修改任何状态也不依赖组件内部变量输入同样的 state 永远输出同样的结果。这种纯函数的确定性是后续所有性能优化的基石。2.2 createSelector 的记忆化机制createSelector是 Reselect 库的核心 APIRedux Toolkit 直接内联了它。记忆化的本质是缓存当输入选择器的返回值与上一次相同时输出选择器不会重新执行直接返回上一次的结果。你可能要问不就是一个缓存吗有什么值得深入研究的问题在于缓存失效的判定时机和比较方式。Reselect 默认使用做比较这意味着它检查的是引用是否相同而不是内容是否相等。这个特性带来一个巨大的好处只要state.devices.list的引用没变即使里面某个设备的字段发生了深层次变化但 slice 返回了新的数组只要顶层 reducer 没有新建这个数组记忆化依然会命中。这理解起来有点绕我用一个实际场景解释Redux Toolkit 的 reducer 基于 Immer它在更新状态时采用结构共享。也就是说当你只修改了某个数组元素的属性Immer 会创建新的数组引用但数组中未修改的元素依然指向旧引用。此时selectAllDevices返回的新数组引用与旧数组不同所以createSelector会判定输入发生变化重新执行派生逻辑。反之如果某个操作没有触发devices.list的变化selectAllDevices返回的引用保持不变selectVisibleDevices就会直接命中缓存。理解这个机制对性能调优极为重要。当你在组件中使用useSelector订阅派生数据时记忆化能够在 Redux store 更新后阻止与本次更新无关的组件发生重渲染。2.3 组合选择器与依赖链选择器真正的威力来自组合。你可以从基础选择器构建出一棵依赖树上层选择器的输入是下层选择器的输出。这种模式让复杂的数据变换分层、清晰每层都可以单独测试。export const selectDevicesByModel createSelector( [selectAllDevices, (state, model) model], (devices, model) devices.filter((d) d.model model) ); export const selectOnlineCount createSelector( [selectAllDevices], (devices) devices.filter((d) d.online).length );依赖链的注意点是每一层都应该保持纯函数特性不要在派生逻辑里面做有副作用的事。如果你在某个选择器里写了console.log或者修改了外部变量记忆化机制会放大这个问题——某些情况下它被跳过、某些情况下它被执行输出结果不可预期。我把这条规则视为选择器开发的第一戒律。3. OpenHarmony 适配下选择器使用的关键细节3.1 useSelector 在 RN 适配层的触发逻辑useSelector是 react-redux 提供的订阅 Hook。它在组件挂载时从 store 中获取选择器的值并订阅 store 的变化。每当 store 更新react-redux 会比较选择器的返回值与上一次的差异不同则触发组件重渲染。在具体讨论 React Native 适配 OpenHarmony 之前有必要先说清 react-redux 的内部判断逻辑。新版 react-redux 里useSelector默认使用严格比较但可以通过equalityFn参数自定义比较函数。这一点在 OpenHarmony 适配场景下格外重要因为如果选择器每次返回一个新数组或者新对象即使内容完全一样组件也会频繁重渲染这会直接拖累低端设备的性能。这里有一个特别容易被忽视的坑在 React 18 的并发渲染特性下useSelector的行为和旧版本有差异。OpenHarmony 适配框架目前对新架构的支持成熟度不一如果 App 跑在旧架构Bridge模式下需要确保 react-redux 版本与 React 版本兼容。建议固定版本组合并在 CI 中验证避免升级时打破订阅行为。3.2 序列化与环境依赖不要在状态里放非纯数据OpenHarmony 适配层与 Android/iOS 的一个显著差异是JavaScript 运行时与原生侧的数据交换往往存在序列化开销某些对象类型如Map、Set、Date在跨桥传输时会丢失自定义属性或变成普通对象。这条限制直接反馈到选择器设计上——你的 state 树和选择器返回值应当尽量保持可序列化纯数据。我的建议是state 里只存 plain object、array、string、number、boolean。如果你需要存 Date统一转成时间戳如果需要存 Map用普通对象或数组替代。选择器返回值同理不要在返回值里包含函数引用或复杂对象。这不仅是跨端安全的需要也与 Redux DevTools 的序列化调试机制保持一致。这条规则能让应用在 Android 和 OpenHarmony 上的行为保持一致避免出现Android 上好好的OpenHarmony 上数据就变了样的怪异问题。3.3 浅比较陷阱与引用稳定性组合使用createSelector时最常见的性能陷阱是输出选择器返回了一个新对象或新数组导致所有订阅方都发生重渲染。比如export const selectDeviceStats createSelector( [selectAllDevices], (devices) ({ total: devices.length, online: devices.filter((d) d.online).length, }) );这个选择器每次执行都会返回一个新对象即使 total 和 online 都没变。如果多个组件都用useSelector(selectDeviceStats)store 每次更新所有组件都会重渲染。正确的写法是先分别计算基础值再组合export const selectTotalCount createSelector( [selectAllDevices], (devices) devices.length ); export const selectOnlineCount createSelector( [selectAllDevices], (devices) devices.filter((d) d.online).length ); export const selectDeviceStats createSelector( [selectTotalCount, selectOnlineCount], (total, online) ({ total, online }) );这样当在线设备数不变时selectOnlineCount缓存命中selectDeviceStats的输入两个都未变化它的返回值引用保持不变组件不会重渲染。引用稳定性在 OpenHarmony 适配场景下还有一个隐藏的放大效应RN 适配层的组件更新往往需要穿越 JavaScript 到原生侧的通信链路一次不必要的重渲染消耗的不仅是 JS 线程的 CPU 时间还有跨桥通信的时间。所以在 OpenHarmony 上做性能优化减少渲染次数比单次渲染提速更关键。4. 实际项目中的选择器设计模式4.1 按业务域拆选择器的目录结构选择器不是随便放在一起就行组织方式直接影响可维护性。我目前在项目中采用的结构是src/ store/ slices/ devicesSlice.js settingsSlice.js selectors/ deviceSelectors.js settingsSelectors.js index.js hooks/ useDeviceData.js每个业务域一个 selector 文件文件内部先放基础选择器直接映射 state 字段再放派生选择器基于基础选择器组合。index.js统一导出组件层只从store/selectors/index.js导入不关心具体文件位置。这样的好处是新增派生数据时你只需要在对应域文件里增加一个导出不会影响其他域。我见过很多项目把所有选择器堆在一个大文件里几百行不分类最终没有人敢动这些代码因为不知道修改一处会不会影响别处的缓存依赖。按域拆分后依赖关系可视化程度大幅提升。4.2 参数化选择器与工厂函数有些选择器需要接收组件传入的参数比如按设备型号过滤。如果直接在createSelector里接收参数记忆化会失效因为每次调用传入的参数都不同。正确的做法是使用工厂函数外面包一层函数内部创建独立的createSelector实例每个参数值对应独立的缓存。export const createSelectDevicesByModel (model) createSelector( [selectAllDevices], (devices) devices.filter((d) d.model model) );组件中使用时配合useMemoconst selectDevicesByModel useMemo( () createSelectDevicesByModel(model), [model] ); const devices useSelector(selectDevicesByModel);这里要特别注意工厂函数创建的选择器必须在组件外部或useMemo中创建不能在每次渲染时都新建否则useSelector会因为选择器引用变化而不断重新订阅。这个坑极为隐蔽表现是组件在父组件渲染时反复触发子组件更新怎么排查都找不到原因。4.3 与请求层搭配的取舍在真实项目中选择器并不是孤立存在的。请求数据的方案比如 React Query、SWR、RTK Query会与选择器产生协作和竞争。我的建议是服务端状态交给请求层缓存客户端全局状态留在 Redux。选择器只负责从 Redux 中派生客户端需要的展示数据不要试图把服务端响应全部塞进 Redux 再派生。比如设备列表的拉取可以用 RTK Query 的useGetDevicesQuery拿到数据后通过onCacheEntryAdded或手动分发动作写入 Redux 的devicesslice再由选择器负责过滤、排序、统计。这样请求层的缓存失效、重新拉取逻辑与 Redux 的派生计算各司其职不会相互纠缠。如果项目里已经用了 React Query也可以完全跳过 Redux 存服务端数据这一步直接在组件内部用selectFromResult做派生。但当多个组件共享同一份派生数据时集中到 Redux 选择器比各组件自行散布更清晰也更容易测试。5. 排查与性能验证三次真实踩坑记录5.1 坑一selector 返回值是新数组导致无限渲染某次开发中我在一个列表组件里使用了useSelector订阅筛选后的设备列表选择器写成了export const selectOnlineDevices (state) state.devices.list.filter((d) d.online);看起来没问题但在定位一个页面卡死、CPU 飙升的问题时我发现组件在一个包含多个子列表的页面里反复渲染。原因在于filter每次执行都返回一个新数组引用useSelector判定返回值变化触发重渲染重渲染后useSelector重新执行选择器又是一个新数组于是陷入死循环。修复方式有两种一是改用createSelector让记忆化缓存数组引用二是在useSelector中传入自定义比较函数const onlineDevices useSelector(selectOnlineDevices, shallowEqual);但第二种方案治标不治本比较一个长列表的成本不低。推荐优先用createSelector从源头保证引用稳定。5.2 坑二createSelector 误把非纯函数当纯函数还有一次我在选择器里调用了生成随机 ID 的函数用于向列表项附加临时 keyexport const selectDevicesWithTempKey createSelector( [selectAllDevices], (devices) devices.map((d) ({ ...d, tempKey: generateUUID() })) );这个选择器每次执行都会生成新的 UUID导致引用变化组件不断重渲染。更严重的是行为不可预测createSelector的缓存在某些情况下命中、某些情况下不命中导致 UI 上的临时 key 跳变。排查时还容易误判为组件 key 设置问题。这个坑给我的教训是选择器必须严格保持纯函数不能在派生逻辑中引入随机性、时间依赖或外部状态读取。如果确实需要随机值在 dispatch action 时就生成好并存入 state让选择器只做数据变换。5.3 坑三OpenHarmony 低端设备上的卡顿定位在 OpenHarmony 的某款低配开发板上测试时应用出现了掉帧和点击延迟。起初我怀疑是原生模块的渲染性能问题但用开发者工具自带的性能分析器抓取调用栈后发现问题出在 JS 线程的长时间任务上。进一步分析发现某些页面上存在多个昂贵的派生计算每次 store 更新都会重新执行。由于选择器没有组织好依赖关系一个用户操作可能触发了整棵派生链上的大部分节点重算。优化方案是把一个复杂的选择器拆成多个粒度的createSelector让无关的派生计算互不影响并利用记忆化让高频操作只触发局部重算。另外我还做了一个优化将某些只在特定页面使用的派生数据从全局选择器改为页面局部useMemo计算避免全局 store 更新时所有订阅者都参与计算。这个取舍要因场景而异不可全局套用。6. 写在最后的工程建议项目迁移到 OpenHarmony 只是开始长期维护才是真正的考验。关于 Redux Toolkit 选择器的使用我最后分享几点经验。第一给你团队定一条规则业务代码永远不要直接从 store 里读取原始状态统一走选择器。这不仅是规范也是未来状态结构调整的唯一安全途径。没有选择器这层隔离你每次调整 state 结构都要在十几个组件里做修改极易出错。第二选择器的性能是设计出来的不是优化出来的。从一开始就按业务域拆分、保持纯函数、控制返回值的引用稳定性后续基本不需要专门做选择器层面的性能优化。反过来如果前期乱写后面想靠createSelector补性能收益往往要重构大量代码。第三在 OpenHarmony 这种跨桥通信成本更高的环境里宁可多写几个选择器、多拆几层也不要写出一次遍历干完所有事的超级选择器。单次计算的性能不是瓶颈不必要的重复计算才是。最后选择器的单测一定要做。输入一个构造好的 state断言输出结果缓存命中与否可以用执行计数来验证。有了测试兜底后续修改 state 结构或调整派生逻辑时才不会半夜被线上问题叫起来。这套理论与实践组合我在模拟项目 X 和某跨平台系统的迁移中都完整跑过一遍。如果你正在做类似的技术选型或迁移希望这篇内容能帮你少走一些弯路。有问题欢迎在评论区聊我看到都会回复。

相关新闻

Transformer论文精读:自注意力机制与训练实战全解析

Transformer论文精读:自注意力机制与训练实战全解析

简介:这是一份围绕《Attention Is All You Need》论文制作的Transformer与Attention机制讲解PPT,适合NLP初学者、算法工程师及组会汇报场景。内容系统梳理Transformer的提出背景,针对RNN并行困难与CNN局部感受野问题,逐层拆解Self…

2026/10/15 6:22:44 阅读更多 →
TensorFlow实战指南:从环境搭建到模型部署的完整流程

TensorFlow实战指南:从环境搭建到模型部署的完整流程

还在几年前,提起TensorFlow,很多人脑子里蹦出来的还是“Google出品”“工业级框架”“部署方便”这几个标签。如今它在深度学习圈子里,基本是新人入门时最先接触的那一个,也是不少公司生产环境里真正在跑的那个。作为一个折腾过图…

2026/10/15 6:22:44 阅读更多 →
从入门到心流:理解内耗机制,找到进入学习状态的开关

从入门到心流:理解内耗机制,找到进入学习状态的开关

有时候我会觉得,学一个东西最难的阶段,恰恰是看起来最不该难的阶段——“入门”。很多人以为入门难在“东西不会”,但我观察下来,入门真正的坎,是心理层面的。那阵子你会在“要不要认真学”和“学了好像也没用”之间反…

2026/10/15 6:22:44 阅读更多 →

最新新闻

SpringBoot+Vue轻量级博客系统设计与实现

SpringBoot+Vue轻量级博客系统设计与实现

1. 项目概述:SpringBootVue轻量级博客系统设计去年帮学弟评审毕业设计时,发现市面上很多博客系统要么功能冗余,要么扩展性差。于是我们决定用SpringBootVue搭建一个轻量级但五脏俱全的在线创作社区。这个系统最核心的特点是采用前后端分离架构…

2026/10/15 6:55:51 阅读更多 →
正念与心流:10分钟呼吸练习,让专注成为生命底色

正念与心流:10分钟呼吸练习,让专注成为生命底色

1. 先说清楚:我们到底在谈什么你有没有过这样的时刻:明明手头有一件重要的事要做,却忍不住五分钟刷一次手机;开会时脑子飘到晚上吃什么;想读几页书,眼睛盯着一行字,心里却已经把明天的待办事项过…

2026/10/15 6:55:51 阅读更多 →
Oracle 数据实时入仓 Doris 和 StarRocks 怎么做?CDC、全量迁移与 5 种方案对比

Oracle 数据实时入仓 Doris 和 StarRocks 怎么做?CDC、全量迁移与 5 种方案对比

在很多企业系统中,Oracle 负责在线事务处理,Doris 或 StarRocks 负责报表、数据分析和实时数据服务。当订单、用户、支付和日志数据持续增长时,直接在 Oracle 上运行复杂统计 SQL,可能影响线上业务。因此,很多团队会建…

2026/10/15 6:55:51 阅读更多 →
SpringBoot+Vue+MyBatis构建企业级电商平台实战

SpringBoot+Vue+MyBatis构建企业级电商平台实战

1. 项目概述"企业级网购平台管理系统"是一个基于SpringBootVueMyBatis技术栈的完整电商解决方案。这套源码包含了电商平台的核心功能模块,采用前后端分离架构设计,数据库使用MySQL,适合快速构建中小型B2C或B2B电商平台。我在实际电…

2026/10/15 6:55:51 阅读更多 →
聚簇索引vs非聚簇索引:从底层原理到实战避坑全解析

聚簇索引vs非聚簇索引:从底层原理到实战避坑全解析

做后端开发这几年,索引相关的坑我踩过不少。面试被问过无数次“聚簇索引和非聚簇索引区别”,带新人的时候也发现,大部分同学对这两个词的理解停留在“一个有序一个无序”或者“聚簇索引就是主键”这种模糊层面。一旦落到实际业务,…

2026/10/15 6:55:51 阅读更多 →
盲盒商城软件开发案例:连接概率说明、微信支付、在线开赏与积分兑换

盲盒商城软件开发案例:连接概率说明、微信支付、在线开赏与积分兑换

盲盒商城的购买过程与普通商品商城有所不同。用户在付款前,需要了解盲盒价格、可抽取内容和相关规则;完成支付后,再进入开赏环节。商品展示、概率说明、支付结果和积分记录之间,需要形成清晰的操作顺序。 某公司的这套盲盒商城软件…

2026/10/15 6:54:51 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/14 7:53:34 阅读更多 →