简介本资源是一套面向Android开发者的Gradle依赖统一管理实践方案聚焦Windows平台下的Gradle命令高效执行与工程化配置优化。针对开发者常因路径符号如Linux/macOS的./在Windows环境报错、依赖配置分散难维护等问题提供开箱即用的批处理脚本与标准化配置模板。压缩包共34个文件含8个功能明确的.bat批处理脚本如libs1.bat至libs4.bat用于分模块依赖注入、4个核心gradle配置文件含config.gradle实现依赖版本集中声明、8个xml配置及5个png示意图辅以README.md说明文档和gradlew.bat等跨平台启动支持整体仅100KB轻量易集成。已有3660人学习下载读者可直接复用其目录结构设计如wrapper封装、多级bat分工、gradle.properties与config.gradle协同机制快速建立可维护的Android项目构建体系并掌握clean、assembleDebug、build等关键命令在Windows下的正确调用方式。1. GradleDependencyManagement为什么统一管理依赖后模块间版本冲突反而更难定位了你删掉implementation androidx.core:core-ktx:1.12.0编译却报core-ktx:1.10.1的 NoClassDefFoundError你明明在app/build.gradle里升级了kotlin-stdlib到 1.9.20但:feature:login模块跑测试时却加载了 1.8.20 的字节码——这不是玄学是 Gradle 依赖解析的「多层叠加 延迟决议」机制在暗处发力。GradleDependencyManagement 这个项目不提供新语法、不封装 DSL它只做一件事把所有依赖坐标group、name、version、约束规则force、exclude、strictly、BOM 导入逻辑从各模块的build.gradle中彻底剥离收束到一个中心化的versions.gradle或libs.versions.toml里。它解决的不是“怎么写依赖”而是“当 12 个模块、7 个插件、3 套 CI 流水线同时参与依赖决议时谁说了算”。搞懂它你才能在./gradlew app:dependencies --configuration releaseRuntimeClasspath输出的 3000 行树里一眼锁定那个偷偷把okhttp降级到 4.9.3 的 transitive dependency 来源。2. 版本声明层为什么libs.versions.toml不是配置文件而是 Gradle 的“类型安全符号表”Gradle 7.4 引入的 Version Catalog即libs.versions.toml常被误认为是“ini 风格的配置文件”这是踩坑的起点。它实际是 Gradle 构建生命周期中首个被解析、且全程参与依赖图构建的元数据层其内容在settings.gradle阶段就被加载为VersionCatalog对象并注入到DependencyHandler的底层 resolver 中。这意味着你写的libs.okhttp.core不是字符串拼接而是一个带类型校验的符号引用libs.bundles.networking不是宏展开而是 resolver 在 resolve 阶段主动触发的 bundle 展开逻辑。2.1libs.versions.toml的真实结构三层嵌套与隐式约束该文件本质是 TOML 格式但 Gradle 对其做了语义增强。核心结构分三层[versions]定义版本别名如kotlin 1.9.20仅用于后续引用不生成任何依赖[libraries]定义单个依赖项如okhttp-core { module com.squareup.okhttp3:okhttp, version.ref okhttp }version.ref必须指向[versions]中的 key[bundles]定义依赖组合如networking [okhttp-core, retrofit-core]resolver 会将其展开为多个独立依赖项关键细节在于version.ref的绑定时机它在settings.gradle解析阶段完成绑定而非build.gradle执行时。因此若你在build.gradle中动态修改ext.kotlinVersion 1.9.21它对libs.versions.toml完全无效——这是设计使然不是 bug。# libs.versions.toml [versions] kotlin 1.9.20 okhttp 4.12.0 androidx-core 1.12.0 [libraries] kotlin-stdlib { module org.jetbrains.kotlin:kotlin-stdlib, version.ref kotlin } okhttp-core { module com.squareup.okhttp3:okhttp, version.ref okhttp } androidx-core-ktx { module androidx.core:core-ktx, version.ref androidx-core } [bundles] networking [okhttp-core, kotlin-stdlib]提示version.ref的值必须严格匹配[versions]中的 key 名大小写敏感。version 4.12.0是非法写法Gradle 会直接报错Cannot set the value of a read-only property。2.2settings.gradle中的加载逻辑为什么必须用enableFeaturePreview(VERSION_CATALOGS)Version Catalog 是 Gradle 的预览特性Preview Feature需显式启用。其加载入口在settings.gradleKotlin DSL 为settings.gradle.kts// settings.gradle.kts enableFeaturePreview(VERSION_CATALOGS) dependencyResolutionManagement { repositories { google() mavenCentral() } versionCatalogs { create(libs) { from(files(../gradle/libs.versions.toml)) } } }这段代码的关键行为是enableFeaturePreview(VERSION_CATALOGS)启用解析器对 TOML 的语义支持versionCatalogs.create(libs)创建名为libs的 catalog 实例其内容由from()指定的文件提供此 catalog 在dependencyResolutionManagement块内注册成为整个构建的全局依赖元数据源。注意from(files(...))中的路径是相对于settings.gradle文件的相对路径不是项目根目录。若libs.versions.toml放在gradle/目录下路径必须写../gradle/libs.versions.toml否则 Gradle 报File not found。2.3 在build.gradle中使用libs是如何变成DependencyHandler的一部分的一旦 catalog 注册成功libs就作为DependencyHandler的扩展属性注入所有模块的build.gradle。使用方式有三种但语义完全不同// build.gradle (Groovy DSL) dependencies { // ✅ 推荐类型安全引用resolver 自动处理 version 和 module implementation libs.okhttp.core // ⚠️ 危险手动拼接字符串绕过 catalog 约束失去版本统一性 implementation com.squareup.okhttp3:okhttp:${libs.versions.okhttp.get()} // ❌ 错误libs 不是 Map不能用 [] 访问 implementation libs[okhttp-core] }第一种写法libs.okhttp.core触发的是 Gradle 内置的VersionCatalogDependency类型resolver 在解析时会查找okhttp.core对应的modulecom.squareup.okhttp3:okhttp查找version.ref okhttp对应的[versions]值4.12.0自动应用该 catalog 定义的所有约束如strictly、force。而第二种写法${libs.versions.okhttp.get()}只是取字符串值完全脱离 resolver 的约束链等于手动开后门。3. 依赖约束层strictly、force、prefer三者为何不能混用它们在 resolver 中的执行顺序是什么统一管理的核心价值不在“写在哪”而在“谁来裁决冲突”。Gradle 的依赖解析器Dependency Graph Resolver采用多阶段约束合并策略strictly、force、prefer是三个不同阶段的干预指令混用会导致未定义行为。理解它们的执行顺序是读懂./gradlew app:dependencies输出的关键。3.1strictly版本锁死resolver 的“铁律”strictly是最硬的约束它在依赖图构建初期Graph Construction Phase就生效作用于单个依赖声明本身。一旦某依赖被标记strictlyresolver 在后续所有阶段都禁止其版本被任何其他声明覆盖。# libs.versions.toml [libraries] okhttp-core { module com.squareup.okhttp3:okhttp, version 4.12.0, strictly true # ← 关键此行让 resolver 在图构建时就锁定版本 }等价于 Groovy 中implementation(com.squareup.okhttp3:okhttp:4.12.0) { strictly() }效果即使另一个模块通过 BOM 或force声明了okhttp:4.11.0resolver 也会报Could not resolve com.squareup.okhttp3:okhttp:4.11.0错误因为strictly已将4.12.0设为唯一合法版本。3.2force强制覆盖resolver 的“最终仲裁”force是第二硬的约束它在依赖图构建完成后、版本决议前Resolution Phase生效作用于整个图。它不修改单个依赖声明而是向 resolver 发出一条全局指令“无论图中出现多少个okhttp最终只允许4.12.0存在”。# libs.versions.toml —— force 必须在 [versions] 或 [libraries] 外单独声明 [plugins] android-application com.android.application:8.2.2 [libraries] okhttp-core { module com.squareup.okhttp3:okhttp, version.ref okhttp } [versions] okhttp 4.12.0 [dependencyConstraints] okhttp-constraint { group com.squareup.okhttp3, name okhttp, version.ref okhttp, strictly true } # ↑ 注意这是 dependencyConstraints不是 libraries提示[dependencyConstraints]是 TOML 中的特殊 section它定义的是全局约束而非具体依赖项。strictly true在此处表示“强制使用该版本拒绝所有其他版本”。3.3prefer软性倾向resolver 的“协商建议”prefer是最软的约束它在版本决议阶段末期Conflict Resolution Phase生效仅当 resolver 发现多个候选版本且无strictly/force干预时才采纳此建议。它不报错、不中断只是影响选择逻辑。// build.gradle —— prefer 只能在 build.gradle 中声明不能写在 toml 里 configurations.all { resolutionStrategy { prefer com.squareup.okhttp3:okhttp:4.12.0 } }效果若图中存在okhttp:4.12.0和okhttp:4.11.0resolver 选4.12.0若只有4.11.0和4.10.0则选4.11.0——prefer不强制存在。3.4 三者执行顺序与冲突优先级关键resolver 的完整流程是Graph Construction收集所有依赖声明 →strictly在此阶段生效锁死单个节点Constraint Application应用dependencyConstraints→force在此阶段生效全局覆盖Conflict Resolution对剩余未锁死的冲突节点按prefer 默认策略最高版本胜出决议因此优先级为strictlyforceprefer。若strictly和force同时存在且版本不同strictly胜出若force和prefer冲突force胜出。4. 避坑5 个血泪经验换来的常见问题排查清单Gradle 依赖管理的黑匣子特性让很多问题表面看是“版本没更新”实则是 resolver 的隐式行为。以下是基于真实翻车场景整理的排查清单每条都附可验证的复现步骤和 root cause。4.1 现象libs.versions.toml修改后./gradlew build无反应旧版本仍在生效原因Gradle 的versionCatalogs缓存机制。settings.gradle中from(files(...))的文件路径被缓存且--refresh-dependencies不刷新 catalog。解决执行./gradlew --stop彻底关闭 daemon再运行./gradlew build或在settings.gradle中改用绝对路径file(/full/path/to/libs.versions.toml)强制重载。4.2 现象implementation libs.okhttp.core编译通过但运行时报NoSuchMethodError原因okhttp-core的version.ref okhttp指向的[versions].okhttp是4.12.0但某个 transitive dependency如retrofit强制依赖okhttp:4.11.0且retrofit的force约束优先级高于libs的strictly。解决运行./gradlew app:dependencies --configuration runtimeClasspath | grep okhttp定位retrofit的 force 来源然后在dependencyConstraints中添加更高优先级的okhttpconstraint。4.3 现象[bundles]中定义networking [okhttp-core, retrofit-core]但implementation libs.bundles.networking只引入了okhttp-core原因retrofit-core在[libraries]中未正确定义或version.ref指向的[versions]key 不存在如写成version.ref retrofit但[versions]中是retrofit-version 2.9.0。解决检查libs.versions.toml中retrofit-core的version.ref是否与[versions]key 完全一致用./gradlew dependencies --configuration compileClasspath验证 bundle 展开结果。4.4 现象Android Studio 的Project Structure→Dependencies面板中libs.okhttp.core显示为unresolved原因IDE 的 Gradle sync 未识别versionCatalogs或settings.gradle中enableFeaturePreview(VERSION_CATALOGS)未放在文件顶部必须在pluginManagement之前。解决确保enableFeaturePreview是settings.gradle的第一行重启 Android Studio 并点击File → Sync Project with Gradle Files。4.5 现象CI 流水线中./gradlew test失败提示kotlin-stdlib:1.8.20被选中但本地libs.versions.toml明确写了kotlin 1.9.20原因CI 使用的 Gradle Wrapper 版本低于 7.4Version Catalog 要求最低 7.4导致libs.versions.toml未被加载fallback 到模块内硬编码的旧版本。解决检查 CI 的gradle/wrapper/gradle-wrapper.properties确认distributionUrl指向gradle-7.4-bin.zip或更高或在 CI 脚本中添加./gradlew --version验证。5. BOMBill of Materials集成为什么platform()和enforcedPlatform()是解决“版本漂移”的终极武器当你的项目依赖数十个 Spring、Micrometer 或 AndroidX 组件时靠人工维护libs.versions.toml中每个version.ref几乎不可能——BOM 就是为此而生。它不是一个“大版本号”而是一份由官方维护的、经过充分兼容性测试的依赖版本矩阵。GradleDependencyManagement 项目深度集成了 BOM但很多人只知其名不知其 resolver 内部如何运作。5.1platform()vsenforcedPlatform()软约束与硬约束的本质区别BOM 本身是pom.xml文件其中dependencyManagement定义了所有组件的推荐版本。Gradle 提供两种导入方式platform(org.springframework.boot:spring-boot-dependencies:3.2.0)软平台仅提供版本建议若模块中显式声明spring-web:6.0.0则6.0.0胜出enforcedPlatform(org.springframework.boot:spring-boot-dependencies:3.2.0)硬平台强制所有 transitive 依赖遵守 BOM 版本显式声明spring-web:6.0.0会被降级为6.1.0BOM 指定值二者在 resolver 中的介入点不同platform()在 Conflict Resolution Phase 提供建议enforcedPlatform()在 Constraint Application Phase 注入force级约束。5.2 在libs.versions.toml中声明 BOM避免硬编码实现可继承的版本基线BOM 应作为versionCatalog的一等公民管理而非写死在build.gradle中# libs.versions.toml [versions] spring-boot 3.2.0 androidx-bom 2023.10.01 [libraries] spring-boot-platform { module org.springframework.boot:spring-boot-dependencies, version.ref spring-boot } androidx-bom { module androidx.compose:compose-bom, version.ref androidx-bom } [dependencyConstraints] spring-boot-enforced { group org.springframework.boot, name spring-boot-dependencies, version.ref spring-boot, enforcedPlatform true # ← 关键声明为 enforcedPlatform }然后在build.gradle中使用dependencies { // 导入 BOM不引入任何实际 jar implementation platform(libs.spring.boot.platform) // 使用 BOM 管理的组件无需指定版本 implementation org.springframework.boot:spring-boot-starter-web implementation org.springframework.boot:spring-boot-starter-data-jpa }注意platform()必须与具体依赖声明在同一dependencies块中且platform()必须在具体依赖之前。顺序错误会导致 BOM 不生效。5.3 BOM 的 resolver 内部机制一张图看懂“版本漂移”如何被拦截当 resolver 处理implementation org.springframework.boot:spring-boot-starter-web时先解析spring-boot-starter-web的pom.xml发现其dependencyManagement中无spring-web版本 → fallback检查当前配置是否导入了platform→ 找到spring-boot-dependencies:3.2.0解析该 BOM 的pom.xml提取dependencyManagement中spring-web的版本如6.1.0若为enforcedPlatform则向图中注入force org.springframework.web:spring-web:6.1.0最终决议时6.1.0成为唯一合法版本任何其他来源的spring-web版本均被拒绝这就是为什么enforcedPlatform能终结“版本漂移”——它把版本决策权从每个模块的build.gradle收归到一份由权威方Spring 团队签名的 BOM 文件。6. 设计取舍为什么 GradleDependencyManagement 不封装apply plugin而坚持手写settings.gradle这个项目最反直觉的设计选择是它拒绝提供任何 Gradle Plugin。你不会看到id com.example.dependency-management这样的插件声明也不会有DependencyManagementExtension配置块。它只提供libs.versions.toml模板、settings.gradle示例和build.gradle使用规范。这并非偷懒而是对 Gradle 构建生命周期深刻理解后的主动取舍。6.1 插件的“时机陷阱”为什么自定义插件无法安全介入versionCatalogsGradle Plugin 的apply()方法执行时机在settings.gradle解析之后、build.gradle执行之前。而versionCatalogs的注册必须在settings.gradle的dependencyResolutionManagement块内完成——这是 Gradle 的硬性要求。若你试图在插件中调用project.settings.dependencyResolutionManagement.versionCatalogs.create(...)会触发IllegalStateException: Cannot access version catalogs before settings evaluation。换句话说插件无法创建 catalog只能消费已存在的 catalog。强行封装只会让用户陷入“插件没生效”的困惑。6.2 “零抽象”原则暴露 resolver 的真实复杂度而非掩盖它很多团队曾尝试封装DependencyManagementPlugin提供dependencyManagement { version kotlin { 1.9.20 } }这样的 DSL。但这种抽象带来两个致命问题隐藏约束优先级DSL 无法清晰表达strictly/force/prefer的层级关系用户误以为“写在哪都一样”破坏 IDE 支持自定义 DSL 无法被 Android Studio 的libs.代码补全识别开发者失去类型安全提示。GradleDependencyManagement 的选择是用最接近 Gradle 原生机制的方式把复杂度摊开。libs.versions.toml是 Gradle 官方格式settings.gradle是官方入口implementation libs.xxx是官方 API。你学到的不是某个项目的私有语法而是 Gradle 本身的设计哲学。6.3 我的验证方法用--scan和--write-locks穿透 resolver 黑匣子要真正信服这套机制我每次落地新项目必做三件事运行./gradlew build --scan打开 Gradle Build Scan进入Dependencies标签页查看libs.okhttp.core的 resolved version 和所有 conflict resolution trace在gradle.properties中添加org.gradle.configuration-cachetrue验证 catalog 是否支持 configuration cache不支持则说明有动态逻辑执行./gradlew :app:resolveMainDependencies --write-locks生成gradle/dependency-locks/compileClasspath.lockfile用 diff 工具对比修改libs.versions.toml前后的 lockfile确认 resolver 是否真的按预期锁定了版本。从那以后我每次重构依赖管理都强制走一遍这三步——不是为了炫技而是因为 resolver 的行为太容易被表象欺骗。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
金仓数据库KCA/KCP认证实操靶场:从真题到可运行环境 简介:本资源是面向金仓数据库KCA初级与KCP进阶认证考生的专项备考资料包,聚焦数据库管理、SQL实战、安装配置及性能调优等核心考点,助力初学者夯实基础、进阶者突破难点。压缩包共2个文件(157KB),含1个Exce… · 2026/11/5 23:02:56
Spark 2.4.0 + Hadoop 3.1.3 环境搭建与路径协议避坑指南 简介:本资源是《大数据技术原理与应用》课程配套的Spark初级编程实践实验报告,面向高校大数据初学者及Hadoop/Spark入门学习者,聚焦分布式计算框架的核心操作训练。内容覆盖Spark环境搭建(Ubuntu虚拟机Hadoop 3.1.3SparkJDK 1.8&a… · 2026/11/3 12:22:34
游戏开发实战:性能优化分类清单与 Unity / Cocos 引擎技巧 1. 为什么性能优化要"分类"来做
在真实项目里,性能问题从来不是"某个函数慢"这么简单。一个掉帧现象背后,可能是 Draw Call 过多、GC 抖动、物理计算爆炸、纹理内存超限、脚本逻辑空转中的任意一种,甚至是它们叠加的结果… · 2026/11/3 0:43:02
EasyU优启通PE维护盘实战指南:制作、启动与核心操作全解析 1. 为什么一个PE维护盘值得单独拿出来聊装系统、修引导、救数据、清病毒,这些活儿但凡干过几次的人都知道,真正决定效率的往往不是你手速多快,而是手边那套PE环境靠不靠谱。EasyU(圈内习惯叫优启通)就是在这个场景下被… · 2026/11/5 16:49:35
Rust原来很简单二:所有权系统 Part 2 核心篇 —— 所有权系统
本篇覆盖 第 6 ~ 10 章:所有权、借用与引用、生命周期、字符串与切片、内存模型。 ⚠️ 这是全书最重要的一篇。 把它吃透,Rust 从此“很简单”;跳过它,你会处处碰壁。请慢下来,动手敲每一段代码。 第 6 章 所有权:Rust 的第一性原理
6… · 2026/11/5 16:26:08
基于Hadoop+Spark的民宿智能推荐系统设计与实现 1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享… · 2026/11/5 14:59:25
ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配RS485,解决远距离温度监控难题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07
多智能体协作系统架构设计与实践优化 1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39
大模型系统性入门实战指南:从原理到部署微调的全链路路线 提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39
鸿蒙文件系统沙箱机制解析与开发实践 1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07