Vue3虚拟滚动列表实现与性能优化
1. 项目概述为什么需要虚拟滚动列表组件在Vue3前端开发中数据列表渲染是最常见的场景之一。当列表项数量超过100条时传统渲染方式会将所有DOM节点一次性插入文档流这会导致严重的性能问题。我在实际项目中遇到过这样的案例一个包含5000条数据的表格在传统渲染方式下页面完全卡死控制台不断抛出Maximum call stack size exceeded错误。虚拟滚动技术通过按需渲染解决了这个问题。它的核心原理是只渲染可视区域内的列表项通过动态计算和定位模拟完整列表的滚动效果监听滚动事件实时更新可视区域内容对于定高列表实现相对简单。但当遇到不定高项目时如包含动态内容的卡片、图文混排等就需要更复杂的解决方案。这正是本文要重点探讨的内容。2. 虚拟滚动核心原理拆解2.1 基础虚拟滚动实现方案一个最基础的虚拟滚动组件需要实现以下核心功能// 伪代码展示核心逻辑 const VirtualList { setup() { const visibleData computed(() { const start Math.floor(scrollTop.value / itemSize) const end start visibleCount.value return listData.value.slice(start, end) }) const listStyle computed(() ({ height: ${listData.value.length * itemSize}px })) return { visibleData, listStyle } } }这种方案适用于固定高度的列表项但存在明显局限需要预先知道每个项目的高度无法适应内容动态变化的情况滚动条精度会有偏差2.2 不定高项目的挑战与解决方案不定高项目的难点在于无法预先计算总高度和位置滚动过程中需要动态测量项目尺寸快速滚动时如何避免频繁重排解决方案通常采用动态测量位置缓存的策略初始化阶段为每个项目设置预估高度渲染阶段记录实际渲染高度到缓存滚动阶段使用缓存数据进行位置计算更新阶段当项目尺寸变化时更新缓存3. Vue3不定高虚拟列表完整实现3.1 组件基础结构设计interface VirtualListProps { data: any[] estimatedItemSize?: number bufferSize?: number } const props definePropsVirtualListProps() // 位置缓存数据结构 interface PositionCache { index: number top: number bottom: number height: number }3.2 核心算法实现3.2.1 动态位置计算const positions refPositionCache[]([]) // 初始化位置缓存 const initPositions () { positions.value props.data.map((_, index) ({ index, top: index * props.estimatedItemSize!, bottom: (index 1) * props.estimatedItemSize!, height: props.estimatedItemSize! })) } // 根据滚动位置计算可见范围 const getVisibleRange () { const scrollTop containerRef.value?.scrollTop || 0 const start findNearestItemIndex(scrollTop) const end findNearestItemIndex(scrollTop containerHeight.value) return { start: Math.max(0, start - props.bufferSize!), end: Math.min(props.data.length - 1, end props.bufferSize!) } }3.2.2 动态高度测量// 使用ResizeObserver监听项目高度变化 const observer new ResizeObserver(entries { entries.forEach(entry { const index parseInt(entry.target.getAttribute(data-index) || 0) const height entry.contentRect.height if (positions.value[index].height ! height) { updateItemSize(index, height) } }) }) const updateItemSize (index: number, height: number) { const oldHeight positions.value[index].height positions.value[index].height height positions.value[index].bottom positions.value[index].top height // 更新后续所有项目的位置 for (let i index 1; i positions.value.length; i) { positions.value[i].top positions.value[i-1].bottom positions.value[i].bottom positions.value[i].top positions.value[i].height } // 更新容器总高度 totalHeight.value positions.value[positions.value.length-1].bottom }3.3 性能优化策略滚动节流使用requestAnimationFrame优化滚动事件const handleScroll () { if (!rafId.value) { rafId.value requestAnimationFrame(() { updateVisibleData() rafId.value null }) } }缓存DOM节点避免频繁创建/销毁DOMconst itemMap new Mapnumber, VNode() const renderItem (item: any, index: number) { if (!itemMap.has(index)) { itemMap.set(index, h(Item, { item, index })) } return itemMap.get(index) }动态缓冲区域根据滚动速度调整预渲染数量const getDynamicBufferSize () { const scrollSpeed Math.abs(lastScrollTop.value - scrollTop.value) return Math.min( maxBufferSize, baseBufferSize Math.floor(scrollSpeed / speedFactor) ) }4. 实战中的问题与解决方案4.1 常见问题排查表问题现象可能原因解决方案滚动时出现空白区域位置缓存未及时更新检查ResizeObserver是否正确绑定滚动条跳动高度估算偏差过大调整estimatedItemSize更接近实际值滚动卡顿滚动事件处理过重增加节流减少DOM操作内存泄漏未清理观察器和缓存在onUnmount中清理资源4.2 性能优化实测数据在以下环境下进行测试测试数据10,000条不规则高度项目设备配置MacBook Pro M1/16GB优化策略首次渲染(ms)滚动FPS内存占用(MB)无优化120012320基础虚拟滚动18045120动态高度22038140所有优化25055130关键发现动态高度带来的性能损耗主要来自初始测量阶段滚动过程中的性能影响在可接受范围内5. 进阶技巧与扩展思路5.1 动态加载大数据集对于超大数据集(如100万)可以采用分页加载策略const loadMore () { if ( scrollTop.value containerHeight.value totalHeight.value - threshold ) { fetchNextPage() } } const fetchNextPage async () { const newData await api.getData({ offset: positions.value.length, limit: pageSize }) // 追加新数据时更新位置缓存 const startIndex positions.value.length positions.value [ ...positions.value, ...newData.map((_, i) ({ index: startIndex i, top: positions.value[startIndex - 1]?.bottom || 0, bottom: 0, // 初始化为0渲染后更新 height: estimatedItemSize })) ] }5.2 与Vue3新特性结合使用Composition API封装逻辑export function useVirtualList(options: VirtualListOptions) { // 所有核心逻辑可以封装在这里 return { listRef, visibleData, scrollTo } }基于Teleport实现固定表头template div classheader v-if$slots.header Teleport to#fixed-header slot nameheader / /Teleport /div div classlist reflistRef !-- 列表内容 -- /div /template利用CSS Contain优化渲染.virtual-item { contain: strict; will-change: transform; }5.3 服务端渲染(SSR)适配虚拟滚动在SSR环境需要特殊处理onMounted(() { if (process.client) { // 只在客户端初始化虚拟滚动 initVirtualScroll() } else { // 服务端渲染完整列表 visibleData.value props.data } })6. 组件API设计与最佳实践6.1 完整组件Props设计interface VirtualListProps { // 数据源 data: any[] // 尺寸相关 estimatedItemSize?: number containerSize?: number | string bufferSize?: number // 功能控制 scrollDebounce?: number horizontal?: boolean ssr?: boolean // 事件 onScroll?: (event: { scrollTop: number }) void onReachBottom?: () void }6.2 插槽设计建议template VirtualList :dataitems !-- 默认项目插槽 -- template #default{ item, index } div classitem{{ item.text }}/div /template !-- 顶部固定内容 -- template #header div classheader列表标题/div /template !-- 底部加载状态 -- template #footer div v-ifloading classloading加载中.../div /template /VirtualList /template6.3 性能监控与调试建议在开发阶段添加性能标记const startMark (name: string) { if (process.env.NODE_ENV development) { performance.mark(${name}-start) } } const endMark (name: string) { if (process.env.NODE_ENV development) { performance.mark(${name}-end) performance.measure(name, ${name}-start, ${name}-end) const duration performance.getEntriesByName(name)[0].duration console.log(${name} took ${duration.toFixed(2)}ms) } } // 使用示例 startMark(updateVisibleData) updateVisibleData() endMark(updateVisibleData)7. 与其他方案的对比分析7.1 主流虚拟滚动库对比特性vue-virtual-scrollervueuc/VVirtualList本文方案Vue3支持需适配层原生支持原生支持不定高支持有限完善完善横向滚动支持支持支持SSR支持需额外配置内置内置依赖大小较大较小可定制动态加载插件实现内置内置7.2 何时选择自研方案建议在以下情况考虑自研有特殊UI需求现有库无法满足需要深度性能优化项目对包体积极其敏感需要与现有组件深度集成否则成熟的第三方库通常是更安全的选择。我在实际项目中的经验是对于中小型项目直接使用vueuc/VVirtualList对于大型复杂应用才会考虑自研方案。8. 项目实战经验分享8.1 真实案例电商商品列表优化在某电商平台项目中我们遇到了这样的需求商品卡片高度不固定取决于标题行数、价格区域等需要支持快速跳转到指定分类滚动时保持流畅的动画效果最终实现的解决方案包含以下关键点双阶段渲染策略const renderItem (item) { if (inVisibleRange(item.index)) { return h(FullItem, { item }) } else { return h(Placeholder, { style: { height: ${positions[item.index].height}px } }) } }分类跳转优化const scrollToCategory (categoryId) { const index data.value.findIndex(item item.category categoryId) if (index 0) { scrollToIndex(index, { behavior: smooth, offset: -100 // 预留标题空间 }) } }滚动动画处理.virtual-item { transition: transform 0.2s ease-out; will-change: transform; }8.2 性能优化关键指标在实现虚拟滚动组件时建议监控以下指标首次内容渲染时间(FCP)应控制在300ms以内滚动帧率保持在50FPS以上内存占用万级数据不应超过200MB滚动响应延迟用户操作到视觉反馈应小于50ms可以使用Chrome DevTools的Performance面板进行详细分析重点关注Layout Shift次数Forced Reflow事件Long Task持续时间8.3 移动端适配技巧在移动端需要特别注意惯性滚动处理let momentumStartTime 0 const handleTouchStart () { momentumStartTime performance.now() } const handleTouchEnd () { const duration performance.now() - momentumStartTime if (duration 300) { // 快速滑动预测滚动位置 predictScrollPosition() } }触摸事件优化containerRef.value.addEventListener(touchmove, handleTouchMove, { passive: true })移动端滚动条隐藏.virtual-scroller { -webkit-overflow-scrolling: touch; scrollbar-width: none; } .virtual-scroller::-webkit-scrollbar { display: none; }9. 测试策略与质量保障9.1 单元测试重点位置计算逻辑test(should calculate correct visible range, () { const { result } renderHook(() useVirtualList({ data: Array(100).fill(0), containerSize: 500, estimatedItemSize: 50 })) act(() { result.current.scrollTo(300) }) expect(result.current.visibleData.value.length).toBe(12) // 500/50 buffer })动态高度更新test(should update positions when item height changes, async () { const { vm } mount(VirtualList, { props: { data: [{ id: 1 }] } }) await nextTick() const itemHeight 100 jest.spyOn(vm.$el.children[0], clientHeight, get).mockReturnValue(itemHeight) await vm.onItemResize(0) expect(vm.positions[0].height).toBe(itemHeight) })9.2 E2E测试方案使用Cypress进行端到端测试describe(VirtualList, () { it(should render visible items only, () { cy.visit(/) cy.get(.virtual-item).should(have.length, 10) // 默认可见数量 cy.scrollTo(0, 1000) cy.get(.virtual-item).should(have.length.greaterThan, 10) }) it(should handle dynamic heights, () { cy.get(.virtual-item).first().invoke(height).then(initialHeight { cy.get(.virtual-item).first().find(button).click() cy.get(.virtual-item).first().should(have.height, initialHeight 50) }) }) })9.3 性能测试基准建议设置以下性能基准benchmark(scroll performance, { setup() { return mountComponentWithLargeData() }, test() { scrollComponent(1000) }, threshold: 50fps }) benchmark(initial render, { test() { mountComponentWithLargeData() }, threshold: 300ms })10. 组件库集成指南10.1 作为独立包发布推荐的项目结构dist/ virtual-list.umd.js virtual-list.esm.js style.css src/ VirtualList.vue composables/ useVirtualList.ts utils/ scrollUtils.tspackage.json关键配置{ name: your-scope/virtual-list, version: 1.0.0, main: dist/virtual-list.umd.js, module: dist/virtual-list.esm.js, exports: { .: { import: ./dist/virtual-list.esm.js, require: ./dist/virtual-list.umd.js }, ./style.css: ./dist/style.css }, peerDependencies: { vue: ^3.0.0 } }10.2 主题定制方案通过CSS变量支持主题定制.virtual-list { --vl-scrollbar-color: #ccc; --vl-scrollbar-hover-color: #aaa; --vl-item-active-bg: #f5f5f5; } .virtual-list.scrollbar { scrollbar-color: var(--vl-scrollbar-color) transparent; } .virtual-list.scrollbar:hover { scrollbar-color: var(--vl-scrollbar-hover-color) transparent; }10.3 国际化支持interface VirtualListMessages { loading?: string empty?: string } const defaultMessages { en: { loading: Loading..., empty: No data }, zh: { loading: 加载中..., empty: 暂无数据 } } const props defineProps{ messages?: VirtualListMessages locale?: string }() const t (key: keyof VirtualListMessages) { return props.messages?.[key] ?? defaultMessages[props.locale || en][key] ?? defaultMessages.en[key] }11. 未来演进方向11.1 Web Worker优化将位置计算等CPU密集型任务移到Web Workerconst worker new Worker(./virtualList.worker.js) worker.postMessage({ type: INIT, data: props.data, estimatedItemSize: props.estimatedItemSize }) worker.onmessage (event) { if (event.data.type VISIBLE_RANGE) { visibleData.value event.data.range } }11.2 WASM加速对于超大数据集可以考虑使用RustWASM加速计算// src/lib.rs #[wasm_bindgen] pub fn calculate_visible_range( positions: Vecf64, scroll_top: f64, container_height: f64 ) - Vecusize { // Rust实现的高性能计算逻辑 }11.3 机器学习预测基于用户滚动行为预测下一步可能浏览的区域const scrollPatternAnalyzer new ScrollPatternAnalyzer() const handleScroll (event) { scrollPatternAnalyzer.recordScroll(event) if (scrollPatternAnalyzer.predictWillScrollDown()) { preloadNextChunk() } }12. 开发者体验优化12.1 DevTools集成开发专属的Chrome扩展用于调试虚拟列表const sendToDevTools (data: DebugData) { if (process.env.NODE_ENV development) { window.postMessage({ source: virtual-list-devtools, payload: data }, *) } } // 在关键节点发送调试信息 sendToDevTools({ type: POSITIONS_UPDATE, positions: positions.value, visibleRange: visibleRange.value })12.2 可视化配置工具提供在线Playground实时调整参数查看效果template div classconfig-panel Slider v-modelconfig.estimatedItemSize min20 max200 / Slider v-modelconfig.bufferSize min0 max20 / Toggle v-modelconfig.dynamicHeight / /div VirtualList v-bindconfig / /template12.3 性能分析插件内置性能分析组件显示关键指标template div classperf-monitor v-ifshowPerf divFPS: {{ fps }}/div divVisible Items: {{ visibleData.length }}/div divTotal Items: {{ data.length }}/div divMemory: {{ memoryUsage }}MB/div /div /template13. 社区生态建设13.1 示例项目集建议提供以下典型场景的示例聊天消息列表动态高度自动滚动到底部电商商品网格多列布局图片懒加载大型表格固定表头列虚拟化时间轴动态加载位置标记13.2 插件系统设计支持通过插件扩展功能interface VirtualListPlugin { install(vlist: VirtualListInstance): void } class SelectionPlugin implements VirtualListPlugin { install(vlist) { vlist.onItemClick (item) { // 处理选择逻辑 } } } // 使用插件 const vlist new VirtualList({ plugins: [new SelectionPlugin()] })13.3 贡献指南要点代码规范ESLint Prettier统一风格提交信息遵循Conventional Commits测试要求新增功能需包含单元测试文档更新同步修改API文档和示例性能基准不能低于现有水平14. 总结与个人实践心得在多个项目中实现虚拟滚动组件后我总结了以下几点关键经验预估高度要合理estimatedItemSize设置得越接近实际平均值初始渲染效果越好。可以通过抽样测量获取近似值。缓冲区域要动态固定缓冲区域要么浪费资源要么在快速滚动时出现空白。基于滚动速度的动态缓冲效果最好。ResizeObserver要慎用虽然方便但频繁触发会导致性能问题。建议添加适当的防抖并考虑批量更新策略。内存管理很重要对于超大列表要注意及时清理不再使用的DOM节点和观察器避免内存泄漏。移动端差异大移动端的触摸事件、惯性滚动等特性需要特殊处理不能简单照搬桌面端方案。在实际项目中我通常会先使用成熟的第三方库快速验证需求当遇到性能瓶颈或特殊需求时再考虑基于业务特点进行定制开发。虚拟滚动看似简单但要实现一个生产级可用的解决方案需要考虑的边界情况非常多。建议在自研前充分评估需求避免过度设计。

