VS2015 编译 gRPC 1.28 C++ 库实战指南
简介本资源为 gRPC 1.28 在 Windows 平台配合 Visual Studio 2015 的 C 编译库面向需要在 Windows 下开发跨语言微服务通信的 C 工程师与学习者。它解决了在旧版 VS 环境中搭建 gRPC 与 protobuf 3.11 开发环境的繁琐问题省去自行编译依赖的步骤。压缩包共 472 个文件约 24.47MB以 387 个 h 头文件、34 个 lib 静态库、14 个 cmake 构建脚本、12 个 proto 接口定义及 11 个 exe 工具为主另含少量 dll、pc 与 pem 文件覆盖头文件引用、库链接、CMake 集成与证书配置等环节。已有 998 人学习下载。借助其中的库文件与构建脚本读者可快速完成服务端 ServerBuilder 监听、客户端 Channel 与 Stub 调用并理解 protoc 将 .proto 编译为 C 代码的完整流程适合作为分布式系统入门与工程集成的参考。1. gRPC 1.28 在 Windows 上用 VS2015 编译 C 库一条被低估的产线级路径如果你手上维护的是 VS2015 时代的 C 工程又需要把服务间通信从自研 socket 换成 gRPC那 gRPC 1.28 这个版本大概率是你绕不开的节点。原因很直接再往后的版本对 MSVC 工具集的要求逐步抬高VS2015v140能干净编过的窗口期并不宽而 1.28 恰好落在一个相对稳定的区间里。它解决的不是“能不能用 gRPC”的问题而是“在老工具链、老 Windows SDK、不能随便升级编译器的产线环境里怎么把 gRPC 的 C 静态库和动态库编出来、并且能被自己的工程链接进去”。这篇内容面向的是需要在 Windows 上落地 gRPC C 的工程师尤其是被 VS2015 锁死、又不想改整个构建体系的那批人。下面按“先立住原理和选型再动手复现最后讲坑”的顺序推。2. 为什么是 1.28 加 VS2015版本、工具链与依赖的三方博弈2.1 gRPC C 在 Windows 上的构建底座gRPC 的 C 实现并不是一个孤立的库它在 Windows 上依赖一整套底层组件Protocol Buffers、zlib、OpenSSL或 BoringSSL、c-ares、abseil1.28 这个阶段还没完全切到 abseil 的强绑定但部分模块已经引入。这些依赖的版本和编译方式直接决定了你能不能编过。gRPC 官方在 Windows 上主推的是 CMake 构建配合 Visual Studio 生成器。VS2015 对应的是 CMake 里的Visual Studio 14 2015 Win64生成器工具集是 v140。这里第一个容易翻车的点很多人拿 VS2017/2019 的生成器去编结果在 protobuf 的某些模板代码上直接报 C 标准不匹配。VS2015 默认只到 C14 的部分特性而 gRPC 1.28 的代码里已经用了不少 C11/14 的写法所以编译选项必须显式打开。我一般会先确认三件事CMake 版本不低于 3.131.28 的 CMakeLists 里用了target_link_libraries的新签名NASM 已安装OpenSSL 在 Windows 上编译汇编部分需要它以及 Perl 可用OpenSSL 的配置脚本依赖。这三样缺一个后面就会在某个依赖的 configure 阶段卡住而且报错信息往往不指向根因。2.2 选 1.28 而不是更高版本的实际理由从工程角度看选 1.28 通常不是因为它“新”而是因为它“够用且可控”。更高的版本会引入更严格的编译器要求比如对 MSVC 2017 以上工具集的硬依赖或者对 C17 的强制使用。VS2015 的 v140 工具集在这些要求面前基本没有还手之力。另一个现实因素是第三方依赖的版本漂移gRPC 1.28 对应的 protobuf 是 3.7.x 到 3.8.x 区间这个区间的 protobuf 在 VS2015 下的编译问题相对少社区里能查到的补丁也集中。再往上protobuf 自身就开始要求更高的 C 标准连锁反应会把你拖进一个无底洞。还有一点常被忽略gRPC 1.28 的 CMake 脚本对 Windows 静态库的支持相对完整。如果你要做的是把 gRPC 编成静态库再嵌进自己的 DLL 或 EXE这个版本的gRPC_MSVC_STATIC_RUNTIME和gRPC_BUILD_CSHARP_EXT之类的开关行为比较可预期。更高版本里这些开关的默认值和交互逻辑改过几轮老工程迁移时容易踩到链接期的一堆符号缺失。2.3 依赖清单与版本对应关系下面这张表是我在多次编译后整理出来的、在 VS2015 下相对稳的组合。注意版本号不是随便写的每个都对应到 gRPC 1.28 的third_party子模块或官方推荐区间。组件建议版本作用VS2015 下的注意点CMake3.13 到 3.16生成 VS 工程高于 3.16 可能引入新策略老工程不认NASM2.13 以上编译 OpenSSL 汇编必须加入 PATH否则 OpenSSL 配置失败Perl5.26 以上OpenSSL 配置脚本Strawberry Perl 即可注意路径无空格OpenSSL1.1.1 系列TLS 支持用no-asm可绕过 NASM但性能下降zlib1.2.11压缩直接用 gRPC 子模块里的版本最稳protobuf3.7.x 到 3.8.x序列化不要混用系统里已装的 protocc-ares1.15 左右异步 DNS1.28 子模块自带一般不用单独编这张表的核心逻辑是能用的子模块就用 gRPC 自带的不要自己去外面找一个“更新”的版本替换。替换带来的版本错配在链接期会以各种未定义符号的形式爆发排查成本极高。3. 从零把 gRPC 1.28 编成 VS2015 可链接的库3.1 源码获取与目录准备第一步是把 gRPC 1.28 的源码和它的子模块一起拉下来。注意子模块必须递归拉取否则third_party下面全是空目录CMake 配置阶段就会报找不到 protobuf。# 假设工作目录为 D:\grpc_build路径不要有空格和中文 cd /d D:\grpc_build git clone --branch v1.28.0 https://github.com/grpc/grpc.git grpc cd grpc # 递归拉取子模块这一步网络不稳会断断了就重跑 git submodule update --init --recursive逻辑说明--branch v1.28.0锁定标签避免拉到主干上不兼容的提交。--recursive是必须的gRPC 的third_party里嵌套了 protobuf、zlib、c-ares 等多个仓库。如果中途断掉重新执行git submodule update --init --recursive即可git 会续传。参数上唯一要注意的是路径Windows 下路径带空格会让后续 CMake 和 Perl 脚本解析出错所以工作目录尽量用纯英文无空格路径。3.2 CMake 配置生成 VS2015 工程的关键参数配置阶段是整个流程里参数最密集的一步。下面这条命令是我在 VS2015 环境下反复用过的组合生成的是 x64 的静态库工程。cd /d D:\grpc_build\grpc mkdir .build cd .build cmake -G Visual Studio 14 2015 Win64 ^ -DCMAKE_BUILD_TYPERelease ^ -DgRPC_BUILD_TESTSOFF ^ -DgRPC_BUILD_CSHARP_EXTOFF ^ -DgRPC_INSTALLON ^ -DCMAKE_INSTALL_PREFIXD:/grpc_build/install ^ -DgRPC_MSVC_STATIC_RUNTIMEON ^ -DgRPC_SSL_PROVIDERpackage ^ -DOPENSSL_ROOT_DIRD:/grpc_build/openssl ^ -DOPENSSL_LIBRARIESD:/grpc_build/openssl/lib ^ -DOPENSSL_INCLUDE_DIRD:/grpc_build/openssl/include ^ ..逻辑说明-G指定 VS2015 的 64 位生成器这是和 v140 工具集绑定的。gRPC_BUILD_TESTSOFF能砍掉大量测试目标的编译省时间也省磁盘。gRPC_MSVC_STATIC_RUNTIMEON表示用静态 CRT/MT如果你的主工程用 /MD这里要改成 OFF否则链接期会报 CRT 冲突。gRPC_SSL_PROVIDERpackage表示用外部已编好的 OpenSSL而不是让 gRPC 自己去编 BoringSSL这样能避开 BoringSSL 在 VS2015 下的一些汇编兼容问题。参数上最容易出错的是 OpenSSL 的三个路径。OPENSSL_ROOT_DIR指向你预先编好的 OpenSSL 安装目录OPENSSL_LIBRARIES指向 lib 子目录OPENSSL_INCLUDE_DIR指向 include。如果 OpenSSL 没编好这一步 CMake 会直接报找不到ssl.h或crypto.lib。3.3 编译与安装用 MSBuild 而不是 IDE配置成功后.build目录下会生成grpc.sln。不要用 VS 打开去点“生成解决方案”那样容易因为并行项目太多而内存爆掉。用 MSBuild 命令行控制并行度。# 找到 MSBuildVS2015 默认在以下路径 set MSBUILDC:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe %MSBUILD% grpc.sln /p:ConfigurationRelease /p:Platformx64 /m:4 /v:m # 编译完成后执行安装把头文件和库拷到 install 目录 %MSBUILD% INSTALL.vcxproj /p:ConfigurationRelease /p:Platformx64逻辑说明/m:4表示最多 4 个并行项目这个数字根据机器内存调整16G 内存建议不超过 4否则容易在链接阶段 OOM。/v:m是最小化日志输出避免刷屏。安装目标INSTALL.vcxproj会把grpc.lib、grpc.lib、protobuf.lib等以及对应的头文件拷到CMAKE_INSTALL_PREFIX指定的目录。编译过程中如果卡在某个项目上超过十分钟大概率是 protobuf 的代码生成阶段在跑属正常。如果报错优先看第一个报错的项目后面的错误往往是连锁反应。3.4 验证产物写一个最小 C 工程链接测试编完之后不要假设万事大吉写一个最小工程验证链接和运行。下面是一个只创建 channel 的测试代码。// test_grpc_link.cpp #include grpcpp/grpcpp.h #include iostream int main() { // 创建一个不连接的 channel只验证库能否被链接和初始化 auto channel grpc::CreateChannel(localhost:50051, grpc::InsecureChannelCredentials()); if (channel) { std::cout gRPC channel created, link OK std::endl; } return 0; }逻辑说明这段代码不发起真实 RPC只调用CreateChannel目的是验证grpc库能被正确链接且运行时能找到依赖的 DLL如果编的是动态库。编译时在 VS2015 工程里需要把install/include加入附加包含目录把install/lib加入附加库目录并链接grpc.lib、grpc.lib、libprotobuf.lib、zlib.lib、libssl.lib、libcrypto.lib等。如果链接报LNK2019先检查是不是漏了ws2_32.lib和crypt32.lib这两个是 Windows 网络和证书相关的系统库gRPC 在 Windows 上必须链接。4. 避坑与排查VS2015 编 gRPC 1.28 最常见的五类翻车4.1 报错 C2872 或 C2039C 标准没打开现象编译 protobuf 或 gRPC 源码时报某个类型“不明确”或某个成员“不是成员”位置集中在模板和std::相关代码。原因VS2015 默认的 C 语言标准不启用 C14 的某些特性而 gRPC 1.28 的代码里用了变参模板、std::make_unique等。解决在 CMake 配置时追加-DCMAKE_CXX_FLAGS/std:c14或者在生成的 VS 工程里把所有项目的“C/C - 语言 - C 语言标准”改为“ISO C14 标准”。注意 VS2015 对 C14 的支持是部分的如果遇到constexpr相关的报错只能手动改源码或找社区补丁。4.2 链接期 LNK2038CRT 运行库不匹配现象链接自己的工程时报LNK2038: 检测到“RuntimeLibrary”的不匹配项值在MT_StaticRelease和MD_DynamicRelease之间冲突。原因gRPC 编译时用了静态 CRTgRPC_MSVC_STATIC_RUNTIMEON而你的主工程用的是动态 CRT/MD或者反过来。解决统一两边。如果主工程是 /MD重新配置 gRPC 时把gRPC_MSVC_STATIC_RUNTIME设为 OFF重新编译。如果主工程是 /MT保持 ON。不要试图用ignore指令绕过运行期会出现堆跨模块释放的崩溃。4.3 OpenSSL 配置失败找不到 nasm 或 perl现象CMake 配置阶段报Could not find NASM或Perl not found导致 OpenSSL 相关目标无法生成。原因OpenSSL 的 Windows 构建脚本依赖 NASM 编译汇编优化部分依赖 Perl 解析配置。解决安装 NASM 和 Strawberry Perl并把它们的可执行文件目录加入系统 PATH。如果实在不想装 NASM可以在配置 OpenSSL 时加no-asm参数但 TLS 性能会下降产线环境不建议。另一个办法是直接用预编译的 OpenSSL 1.1.1 库跳过自己编译这一步。4.4 编译到一半内存耗尽或 MSBuild 崩溃现象MSBuild 在编译某个大项目通常是grpc或grpc时进程消失或者报out of memory。原因并行编译的项目太多每个 MSVC 编译器实例都吃内存16G 内存开 8 个并行很容易爆。解决把/m后面的数字降到 2 或 3或者分步编译先单独编libprotobuf再编grpc最后编grpc。用%MSBUILD% grpc.sln /t:项目名可以只编指定项目。4.5 运行期报缺少 DLL 或 0xc000007b现象编译出的 exe 在别的机器上跑报找不到grpc.dll或直接0xc000007b架构不匹配。原因编的是动态库但运行时没把 DLL 放到 exe 同目录或系统 PATH或者 exe 是 32 位而 DLL 是 64 位。解决用 Dependency Walker 或dumpbin /dependents检查依赖。产线环境建议直接编静态库gRPC_MSVC_STATIC_RUNTIMEON配合静态链接把依赖降到最低。如果必须用动态库把所有依赖 DLL 和 exe 放同一目录不要依赖系统 PATH。5. 进阶把 gRPC 1.28 集成进现有 VS2015 工程的三个具体技巧5.1 用属性表.props统一管理包含目录和库目录在 VS2015 里手动给每个项目加包含目录和库目录是体力活也容易漏。更稳的做法是建一个grpc.props属性表把install/include、install/lib、以及需要链接的库名都写进去然后在所有需要 gRPC 的项目里导入这个属性表。属性表里用$(GRPC_ROOT)这样的用户宏方便换机器时只改一处。具体做法在 VS 的“属性管理器”里右键项目 - 添加现有属性表或者直接编辑.props文件把AdditionalIncludeDirectories和AdditionalDependencies写进去。这样新加项目时导入一次即可不用重复配置。5.2 处理 protoc 代码生成把 .proto 编译集成到构建流程gRPC 工程里.proto文件需要先用protoc生成.pb.cc和.grpc.pb.cc再参与编译。手动生成容易忘建议在 VS 工程里加自定义生成步骤。在.proto文件的属性里把“自定义生成工具”的命令行设为D:\grpc_build\install\bin\protoc.exe --grpc_out. --pluginprotoc-gen-grpcD:\grpc_build\install\bin\grpc_cpp_plugin.exe -I. %(Filename).proto D:\grpc_build\install\bin\protoc.exe --cpp_out. -I. %(Filename).proto输出文件设为%(Filename).pb.cc和%(Filename).grpc.pb.cc并把它们加入工程编译。注意grpc_cpp_plugin.exe必须和protoc.exe版本匹配混用不同版本的插件会生成不兼容的代码。5.3 用静态库加 /WHOLEARCHIVE 解决符号被裁剪如果 gRPC 编的是静态库链接时 MSVC 默认只拉取被引用到的目标文件而 gRPC 里有些注册机制比如服务注册依赖静态初始化可能被裁掉导致运行期找不到服务。解决办法是在链接器选项里加/WHOLEARCHIVE:grpc.lib和/WHOLEARCHIVE:grpc.lib强制把整个库的目标文件都链进来。代价是 exe 体积变大但能避免“编译链接都过、运行期服务找不到”这种玄学问题。这个技巧在产线环境里救过我好几次尤其是服务端程序。最后说一个我自己的习惯每次编完 gRPC我都会把install目录整个打包备份连同 CMake 的配置命令一起写进一个build_notes.txt。因为 VS2015 加 gRPC 1.28 这套组合隔半年再回来编环境变量和依赖版本很容易记混有备份和命令记录能省掉重新踩坑的时间。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Node.js+Vue构建医疗体检管理系统实践

