首页/新闻资讯/正文详情

Windows下用Verdaccio和PM2搭建npm私有仓库完整实践

发布时间:2026/9/25 3:22:55 来源:云帆数科 栏目:资讯中心
Windows下用Verdaccio和PM2搭建npm私有仓库完整实践
我做了几年Node.js开发和前端基建每次换电脑、换项目最头疼的事情往往不是写代码而是等依赖。公司网络访问npm官方源时断时续装一个依赖动辄几分钟内网机更惨干脆连不上。后来我花了一个周末在Windows Server上搭了一套 npm 私有仓库跑到现在一年半从团队几个人用到几十人没出过大乱子。整个方案核心就两个工具Verdaccio提供仓库服务PM2负责让它在Windows上常驻并开机自启。这篇文章写给想在Windows环境里自建npm私有仓库的人不管你是运维、前端负责人还是一个想给本地开发做依赖缓存的独立开发者下面这套流程基本可以照着走。我会把环境配置、Verdaccio的核心配置、PM2进程管理、客户端发布与安装全部过一遍全程以Windows为主线把容易踩的坑提前标出来。1. 为什么自建npm私有仓库以及我为什么没选Nexus1.1 先认清三个真实场景第一个场景内网隔离。公司研发网段只能访问白名单域名公网npm registry根本不在白名单里。这种情况不做私有仓库就只能靠U盘搬运node_modules非常痛苦。第二个场景外网能通但很慢。开发机多、依赖多每次从零npm ci都要等十几分钟。私有仓库会在本地缓存所有下载过的包之后再有安装请求直接走局域网速度从分钟级降到秒级。第三个场景要发公司内部包。公共组件库、工具函数库不想发到公网npm又希望安装时跟普通npm包一样简单这种需求光靠Git链接分发不好维护版本。这三点合在一起就是自建npm私有仓库的核心价值缓存加速、统一管控、私域发布。1.2 方案对比Verdaccio、Nexus还是CNPM当时我调研过三个方向Nexus Repository Manager功能全支持Maven、npm、Docker镜像等一大堆格式但配置复杂内存占用高搭一个东西要理解仓库组、Blob存储、权限矩阵运维成本不低。CNPM基于淘宝开源的cnpmjs.org思路适合服务大规模团队但架构里包含数据库、多进程调度单独为几十人的前端团队上这一套有点重。Verdaccio纯Node.js实现单一进程配置文件是一个YAML默认端口4873自带Web界面。官方直接支持Windows下运行安装只需一行npm i -g verdaccio。我最后选Verdaccio还有一个重要原因是团队本身就在Node.js生态里出问题可以自己维护不用去学Java系的仓库管理概念。Verdaccio的缓存机制是“按需代理”——本地没有的包它从上游registry下载一份存起来之后同版本不再回源。这个机制对开发场景非常友好。1.3 私有仓库搭好之后日常工作流变成什么样搭好之后团队成员的npm install不再直连公网而是先请求Verdaccio由它决定是直接给缓存还是回源拉取。发布私有包时包名使用 scoped 形式比如team/component走npm publish推到本地仓库。整个流程对开发者几乎透明只需要把registry改一次。这样做的额外好处是依赖版本出现问题时可以登录Verdaccio的Web界面查看包列表、版本历史和md5校验值排查问题比对着node_modules翻快得多。因为所有安装流量都经过仓库我还能从访问日志里看出哪些包被频繁拉取哪些版本一直没人用后面清理依赖的时候心里有数。2. 在Windows上先把npm环境理顺PATH、PowerShell执行策略与镜像源2.1 “npm不是内部或外部命令”的根因在Windows上装好Node.js之后很多人会遇到npm 不是内部或外部命令。这通常不是npm没装上而是Node.js的安装目录没有写进PATH或者安装过程中环境变量刷新不及时。判断方法很简单打开cmd输入where npm where node如果where能列出来说明PATH里有如果提示找不到就把Node.js安装目录比如C:\Program Files\nodejs\手动加到系统环境变量的Path中。改完之后一定要重新打开终端窗口因为Windows终端的环境变量是在创建时快照的老窗口不会自动感知新变量。还有一个隐藏问题如果你电脑里装过多个Node.js版本比如一个来自官方安装包一个来自nvm-windowsPATH里出现两条Node路径npm命令可能指向你没想到的那个版本。遇到这种情况我建议统一用nvm-windows管理Node版本PATH里只留nvm的入口避免系统自带Node和实际使用的Node打架。2.2 PowerShell下 npm.ps1 禁止运行脚本的问题这个坑我在Windows上遇到太多次了。在PowerShell里执行npm install突然报npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本。原因是PowerShell的执行策略默认是Restricted禁止运行任何.ps1脚本。npm在Windows下有一个npm.ps1入口因为PowerShell的环境被策略限制脚本直接被拒了。修复方法并不复杂用一个管理员权限的PowerShell窗口执行Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUserRemoteSigned的意思是本地脚本可以运行从网上下载的脚本必须有可信签名。设置完成后再用Get-ExecutionPolicy确认结果。如果不想用PowerShell也可以在cmd里跑npmcmd不受PowerShell执行策略限制但治标不治本团队里如果有人用VS Code的默认终端照样会遇到。我第一次搭Verdaccio的时候就是被这个策略卡了一晚上。cmd窗口里一切正常切到Windows Terminal用PowerShell扫一眼服务状态顺手执行个npm命令就报脚本禁运。后来把执行策略改掉这类问题才彻底消失。2.3 npm镜像源和私有仓库的关系很多人一开始为了加速安装会把全站registry切到国内镜像npm config set registry https://registry.npmmirror.com这个做法对个人开发没问题但在团队场景里每个人各用各的镜像很难保证版本一致性。私有仓库的价值之一就是把“镜像源”收拢到团队内部把上游选择权统一控制。Verdaccio的配置里可以有多个uplinks比如上游用npm官方源或者用npmmirror。团队内部所有机器只认本地仓库地址至于本地仓库回源时走哪个上游由管理员在服务端决定。这样既享受了国内镜像的快又避免了开发机各搞一套的混乱。这里还要提醒一点配置镜像源时新旧npm对registry的缓存策略不太一样。如果发现改了registry之后安装还是走了旧地址检查一下npm config get registry的实际值以及项目根的.npmrc是否有残留配置。.npmrc的优先级比全局配置高很多人全局改了没用就是被项目级文件覆盖了。3. Verdaccio配置文件的每一段我都是怎么填的3.1 安装、初始化与配置文件位置安装很简单npm i -g verdaccio安装之后先启动一次verdaccio第一次启动会自动在用户目录下生成配置目录。Windows上是C:\Users\用户名\AppData\Roaming\verdaccio\里面主要有两个东西config.yaml是配置文件storage目录是包的实际存储位置。默认配置可以直接跑但要做团队私服至少要把监听地址、认证、权限、上游代理这几项改明白。我建议一开始就编辑config.yaml而不是直接裸跑省得后面服务起来了又要重启。3.2 一份适合内网Windows场景的完整config.yaml下面这份配置我实际用于团队内网环境锚点都在Windows路径下可以直接参考storage: C:/Users/yourname/AppData/Roaming/verdaccio/storage auth: htpasswd: file: C:/Users/yourname/AppData/Roaming/verdaccio/htpasswd max_users: 200 web: enable: true title: Team NPM Private Repository primary_color: #2d6cdf uplinks: npmmirror: url: https://registry.npmmirror.com/ timeout: 60s max_fails: 5 npmjs: url: https://registry.npmjs.org/ timeout: 30s packages: team/*: access: $all publish: $authenticated proxy: npmmirror npmjs **: access: $all publish: $authenticated proxy: npmmirror npmjs listen: - 0.0.0.0:4873 log: type: stdout format: pretty level: http解释几个关键点storage指定包存储目录建议放到数据盘后面备份时直接拷这个目录。auth.htpasswd.file是账号文件Verdaccio用它来做基本认证。max_users: 200表示允许通过Web界面或npm adduser自助注册账号。如果完全不想开放注册改成max_users: -1账号只能由管理员手动在文件里维护。uplinks是上游源列表。我通常配置两个npmmirror作为默认回源npm官方作为备用。注意proxy顺序有讲究列表里第一个是优先选择。packages是权限规则的核心。team/*代表公司私有作用域包access: $all表示所有人能下载publish: $authenticated表示只有登录用户才能发布。**是兜底规则匹配所有其他包。listen一定要写0.0.0.0:4873否则Verdaccio可能只监听127.0.0.1团队其他机器无法访问。3.3 权限模型与uplinks的配合逻辑Verdaccio的权限模型一共就三个角色$all所有人、$anonymous未登录用户、$authenticated已登录用户。理解这三个角色之后配置就不难了。我的做法是下载默认开放给所有人发布只开放给登录用户。因为私有仓库的价值是让安装无障碍而发布关系到代码安全。如果某些包希望完全内网不代理外网可以把对应的proxy去掉packages: team/private-core: access: $authenticated publish: $authenticated去掉proxy后这个包如果本地没有安装会直接报错不会回源。适合那种明确只允许内网使用的核心依赖。uplinks和proxy的配合还需要注意如果团队所在网络完全上不了外网那么uplinks可以保留配置但不让它真的生效或者直接删掉。因为一旦某个包在本地缓存中没有而uplink又连不上安装会卡在上游请求超时。离线场景下我更建议把proxy全去掉只允许安装本地已经存在的包这样至少不会让开发机等半天才报错。3.4 修改配置后如何生效修改config.yaml后最简单的方式是重启进程。用PM2管理后我一般是pm2 restart verdaccio如果是纯前台方式运行可以CtrlC停掉再重新执行verdaccio。Verdaccio对配置文件的监听并不是百分百可靠的别指望改了配置它自动热加载所有字段。改一次配置重启一次养成习惯。另外Windows上如果用了杀毒软件或安全策略可能会把Verdaccio的storage目录盯得很紧导致读写变慢甚至权限拒绝。我习惯把storage配置在单独的目录并把该目录加入杀毒白名单实测能避免不少莫名其妙的“包写不进去”问题。4. 用PM2让Verdaccio常驻Windows兼谈开机自启的正确姿势4.1 为什么不用cmd窗口裸跑也不用NSSMVerdaccio官方文档推荐直接verdaccio前台运行Windows上你确实可以这么做但有个致命问题一个cmd窗口开着服务就在谁不小心关掉窗口仓库就没了。团队的人找你排查依赖问题你跑过去一看原来是运维那条窗口被回收了非常尴尬。有人会想到NSSMNon-Sucking Service Manager它可以把任意exe注册成Windows服务。但NSSM方案在Verdaccio这种需要读取环境变量的Node服务上配置起来有点绕。相比之下PM2在Node.js圈子里更通用日志、重启、监控、进程列表这些都用一套命令搞定。而且PM2本身也是Node生态的工具团队里的人上手门槛低。4.2 PM2启动Verdaccio的具体操作全局安装PM2npm i -g pm2启动Verdaccio注意第一次仍然需要先手动确认Verdaccio能跑再交给PM2pm2 start verdaccio --name verdaccio启动后查看状态pm2 list如果Verdaccio启动出问题看日志pm2 logs verdaccio --lines 50日常运维中我用到最多的几个命令pm2 restart verdaccio重启服务改配置后用。pm2 stop verdaccio/pm2 start verdaccio临时停启。pm2 monit查看CPU、内存。pm2 save保存当前进程列表配合开机恢复使用。在Windows上还有一个小细节PM2管理的进程是通过node.exe运行的但pm2 list里显示的name就是你在--name参数里指定的名字方便识别。别同时开好几个前端项目最后分不清哪个是仓库服务。还有一个容易被忽略的点PM2会默认开启“watch”吗不会。如果你想让Verdaccio配置改动后自动重启需要额外加--watch参数。但我不建议在生产仓库上开watch因为config.yaml被编辑时如果语法有问题watch模式会导致进程反复重启。我宁愿手动pm2 restart安全可控。4.3 Windows开机自启pm2 startup不可用怎么办Linux上执行pm2 startup就能用systemd做开机自启但Windows不支持这个命令。我自己第一次试的时候执行后明明提示成功重启机器发现服务还是没起来。后来才确认PM2官方在Windows的启动支持依赖额外的服务包装工具。我推荐用pm2-windows-servicenpm i -g pm2-windows-service然后在管理员权限的PowerShell窗口执行服务安装pm2-service-install -n pm2-service -u 你的Windows用户名 -p 你的密码安装完成后Windows服务管理器里会出现一个名为pm2-service的服务开机自动启动。之后再用pm2 start verdaccio启动仓库并且执行pm2 save就能实现开机后由PM2自动拉起Verdaccio。需要注意几点安装服务时用的Windows账号必须有相应权限如果用SYSTEM账号方式PM2可能读不到用户目录下的verdaccio配置导致启动失败。我是直接指定了日常使用的管理员账号实测最稳。如果公司安全策略不允许把明文密码写在命令行参数里考虑改用pm2-installer但后者需要额外准备Visual Studio Build Tools和node-gyp环境安装成本高。我试过一次觉得没有pm2-windows-service省心。卸载服务用pm2-service-uninstall别手动去services.msc里删容易留下半残状态。执行服务安装前先确认Verdaccio已经通过pm2 start托管并且执行过pm2 save。服务只是把PM2的守护进程拉起来真正的恢复动作靠的是PM2自己保存的进程列表。如果你不想装额外的npm包Windows计划任务也可以实现开机自启。先pm2 save然后在计划任务里创建一个开机触发任务执行的程序是cmd参数是/c pm2 resurrect。这个方法我实测过能用但计划任务在系统刚刚启动、网络还没有完全就绪时可能会失败Verdaccio需要稍后手动拉起。相比起来pm2-windows-service处理时序更稳定。4.4 端口占用排查4873被别的进程抢了怎么办Windows上启动Verdaccio最经典的问题就是端口被占。查端口占用netstat -ano | findstr :4873如果结果里有进程按PID杀掉taskkill /F /PID 1234但要注意杀进程只是缓解。如果某个程序总是在开机时抢4873最好在config.yaml里换一个端口然后把客户端registry地址同步改掉。改端口不是一个难事但团队成员都要跟着改所以建议一开始就规划好端口比如4873、4874都行尽量避开Windows上常见的开发工具端口。5. 客户端接入与发布私有包附一份常见报错排查表5.1 把开发机的registry指到私有仓库最简单的方式npm config set registry http://服务器IP:4873如果团队里有人不想全局改registry可以只在项目里配。项目根目录创建.npmrc写入registryhttp://服务器IP:4873.npmrc的优先级是项目级 用户级 全局所以项目配了自己生效不影响其他项目。我更推荐的是用nrm管理多个源npm i -g nrm nrm add local http://服务器IP:4873 nrm use local这样切换官方源、公司源、镜像源都方便。团队协作时我一般会把地址直接固化到文档或脚手架模板里新人拉代码后执行一次nrm use local就完成了。这里有个细节要注意如果Verdaccio服务器用的不是默认4873端口而是自定义端口客户端配置时一定要写全端口号别省略。很多人浏览器访问http://192.168.1.10:4873能打开Web界面但npm install一直连不上一看是.npmrc里漏写了端口。5.2 注册账号并发布私有包Verdaccio默认支持npm adduser注册npm adduser --registry http://服务器IP:4873按要求输入用户名、密码、邮箱后本地会写入认证token。之后发布包之前先确认package.json里的name是不是带scope的{ name: team/component, version: 1.0.0, publishConfig: { registry: http://服务器IP:4873 } }然后执行npm publish发布成功后如果配置了proxy其他人直接npm install team/component就能从本地仓库拉到。因为publishConfig.registry已经写死发布时不需要额外加--registry参数省得有人发到公网去。私有包版本管理跟公网包的语义化版本规则完全一样新版本号必须大于已发布版本重复版本会报409。发错版本不要删除整个包来补一个是Verdaccio的删除在默认配置下不完整另一个是别人可能已经装了旧版本删除会导致锁文件解析不了。正确做法是发一个新的patch版本并在发布说明里标注废弃原因。还有一个建议私有包尽量加private: true或者至少把publishConfig的registry写死。不然哪天有人把某个内部包的项目代码拖出去直接执行npm publish因为全局registry还是官方源包就可能被误发到公网。这种事故一旦发生相当于公司内部代码泄露没有任何后悔药。我在团队里是把.npmrc和publishConfig都写进脚手架模板强制带上的。5.3 常见报错与解决思路下面是我在实际使用中高频遇到的一些报错放在表格里方便对照定位报错关键词可能原因处理方式403 Forbidden没有发布权限或者用户未登录先npm adduser登录再检查Verdaccio配置里packages的publish权限409 Conflict包版本号已存在修改package.json版本号后重新发布ECONNREFUSED服务没启动或者端口/地址写错用netstat -ano | findstr :4873检查服务再核对registry地址ETARGET本地仓库和上游源都找不到该版本检查uplinks和proxy配置确认上游可以访问或确认包名、版本拼写CERT_HAS_EXPIRED自签名证书或者系统时间问题确认Verdaccio是否启用了HTTPS是否使用了受信任证书ERESOLVE overriding peer dependency依赖树中的peer冲突多数是生态包自身的问题可临时用--legacy-peer-deps安装同时推动上游修复npm warn deprecated node-domexception1.0.0依赖链上的过时包警告不影响功能留意后续升级即可这里单独说一下npm warn deprecated这一类信息在日志里看着吓人实际上只是npm在提示某个间接依赖已不再维护。不需要专门处理。真正需要关注的是ECONNREFUSED和权限类的错误这类通常会直接导致安装失败。排查的时候我习惯先在服务器本机执行一次安装验证npm install some-package --registry http://localhost:4873如果本机能装通说明问题出在客户端到服务器的网络路径比如防火墙、端口封禁或者服务器监听地址没放开。如果本机也装不通再回头检查Verdaccio日志看是配置问题还是上游回源的问题。这个二分法在Windows环境里很管用能快速把问题定位到网络层还是服务层。6. 一点个人体会最后讲一段自己的实际经验。搭建这套Verdaccio PM2方案本身不难难的是把Windows环境里那些零碎问题一次性理顺尤其是PowerShell执行策略、PATH环境变量和Windows服务自启这三个点。我建议先把Verdaccio用前台模式跑通确认包能发、能装再交给PM2托管最后再做开机自启和防火墙放行。顺序反了的话出了问题就分不清是配置问题还是服务问题。仓库搭好之后一定要记得定时备份storage目录和htpasswd文件这俩一个是包数据一个是账号数据丢了都挺折腾。我在团队里还专门把这两个目录的备份写进了巡检脚本每周自动打包一次。如果你的团队准备长期使用这套方案这个习惯越早建立越好。还有一个小技巧升级Verdaccio之前把现有storage目录复制一份再执行npm更新一旦新版本配置格式有变化可以快速回滚。升级这种事最怕的就是服务起不来了数据还在复制一份无伤大雅关键时刻能救命。