相关新闻

Unity与URSim搭建机械臂数字孪生:从零实现实时同步与仿真

Unity与URSim搭建机械臂数字孪生:从零实现实时同步与仿真

1. 项目概述与核心价值如果你对工业机器人、自动化或者游戏开发感兴趣,那么“数字孪生”这个词对你来说一定不陌生。简单来说,数字孪生就是在虚拟世界里,一比一复刻一个物理实体,让它能实时同步、预测和优化物理实体的状态。听起来…

2026/10/14 15:55:46 阅读更多 →
Windows Defender Remover:重新定义Windows安全控制权的技术哲学

Windows Defender Remover:重新定义Windows安全控制权的技术哲学

Windows Defender Remover:重新定义Windows安全控制权的技术哲学 【免费下载链接】windows-defender-remover A tool which is uses to remove Windows Defender in Windows 8.x, Windows 10 (every version) and Windows 11. 项目地址: https://gitcode.com/gh_m…

2026/10/15 8:14:11 阅读更多 →
虚幻引擎项目资产管理:ProjectCleaner智能清理工具原理与应用

虚幻引擎项目资产管理:ProjectCleaner智能清理工具原理与应用

1. 项目概述:为什么你的UE项目需要一个“清洁工”干了这么多年虚幻引擎开发,我敢说,每个项目组都至少经历过一次“硬盘空间告急”的恐慌。项目初期,一切都很美好,资产库井井有条。但随着版本迭代,美术同学丢…