Node.js+Vue构建医疗体检管理系统实践

1. 项目概述 健康医疗体检管理系统是现代医疗机构数字化转型的重要组成部分。这个基于Node.jsVue框架构建的系统,专为体检中心、医院健康管理中心等场景设计,实现了从预约登记到报告生成的全流程数字化管理。 我在医疗信息化领域工作多年,参…

2026/10/14 13:15:16 阅读更多 →
HVDC输电系统在线监测控制技术解析与应用

HVDC输电系统在线监测控制技术解析与应用

1. 高压直流输电系统在线监测控制系统概述在电力传输领域,高压直流(HVDC)输电技术因其远距离大容量输电优势而日益重要。作为HVDC系统的"健康管家",在线监测控制系统通过实时数据采集与分析,确保这条电力&qu…

2026/10/14 13:14:16 阅读更多 →
用Excel搭建LPR浮动+提前还款的房贷计算器模型

用Excel搭建LPR浮动+提前还款的房贷计算器模型

不知道你有没有遇到过这种情况:在手机上用某个房贷计算器App算好了月供,过了两年利率一变,或者你提前还了一笔钱,之前的数字就全部作废了。而且大多数App不支持“利率已经浮动过”加上“中间提前还款”的组合测算,你只…

2026/10/14 13:14:16 阅读更多 →

