多环境配置这件事几乎每个用 Spring Boot 做项目的团队都会撞上。最早大家都是把所有配置往一个application.yml里塞本地开发、测试环境、预发、生产全混在一起改一行配置心惊胆战提交代码前还得反复确认有没有把本地数据库地址带上去。后来大家开始用application-{profile}.yml拆分情况好了一些但新的问题又冒出来打包的时候到底激活哪个 profile怎么保证打出来的 jar 在目标环境里跑的就是对应那套配置Maven 的 profile 和 Spring 的 profile 名字对不上怎么办这篇文章就围绕“用 Maven 多环境构建来动态启用 Spring Boot 的 Profiles”这条主线把文件拆分、构建参数注入、资源过滤、打包验证这一整套流程拆开讲清楚。适合已经能跑起 Spring Boot 项目、但被多环境配置折腾过的同学也适合刚接手一个配置混乱的老项目、想动手整理的人。下面这些内容都是我在实际项目里反复调整后沉淀下来的做法能直接照着复现。1. 整体设计思路与方案选型拆解1.1 为什么不能只靠 Spring 的 profile 机制先把这个问题的本质说清楚。Spring Boot 的 profile 机制解决的是“运行时加载哪套配置”它通过spring.profiles.active这个属性来决定激活哪些配置文件。但这里有个关键点这个属性本身也得有个地方写。如果写死在application.yml里那打包出来的产物就绑死了某个环境如果靠启动命令--spring.profiles.activeprod传那部署脚本就得记住每个环境该传什么一旦漏传或者传错应用可能带着开发库的配置跑在生产上这种事故我见过不止一次。所以真正要解决的是两层问题第一层是配置文件按环境拆开让每套配置独立可维护第二层是构建阶段就把目标环境确定下来让打出来的包自带正确的 profile 激活信息部署时不需要人工干预。Maven 的 profile 机制正好补上第二层它能在构建时根据传入的参数决定打包行为包括往产物里写入哪个 profile 标识。两层机制各管一段职责清晰这是整个方案能站得住脚的根基。1.2 文件拆分的粒度怎么定拆分配置文件不是越细越好。我见过有人把数据源、缓存、消息队列、日志各拆一个文件最后application.yml里一堆spring.config.import维护成本反而上去了。比较务实的做法是按“环境”这个维度拆也就是application-dev.yml、application-test.yml、application-prod.yml这种每个文件里放该环境下的全部差异配置。公共配置留在application.yml里比如应用名、端口、序列化规则这些不随环境变化的东西。这样拆的好处是看一个文件就能知道某个环境的全貌排查问题时不用在多个文件之间跳。代价是每个环境文件里会有一些重复的键但这个重复是可接受的因为环境之间的差异本来就该显式表达出来而不是靠继承关系去猜。至于更细的维度比如按业务模块拆我建议等配置量真的到了几百行的规模再考虑早期没必要给自己加复杂度。1.3 Maven profile 与 Spring profile 的对应关系这里有个容易混淆的点Maven 的 profile 和 Spring 的 profile 是两套独立的东西名字可以一样也可以不一样但为了可维护性我强烈建议让它们保持一致。Maven profile 的 id 用dev、test、prodSpring 的 profile 也用同样的名字这样从构建命令到运行时日志整个链路上的标识是统一的排查问题时不会出现“Maven 里叫 production、Spring 里叫 prod”这种对不上的情况。对应关系的实现方式是在 Maven profile 里定义一个属性比如spring.profiles.active然后在资源过滤时把这个属性值写进配置文件。这样构建时-P prod就会让产物里的激活标识变成prod。整个链路是Maven 参数决定 Maven profileMaven profile 决定属性值属性值经过资源过滤写进配置文件Spring 启动时读取配置文件里的标识。每一环都可追踪出问题能快速定位是哪一环没对上。1.4 方案对比资源过滤 vs 多套配置文件目录实现动态启用还有另一种常见做法为每个环境准备一套完整的配置目录构建时通过 Maven 的资源插件把对应目录复制到target/classes。这种做法的优点是环境之间完全隔离缺点是公共配置要复制多份改一个公共项得改好几个地方容易漏。资源过滤的做法是只维护一份application.yml模板里面用占位符表示会变化的部分构建时替换。它的优点是公共配置只有一份缺点是占位符语法如果和配置文件本身的语法冲突比如 YAML 里的${}有时会被 Spring 自己解析需要小心处理。我最终选的是资源过滤因为配置项重复带来的维护成本在长期看更高而占位符冲突的问题可以通过约定命名规则来规避比如统一用...这种 Maven 默认的过滤分隔符避免和 Spring 的${}撞车。2. 核心配置细节与实操要点2.1 目录结构怎么摆先看落地后的目录结构这是后面所有操作的基础。标准做法是把环境配置文件放在src/main/resources下和主配置文件平级src/main/resources/ ├── application.yml ├── application-dev.yml ├── application-test.yml └── application-prod.ymlapplication.yml里只放公共配置和 profile 激活的占位符各环境文件放差异配置。这里有个细节要注意application.yml里的spring.profiles.active用占位符写构建时被替换而各环境文件里不要再写spring.profiles.active否则会出现自己激活自己的混乱情况。我见过有人在application-prod.yml里又写了一遍spring.profiles.active: prod虽然多数情况下不报错但语义上是冗余的容易让人误以为这里才是激活入口。2.2 主配置文件里的占位符写法application.yml的关键片段大概是这样spring: application: name: demo-service profiles: active: spring.profiles.active这里的spring.profiles.active就是 Maven 资源过滤的占位符。用...而不是${...}的原因是Spring 自己在解析 YAML 时也会处理${}两者混在一起容易出现“到底是谁替换的”这种困惑。Maven 的资源过滤默认支持...分隔符较新版本的 maven-resources-plugin 默认开启用它可以和 Spring 的占位符彻底分开减少踩坑。需要确认的是pom.xml里资源过滤是否开启。默认情况下src/main/resources的资源过滤是关闭的必须显式配置build resources resource directorysrc/main/resources/directory filteringtrue/filtering /resource /resources /build注意开启过滤后src/main/resources下所有文件里的...都会被替换。如果项目里有二进制文件或者不该被过滤的模板文件要用excludes排除否则可能把文件内容改坏。2.3 Maven profile 的定义方式在pom.xml里定义三个 profile每个负责设定spring.profiles.active的值profiles profile iddev/id properties spring.profiles.activedev/spring.profiles.active /properties activation activeByDefaulttrue/activeByDefault /activation /profile profile idtest/id properties spring.profiles.activetest/spring.profiles.active /properties /profile profile idprod/id properties spring.profiles.activeprod/spring.profiles.active /properties /profile /profilesdev设为默认激活这样本地直接mvn package不用带参数也能跑符合开发时的习惯。test和prod必须显式指定避免误打包。这里属性名用spring.profiles.active是刻意的它和 Spring 的属性名一致读配置的人一眼能明白这个属性最终流向哪里。2.4 环境文件里的差异配置示例以数据源为例application-dev.yml和application-prod.yml的差异大概是这样# application-dev.yml spring: datasource: url: jdbc:mysql://127.0.0.1:3306/demo_dev?useUnicodetrue username: dev_user password: dev_pass logging: level: root: debug# application-prod.yml spring: datasource: url: jdbc:mysql://db-host:3306/demo_prod?useUnicodetrue username: prod_user password: ${DB_PASSWORD} logging: level: root: info生产环境的密码用环境变量占位不写死在文件里这是基本的安全习惯。开发环境可以写明文方便调试但也要注意别把真实的生产凭据提交到代码库。这种差异配置的写法让每个环境的全貌一目了然改配置时目标明确。2.5 资源过滤的排除规则前面提到要排除不该过滤的文件。实际项目里常见需要排除的有字体文件、图片、证书、以及一些第三方库要求的二进制资源。配置方式resource directorysrc/main/resources/directory filteringtrue/filtering excludes exclude**/*.png/exclude exclude**/*.jpg/exclude exclude**/*.ttf/exclude exclude**/*.p12/exclude /excludes /resource这个排除列表要根据项目实际情况维护。我踩过的坑是漏排了一个.p12证书文件过滤后文件损坏启动时报证书解析失败排查了半天才定位到是资源过滤干的。所以凡是二进制文件一律排除宁可多排不要漏排。3. 完整构建流程与验证方法3.1 构建命令与参数传递日常构建分三种场景命令各不相同# 本地开发走默认 dev profile mvn clean package # 打测试环境包 mvn clean package -P test # 打生产环境包 mvn clean package -P prod-P后面跟的是 Maven profile 的 id。这里要注意如果同时激活多个 profile后定义的属性会覆盖先定义的容易出意外所以构建脚本里应该明确只传一个。CI 流水线里建议把 profile 作为必填参数不传就报错退出避免默认值把生产包打成开发包。3.2 验证产物里的 profile 是否正确打包完成后别急着部署先验证产物里的配置对不对。最直接的办法是解压 jar 看application.ymlunzip -p target/demo-service.jar BOOT-INF/classes/application.yml | grep -A2 profiles期望看到active: prod这样的输出。如果看到的是占位符原文spring.profiles.active说明资源过滤没生效回去检查pom.xml里的filteringtrue/filtering是否配置在正确的 resource 上。如果看到的是dev说明-P prod没传对或者 profile id 拼错了。这个验证步骤看起来简单但能拦住大部分构建配置问题。我习惯把它写进 CI 的构建后检查里用脚本自动比对不通过就中断流水线避免带错配置的包流到部署环节。3.3 启动时的日志确认应用启动后日志里会打印当前激活的 profile类似The following 1 profile is active: prod。这是运行时的最终确认。如果这里显示的和预期不符说明配置文件里的激活标识没写对或者启动命令里又用--spring.profiles.active覆盖了。后者是常见的“双重指定”问题构建时写进了配置文件部署脚本里又传了启动参数两者不一致时启动参数优先级更高结果就是构建时的努力白费。所以部署脚本里不要再传 profile 参数让产物自己决定。3.4 多模块项目的处理如果项目是多模块的资源过滤的配置要放在需要过滤的那个模块的pom.xml里而不是父 pom。父 pom 里定义 profiles 和属性子模块里配置资源过滤这样职责清晰。我见过把资源过滤配在父 pom 的build里结果所有子模块都被过滤某个子模块的模板文件被改坏的情况。多模块场景下只给真正需要写 profile 标识的模块开启过滤其他模块保持默认。3.5 打包产物的命名区分不同环境的包最好在文件名上就能区分避免部署时拿错。可以在pom.xml的finalName里带上 profile 信息build finalName${project.artifactId}-${spring.profiles.active}/finalName /build这样打出来的包叫demo-service-prod.jar一眼能看出是哪个环境的。这个小改动在多人协作、多环境并行部署时特别有用能省掉不少“这个包是哪个环境的”这种确认成本。4. 常见问题排查与避坑经验4.1 占位符没被替换的几种原因这是最高频的问题。表现是产物里的application.yml还带着spring.profiles.active原文。原因通常有三个一是资源过滤没开启检查filteringtrue/filtering二是占位符分隔符不匹配如果项目用的老版本插件只认${}那...就不会被替换需要确认插件版本或者改用${}并处理好与 Spring 的冲突三是文件不在被过滤的 resource 目录下比如放在了src/main/resources/config/子目录但 resource 配置只覆盖了根目录。逐个排查基本能定位。4.2 profile 激活了但配置没生效有时候日志显示激活了prod但连的还是开发库。这种情况多半是环境文件名和 profile 名对不上比如 profile 叫prod文件却叫application-production.ymlSpring 找不到对应文件就只用主配置。命名必须严格一致application-{profile}.yml里的{profile}就是激活的标识。另一个可能是环境文件放在了错误的目录比如放到了src/main/resources/config/下而 Spring Boot 默认的搜索路径虽然包含config子目录但优先级和预期可能不同建议统一放在resources根下。4.3 敏感配置泄露的风险资源过滤会把属性值写进产物如果属性值里包含密码那密码就明文躺在 jar 里了。所以生产环境的敏感项一律用环境变量占位比如${DB_PASSWORD}构建时不替换运行时由部署环境注入。这里要注意${DB_PASSWORD}这种写法在 YAML 里会被 Spring 解析属于运行时占位和 Maven 的构建时占位是两回事不要混淆。构建时占位用...运行时占位用${...}两者分工明确。4.4 常见问题速查表现象可能原因排查方向产物里是占位符原文资源过滤未开启或分隔符不匹配检查 filtering 配置和插件版本激活了 profile 但配置没生效文件名与 profile 名不一致核对application-{profile}.yml命名启动参数覆盖了构建配置部署脚本传了--spring.profiles.active移除启动参数让产物决定二进制文件损坏资源过滤误伤了非文本文件在 excludes 里补充排除规则多模块下部分模块配置异常过滤配置放在了父 pom移到需要过滤的子模块4.5 实操心得先小步验证再全量铺开这套方案涉及 Maven 和 Spring 两层机制一次性全改容易出问题。我的建议是先在一个模块或者一个简单项目上跑通完整链路拆分文件、配好 profile、打包、解压验证、启动确认。链路通了再往多模块、往老项目上迁移。迁移时优先处理数据源和日志这两类配置因为它们最容易出环境差异问题也最容易验证效果。等这两类稳定了再逐步把其他配置项挪进环境文件。另外一个小技巧在application.yml里保留一份“兜底”的公共配置环境文件只覆盖差异项。这样即使某个环境文件漏了某个键应用也能用兜底值启动不至于直接起不来。当然兜底值要选安全的默认值比如指向本地地址而不是生产地址避免漏配时误连。5. 与 CI 流水线的衔接要点5.1 把 profile 作为流水线参数在持续集成环境里构建任务应该把 profile 作为显式参数暴露出来由触发构建的人或者上游任务指定。参数校验要做只允许dev、test、prod这几个白名单值传其他值直接失败。这样能防止手误传错导致打错包。构建命令里-P后面跟的就是这个参数整个链路从流水线参数到 Maven profile 到产物配置一气呵成。5.2 构建后自动校验前面提到的解压验证可以脚本化放在构建步骤之后、制品归档之前。脚本逻辑是解压产物里的application.yml提取spring.profiles.active的值和传入的参数比对不一致就退出非零码让流水线失败。这个检查成本很低但能拦住绝大多数配置错误值得每个项目都加上。5.3 制品命名与归档制品归档时文件名带上 profile 标识配合前面说的finalName配置归档后的包名自带环境信息。这样在制品仓库里一眼能区分不同环境的包回滚时也不会拿错。如果制品仓库支持元数据还可以把 profile 作为元数据字段存进去方便检索。5.4 部署环节的配合部署脚本里不要再传--spring.profiles.active让产物自己决定。如果部署平台强制要求传启动参数那要确保传的值和产物里的一致并且以产物为准做校验。最稳妥的做法是部署前先读产物里的 profile 值再决定部署到哪个环境形成闭环。这样即使有人手动改了包部署环节也能发现不一致并拦截。6. 配置演进与长期维护建议6.1 配置项该放哪一层随着项目演进配置项会越来越多这时候要有明确的归属规则。我的规则是不随环境变化的放application.yml随环境变化的放环境文件敏感信息用环境变量。判断标准很简单问一句“这个值在测试环境和生产环境一样吗”答案是否就放环境文件。这条规则能覆盖绝大多数情况减少“这个配置该放哪”的纠结。6.2 环境文件的评审机制环境文件里的改动应该走代码评审尤其是生产环境文件。因为一个配置错误可能直接导致线上故障评审时重点关注数据源地址、超时时间、开关类配置。我见过把限流开关误改成关闭导致线上被打挂的案例评审能拦住这类低级错误。评审时还可以对比不同环境文件的差异确认差异都是有意为之。6.3 定期清理无用配置项目跑久了环境文件里会积累一些已经废弃的配置项比如已经下线的功能对应的开关。这些无用配置不仅增加维护负担还可能误导后来的人。建议每个季度做一次配置清理对照代码里的实际使用情况把没人读的键删掉。清理前先在测试环境验证确认删除不影响启动和功能。6.4 新成员的上手引导这套机制对新人来说有一定理解成本尤其是 Maven profile 和 Spring profile 两层的关系。建议在项目文档里写清楚构建命令怎么传参、产物怎么验证、配置该放哪一层。文档不用长把关键链路画清楚就行。新人第一次打包时带着走一遍完整流程比看文档更有效。7. 几个容易被忽略的细节7.1 IDEA 里的运行配置在本地用 IDEA 跑应用时IDEA 会用自己的方式处理资源可能不经过 Maven 的资源过滤。这时候application.yml里的spring.profiles.active不会被替换启动会报错或者激活失败。解决办法是在 IDEA 的运行配置里把spring.profiles.active作为环境变量或者 VM 参数传进去绕开占位符。或者干脆在本地开发时用一个不带占位符的配置打包时才用模板。这个细节很多人第一次搭这套机制时会卡住提前知道能省不少时间。7.2 测试代码里的配置加载单元测试和集成测试加载配置的方式和生产不同如果测试里依赖了application.yml的占位符可能因为没经过 Maven 过滤而失败。测试场景建议用ActiveProfiles注解显式指定 profile或者用测试专用的配置文件不要依赖构建时的占位符替换。测试配置和构建配置分开管理互不干扰。7.3 配置文件的编码问题资源过滤时如果文件编码和插件配置的编码不一致中文注释可能变成乱码。确保pom.xml里配置了project.build.sourceEncodingUTF-8/project.build.sourceEncoding并且资源插件的编码也设为 UTF-8。这个坑在配置文件里有中文注释时才会暴露容易被忽略但一旦出现排查起来很费劲。7.4 版本升级时的兼容性Maven 资源过滤的默认分隔符在不同版本的插件里有差异老版本默认只认${}新版本才默认支持...。升级构建插件版本时要注意这个变化升级后跑一遍完整构建验证产物。如果项目里同时有${}和...两种占位符升级后可能出现意外的替换行为升级前先梳理清楚占位符的使用情况。这套机制搭好之后日常开发基本感觉不到它的存在打包传个参数就行配置各归各位部署时不用再操心激活哪个 profile。真正体现价值的时候是出问题的时候配置错了能快速定位是哪一层的问题产物验证能拦住带错配置的包评审能拦住危险的改动。我在几个项目里推行这套做法后因为配置问题导致的部署事故基本归零这大概就是前期多花点时间把机制理顺的回报。后续如果配置规模继续膨胀可以考虑引入配置中心把环境差异从文件里挪到中心化管理但那是另一个话题了在配置量还没到那个程度之前Maven 加 Spring 的这套组合足够用也足够稳。
企业数字化 ERP 产品动态
相关推荐
UG NX 10.0二次开发:图层分类标签获取技术详解 1. UG NX 10.0二次开发中的图层分类标签获取技术解析 在机械设计领域,UG NX(原Unigraphics)作为主流的三维CAD/CAM/CAE软件,其二次开发能力为工程师提供了强大的定制化工具。今天要深入探讨的是UG NX 10.0二次开发中一个关键API函… · 2026/11/3 11:40:20
RabbitMQ路由模式与死信队列实战解析 1. 项目概述:RabbitMQ路由模式与死信队列的黄金组合 RabbitMQ作为企业级消息中间件的代表,其路由模式和死信队列的组合堪称分布式系统中的"保险丝导航仪"机制。我在电商秒杀系统的实战中发现,这种组合能同时解决消息精准投递和异常… · 2026/11/3 11:39:19
叶面温度监测技术原理与农业应用实践 1. 叶面温度监测的核心价值与应用场景 植物叶面温度是反映其生理状态的关键指标之一。与空气温度不同,叶片温度直接体现植物与环境的热交换结果,包含蒸腾作用、光合效率等关键生理信息。传统农业中,我们往往通过观察叶片萎蔫程度来判断水分状… · 2026/11/3 11:39:19
大学生论文写作模型选型指南:避坑策略与WorkBuddy配置方案 大学生论文写作的模型选型,我踩过的坑和最终方案 每到论文季,后台总有人问我同一个问题:用 WorkBuddy 这类写作辅助工具写课程论文、学术文章,到底该挂哪个模型?问的人多了,我发现大家真正纠结的不是"… · 2026/11/3 14:44:47
云原生监控实战:Prometheus+Grafana高效部署与优化 1. 项目概述:云原生监控的黄金搭档 在容器化和微服务架构盛行的今天,传统的监控手段已经难以应对动态变化的云原生环境。这正是PrometheusGrafana组合大显身手的场景——前者负责多维度指标采集,后者专注可视化呈现,两者结合形成了… · 2026/11/3 14:44:47
Hindsight实战:用Python解析Firefox历史记录并生成可视化报告 1. 项目概述:"hindsight"到底在解决什么问题 如果你在技术社区搜索 hindsight 这个词,大概率会碰到两条线索:一条是 Google 的强化学习团队提出的 Hindsight Experience Replay,解决稀疏奖励问题;另一条是 M… · 2026/11/3 14:44:47
Redis缓存击穿解决方案与Redisson分布式锁实践 1. 项目背景与问题定义在微服务架构中,缓存是提升系统性能的关键组件。Redis作为主流的内存数据库,常被用作缓存中间件。但实际应用中,缓存击穿问题就像一把悬在头顶的达摩克利斯之剑——当热点key过期瞬间遭遇高并发请求,数据库可… · 2026/11/3 14:44:47
FUXA开源组态软件:工业自动化SCADA与HMI开发指南 1. FUXA组态软件概述FUXA是一款基于Web的开源组态软件,主要用于工业自动化领域的SCADA(监控与数据采集)、HMI(人机界面)和可视化看板开发。作为一款轻量级解决方案,它允许工程师快速构建现代化的工业数据可… · 2026/11/3 14:44:47
后见之明偏差如何毁掉复盘?用时间线与决策记录破解 “hindsight is 20/20”,这句英文俗语我读第一遍的时候只觉得是在说“事后看一切清楚”。后来干了十几年研发,参与过几十场大大小小的复盘会,才真正理解“事后”这两个字的杀伤力。每当线上事故复盘,总有人拍桌子说“这个配置错误… · 2026/11/3 14:43:44
双极步进电机控制实战: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/2 13:10:28
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/3 6:04:24
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/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战: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/2 13:10:28
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/3 6:04:24
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/3 5:52:46