2026/10/15 1:21:44 阅读更多 →

最新新闻

微信小程序预约挂号系统:号源并发扣减与排班逻辑实战

微信小程序预约挂号系统:号源并发扣减与排班逻辑实战

简介:这份资源是面向计算机专业毕业设计场景的微信小程序预约挂号系统完整论文资料,适合正在准备毕设选题、需要参考系统分析与设计写作范式的学生,以及希望了解SSM框架与微信小程序结合开发思路的开发者。压缩包共1个doc文件,约2…

2026/10/15 13:57:45 阅读更多 →
审稿意见回复信英文模板和语料总结:用 TaoToken 统一 Key 跑通多模型润色与术语一致性校验

审稿意见回复信英文模板和语料总结:用 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/15 13:57:45 阅读更多 →
养龙虾踩坑:Linux没有图形界面,OpenClaw机器人要用浏览器怎么办?TaoToken统一Key接入实践

养龙虾踩坑:Linux没有图形界面,OpenClaw机器人要用浏览器怎么办?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/15 13:57:45 阅读更多 →
如何更改 Cursor 的背景颜色?从 settings.json 到 TaoToken 配置的完整实践

如何更改 Cursor 的背景颜色?从 settings.json 到 TaoToken 配置的完整实践

/* 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 13:57:45 阅读更多 →
Spring Boot与区块链在土地资源管理系统的实践

Spring Boot与区块链在土地资源管理系统的实践

1. 项目背景与核心价值新农村建设作为乡村振兴战略的重要抓手,正在全国范围内加速推进。在这个过程中,土地资源作为农村最重要的生产资料,其信息化管理水平直接关系到集体经济发展和农民权益保障。我们团队在参与某省数字乡村试点项目时&…

2026/10/15 13:57:45 阅读更多 →
海康iSecure Center综合管理平台实战:架构解读、部署流程与常见故障排查指南

海康iSecure Center综合管理平台实战:架构解读、部署流程与常见故障排查指南

简介:海康威视iSecure Center综合管理平台产品手册是一份面向安防项目集成商、系统运维人员及技术管理人员的专业文档,旨在帮助读者系统理解综合安防平台的架构设计、业务能力与运维要点。手册基于统一软件技术架构理念,围绕视频监控、门禁一…

2026/10/15 13:56:43 阅读更多 →

日新闻

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/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 9:49:14 阅读更多 →