最新新闻

Claude账号防封实战指南:从环境搭建到使用节奏的全面解析

Claude账号防封实战指南:从环境搭建到使用节奏的全面解析

1. 从“防封”这个词说起:账号稳定性的真实含义很多人第一次看到“防封”这两个字,脑子里浮现的画面是某种神秘操作或者隐藏开关。但如果你真正在长期使用各类AI工具,尤其是像Claude这种对账号行为比较敏感的服务,就会明白所谓的“…

2026/10/14 22:30:56 阅读更多 →
最简单的出入库系统怎么选?仓库管理软件选型避坑指南

最简单的出入库系统怎么选?仓库管理软件选型避坑指南

仓库管理软件哪个好用?这恐怕是我被问次数最多的问题,没有之一。每次听到这个问题,我都会先反问一句:你说的“好用”,到底是想要什么效果?聊下去之后,绝大多数人其实并不是想要一个功能多么齐全…

2026/10/14 22:30:56 阅读更多 →
Mac 源码编译 OpenClaw 实战:从依赖管理到避坑指南

Mac 源码编译 OpenClaw 实战:从依赖管理到避坑指南

简介:这份源码包面向需要在 Mac 上部署 OpenClaw 的开发者与运维人员,提供从环境准备到配置落地的完整参考。内容围绕 Homebrew、Node.js、Git 等基础依赖的确认,以及通过 brew 安装 OpenClaw CLI、配置本地大模型与网关端口、模型 ID 等关键…