相关推荐

CTF Wiki 智能合約安全實戰:深入解析 EVM 整數溢位與下溢(Integer Overflow and Underflow)
CTF Wiki 智能合約安全實戰:深入解析 EVM 整數溢位與下溢(Integer Overflow and Underflow)

文档网络安全教程 【免费下载链接】ctf-wiki Come and join us, we need you! 项目地址: https://gitcode.com/gh_mirrors/ct/ctf-wiki 点击查看 免费下载 本篇技術指南以 ctf-wiki 區塊鏈章節為核心,系統講解以太坊虛擬機(EVM)中… · 2026/9/25 3:22:55

OpenShell Google Vertex AI Provider:为沙箱授予安全访问 Google Vertex AI 的架构与实战指南
OpenShell Google Vertex AI Provider:为沙箱授予安全访问 Google Vertex AI 的架构与实战指南

【免费下载链接】OpenShell OpenShell is the safe, private runtime for autonomous AI agents. 项目地址: https://gitcode.com/gh_mirrors/op/OpenShell 点击查看 免费下载 本文以 OpenShell 仓库中的架构文档 architecture/google-vertex-ai-provider.md 为主体… · 2026/9/25 3:22:55

CTF-Wiki:glibc 2.24 下 IO_FILE 利用進階——vtable 校驗、緩衝區劫持與 `_IO_str_jumps` 實戰
CTF-Wiki:glibc 2.24 下 IO_FILE 利用進階——vtable 校驗、緩衝區劫持與 `_IO_str_jumps` 實戰

文档网络安全教程 【免费下载链接】ctf-wiki Come and join us, we need you! 项目地址: https://gitcode.com/gh_mirrors/ct/ctf-wiki 点击查看 免费下载 本文基於 CTF-Wiki 的 glibc 2.24 下 IO_FILE 的利用 一文展開,系統梳理 glibc 2.24 新增的 vta… · 2026/9/25 3:22:55

深入gnhf编排器架构:状态机如何让AI代理整夜循环不丢一行代码
深入gnhf编排器架构:状态机如何让AI代理整夜循环不丢一行代码

深入gnhf编排器架构:状态机如何让AI代理整夜循环不丢一行代码 【免费下载链接】gnhf Before I go to bed, I tell my agents: good night, have fun 项目地址: https://gitcode.com/gh_mirrors/gn/gnhf gnhf(good night, have fun)是一… · 2026/9/25 4:25:44

VirtualBox E_FAIL (0x80004005) 报错全解析:从驱动冲突到UUID修复
VirtualBox E_FAIL (0x80004005) 报错全解析:从驱动冲突到UUID修复

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 4:25:44

MIPI DSI转LVDS桥接方案:LT9211与N76E003配置实战
MIPI DSI转LVDS桥接方案:LT9211与N76E003配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 4:25:44

Windows 11锁屏机制深度解析与分版本禁用方案
Windows 11锁屏机制深度解析与分版本禁用方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 4:25:44

必应搜索出现Ref A/B/C标签?原因排查与解决指南
必应搜索出现Ref A/B/C标签?原因排查与解决指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 4:25:37

Cadence Sigrity TDR仿真实战:从原理到阻抗曲线分析
Cadence Sigrity TDR仿真实战:从原理到阻抗曲线分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 4:25:37

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码