2026/10/14 22:30:56 阅读更多 →
工业AI落地实践:从边缘计算到视觉质检与预测性维护

工业AI落地实践:从边缘计算到视觉质检与预测性维护

工业AI这四个字,这两年被喊得很响,但说实话,真正蹲过产线、在机台边吃过灰、把一个模型从实验室搬到车间里还让它稳定跑上三个月的人,并没有想象中那么多。我入这行快十年,前几年做的是传统机器视觉和PLC集成&#xff…

2026/10/14 22:30:56 阅读更多 →
DSP28335步进电机控制方案:ePWM脉冲输出与梯形加减速详解

DSP28335步进电机控制方案:ePWM脉冲输出与梯形加减速详解

简介:这是一套基于TI TMS320F28335 DSP与Xilinx XC3S500E FPGA协同控制步进电机的完整工程参考,适合电机控制学习者、嵌入式软硬件开发人员,以及开展课程设计或毕业设计的学生使用。整个压缩包共248个文件,大小约25.13MB&#xff…

2026/10/14 22:30:56 阅读更多 →
SQL注入本质:输入未过滤、拼接未隔离、权限未收敛

SQL注入本质:输入未过滤、拼接未隔离、权限未收敛

1. 这不是“黑进网站”的速成课,而是你真正该懂的SQL注入本质很多人看到“SQL注入”四个字,第一反应是黑客电影里飞速滚动的代码、被攻破的数据库、管理员惊恐的脸——这种刻板印象害了不少人。我带过不少刚入门的安全学习者,他们花大量时间背…

2026/10/14 22:29:55 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战: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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →