Python贪吃蛇实战:30行代码掌握游戏开发核心逻辑
1. 项目概述为什么用30行代码写贪吃蛇如果你刚学Python看了一堆语法变量、循环、列表都懂了但总觉得离“做出点什么”还差一口气那这个项目就是为你准备的。用Python的pygame库30行左右的代码实现一个能跑、能玩、有碰撞检测的经典贪吃蛇游戏。这听起来像是个噱头但它的价值在于它能把你学过的零散知识点——比如列表操作、循环控制、事件处理——瞬间串联成一个看得见、摸得着的成果。这种正反馈是看十遍教程都给不了的。贪吃蛇这个游戏规则极简蛇头控制方向吃到食物变长撞墙或撞到自己就结束。但正是这种简单让它成为入门游戏开发的绝佳练手项目。你不需要纠结复杂的图形和物理引擎可以完全专注于程序逻辑本身。而pygame作为一个专为2D游戏设计的库接口直观文档友好特别适合新手快速上手建立“我也可以做游戏”的信心。2. 环境准备与核心工具解析2.1 Python与Pygame的安装与验证工欲善其事必先利其器。第一步是确保你的Python环境就绪。我推荐使用Python 3.8或更高版本它们对各类库的兼容性更好。你可以打开命令行Windows上是CMD或PowerShellmacOS/Linux上是Terminal输入python --version或python3 --version来检查。如果显示版本号说明已安装。接下来安装pygame。这是最关键的一步很多新手在这里卡住。最稳妥的方式是使用pipPython的包管理工具。在命令行中执行pip install pygame如果你系统里有多个Python版本可能需要用pip3。如果遇到网络超时可以尝试使用国内的镜像源加速例如pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后不要急着写代码先写一个几行的测试脚本验证库是否正常工作。创建一个名为test_pygame.py的文件写入以下内容import pygame pygame.init() screen pygame.display.set_mode((400, 300)) pygame.display.set_caption(Pygame Test) running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False screen.fill((0, 0, 0)) # 用黑色填充屏幕 pygame.display.flip() # 更新屏幕显示 pygame.quit()运行这个脚本如果弹出一个黑色的窗口并且点击窗口关闭按钮能正常退出那么恭喜你pygame环境配置成功。这个测试脚本本身就是一个极简的游戏循环框架后面我们会基于它扩展。注意有些集成开发环境IDE如PyCharm或VS Code可能会为每个项目创建独立的虚拟环境。如果你在IDE的终端里安装了pygame但在运行脚本时依然提示“ModuleNotFoundError: No module named pygame”很可能是因为你没有在项目的虚拟环境中安装。需要在IDE的终端或设置里确认当前使用的Python解释器路径。2.2 代码编辑器与项目结构规划对于这个项目任何文本编辑器都可以但我强烈推荐使用VS Code或PyCharm这类智能编辑器。它们能提供代码高亮、自动补全和错误提示能极大提升效率尤其是当你记不清pygame某个函数名的时候。在动手前先在脑子里或纸上规划一下代码结构。虽然我们目标是30行但逻辑必须清晰。核心模块无非这几个初始化导入库初始化pygame设置游戏窗口。游戏状态定义蛇的坐标用一个列表存储食物的坐标蛇的移动方向游戏是否结束的标志。主循环游戏的心脏不断重复“处理事件 - 更新逻辑 - 绘制画面”这个过程。事件处理监听键盘按键改变蛇的移动方向。游戏逻辑更新根据方向移动蛇头检查是否吃到食物坐标重合检查是否撞墙或撞到自己。图形绘制在屏幕上画出蛇的每一节和食物。按照这个思路即使代码紧凑也不会变成一团乱麻。我们追求的是简洁而不是混乱。3. 核心逻辑拆解与代码实现3.1 游戏世界的初始化与基本参数让我们开始写真正的游戏代码。首先创建一个新文件比如snake_game.py。开头当然是导入必要的模块并初始化pygame。import pygame import random # 初始化 pygame.init()接下来定义一些游戏常量。使用常量而不是直接把数字写在代码里是个好习惯方便后期调整。# 游戏常量 WIDTH, HEIGHT 600, 400 # 游戏窗口大小 GRID_SIZE 20 # 网格大小也是蛇身和食物的尺寸 FPS 10 # 游戏帧率控制蛇的移动速度这里的关键是GRID_SIZE。我们将游戏窗口想象成一个网格蛇和食物都占据网格的一个格子。这样蛇的移动就不是平滑的像素移动而是从一个格子“跳”到下一个格子大大简化了碰撞检测和坐标计算。FPSFrames Per Second控制了游戏循环每秒运行的次数也就间接控制了蛇的移动速度。10 FPS意味着蛇每秒移动10个“逻辑帧”速度比较适中。然后创建游戏窗口和时钟对象。# 创建窗口 screen pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(30行贪吃蛇) clock pygame.time.Clock()screen对象代表了游戏窗口我们所有的绘制操作都发生在这个“画布”上。clock对象用于控制游戏循环的速度确保它在不同的电脑上都能以基本相同的速度运行。3.2 蛇与食物的数据表示与绘制如何用数据表示一条蛇最经典的方式是使用一个列表列表中的每个元素是一个(x, y)坐标元组代表蛇身一节的位置。蛇头就是列表的第一个元素索引0蛇尾是最后一个。# 游戏初始状态 snake [(WIDTH // 2, HEIGHT // 2)] # 蛇初始位置在屏幕中央 snake_dir (GRID_SIZE, 0) # 蛇初始向右移动 (dx, dy) food (random.randrange(0, WIDTH, GRID_SIZE), random.randrange(0, HEIGHT, GRID_SIZE)) # 食物随机位置 game_over Falsesnake_dir是一个表示方向的向量(GRID_SIZE, 0)表示每帧在x轴上增加一个网格宽度y轴不变即向右移动。食物的坐标通过random.randrange生成确保其x和y坐标都是GRID_SIZE的整数倍从而对齐网格。绘制蛇和食物非常简单。在pygame中我们可以用pygame.draw.rect函数画矩形。# 绘制函数 def draw(): screen.fill((0, 0, 0)) # 清屏黑色背景 # 画蛇 for segment in snake: pygame.draw.rect(screen, (0, 255, 0), (*segment, GRID_SIZE, GRID_SIZE)) # 绿色蛇身 # 画食物 pygame.draw.rect(screen, (255, 0, 0), (*food, GRID_SIZE, GRID_SIZE)) # 红色食物 pygame.display.flip() # 更新整个屏幕这里(*segment, GRID_SIZE, GRID_SIZE)是Python的解包操作。因为segment是(x, y)元组所以这等价于(segment[0], segment[1], GRID_SIZE, GRID_SIZE)构成了pygame.draw.rect所需的(x, y, width, height)参数。pygame.display.flip()是将我们在内存中画好的这一帧图像真正更新到显示器上。3.3 游戏主循环与事件处理游戏的核心是一个while循环只要game_over不为True循环就会一直进行。每一轮循环我们称之为一“帧”。# 主游戏循环 while not game_over: # 1. 处理事件 for event in pygame.event.get(): if event.type pygame.QUIT: game_over True elif event.type pygame.KEYDOWN: if event.key pygame.K_UP and snake_dir ! (0, GRID_SIZE): snake_dir (0, -GRID_SIZE) elif event.key pygame.K_DOWN and snake_dir ! (0, -GRID_SIZE): snake_dir (0, GRID_SIZE) elif event.key pygame.K_LEFT and snake_dir ! (GRID_SIZE, 0): snake_dir (-GRID_SIZE, 0) elif event.key pygame.K_RIGHT and snake_dir ! (-GRID_SIZE, 0): snake_dir (GRID_SIZE, 0)事件处理是游戏与玩家交互的桥梁。pygame.event.get()获取当前帧发生的所有事件比如按键、鼠标移动。我们主要关心两种QUIT事件点击窗口关闭按钮和KEYDOWN事件按下键盘。控制蛇转向的逻辑有个关键限制不能直接反向。例如蛇正在向右移动(GRID_SIZE, 0)此时按下左键(-GRID_SIZE, 0)会导致蛇头瞬间掉头撞到自己这通常是不被允许的。所以我们在判断按键时加了一个条件新的方向不能是当前方向的直接反方向。3.4 游戏状态更新移动、吃食与死亡判定处理完玩家输入就要更新游戏内部的状态这是游戏逻辑的核心。# 2. 更新游戏状态 # 计算新的蛇头位置 head_x, head_y snake[0] new_head (head_x snake_dir[0], head_y snake_dir[1])首先根据当前蛇头位置和移动方向计算出下一帧蛇头应该出现的新位置new_head。接下来进行碰撞检测# 碰撞检测撞墙或撞自己 if (new_head[0] 0 or new_head[0] WIDTH or new_head[1] 0 or new_head[1] HEIGHT or new_head in snake): game_over True break条件判断了三件事1. 蛇头x坐标是否超出窗口左/右边界2. 蛇头y坐标是否超出窗口上/下边界3. 新的蛇头位置是否已经存在于蛇身的坐标列表中即撞到自己。只要满足其一游戏结束。如果没发生碰撞就把新的蛇头位置插入到蛇身列表的最前面snake.insert(0, new_head)然后判断是否吃到食物# 吃食物判断 if new_head food: # 生成新的食物确保不在蛇身上 while True: food (random.randrange(0, WIDTH, GRID_SIZE), random.randrange(0, HEIGHT, GRID_SIZE)) if food not in snake: break else: # 没吃到食物移除蛇尾保持长度不变 snake.pop()如果蛇头的新位置和食物位置重合就表示吃到了。这时我们不需要移除蛇尾pop操作蛇自然就变长了一节。然后需要在一个循环里随机生成新的食物位置并确保这个新位置不在蛇身的任何一节上。如果没吃到食物就执行snake.pop()移除列表最后一个元素蛇尾这样蛇的总长度保持不变实现了“移动”的效果。实操心得食物生成这个循环在蛇很长、几乎占满屏幕时可能会短暂地空转很多次才能找到空位。对于这个小游戏无伤大雅但如果要做更复杂的可以考虑预先计算所有空位格子然后随机选择一个效率更高。最后完成这一帧的绘制和速度控制# 3. 绘制 draw() # 4. 控制帧率 clock.tick(FPS)clock.tick(FPS)是点睛之笔它告诉pygame“这一帧循环已经处理完了请暂停一下确保从循环开始到现在的时间刚好是 1/FPS 秒。” 这样就实现了稳定的10帧每秒的更新速度。循环结束后退出pygamepygame.quit()4. 代码优化与功能扩展思路4.1 30行代码的极致压缩与可读性权衡网上流传的“极简版”贪吃蛇往往通过牺牲可读性来压缩行数。例如将初始化、绘制、逻辑更新全部挤进主循环去掉所有函数定义和常量使用短变量名。虽然看起来厉害但不利于学习和调试。我们上面的代码结构清晰约50行已经非常精简。如果非要追求“30行”可以在我们清晰版的基础上进行合并。例如将绘制代码直接写入主循环将方向判断写成字典映射合并一些语句。但我要强调的是对于学习和项目而言清晰可维护的代码远比行数少更重要。你写代码是给人包括未来的自己看的不是给行数统计器看的。我们练习压缩行数的目的是更好地理解Python的语法糖和表达方式而不是以此为目标。4.2 增加游戏功能与提升可玩性基础版本跑通后你可以尝试添加更多功能这才是提升编程能力的正道分数显示在吃到食物时增加一个score变量并在屏幕左上角用pygame.font模块渲染显示出来。游戏结束界面当game_over为True时不要立即退出而是显示“Game Over”和最终得分并提示按R键重新开始。难度递增可以让分数每增加10分FPS就加1这样蛇会越跑越快增加挑战性。音效使用pygame.mixer在吃到食物和游戏结束时播放简单的音效。障碍物在游戏区域随机生成一些不可通过的障碍物方块增加游戏复杂度。每一个小功能的添加都会涉及到新的pygame模块和编程逻辑是绝佳的练习。4.3 从脚本到可执行文件打包分发当你做出了一个让自己满意的版本可能想分享给不会安装Python的朋友。这时就需要打包成独立的可执行文件.exe。最常用的工具是PyInstaller。首先安装它pip install pyinstaller。然后在命令行中进入你的游戏脚本所在目录执行pyinstaller --onefile --windowed snake_game.py--onefile将所有依赖打包成一个单独的exe文件。--windowed运行时不显示命令行控制台窗口对于图形游戏很必要。命令执行后会在当前目录生成一个dist文件夹里面就是打包好的exe文件。你可以把这个文件发给任何人他们在Windows上双击就能运行。踩坑记录打包时最常见的错误是找不到资源文件如图片、字体、音效。如果你的代码里用到了本地文件需要确保PyInstaller能将其打包进去。更高级的做法是使用pyinstaller的--add-data参数指定资源路径。对于我们的纯代码版本则没有这个问题。5. 常见问题与调试技巧实录即使代码逻辑清晰第一次运行时也难免遇到各种“坑”。这里记录几个最常见的问题和排查思路。5.1 运行时报错与导入问题问题运行脚本立即报错ModuleNotFoundError: No module named pygame。排查这说明pygame没有安装到当前Python环境。请确认你安装pygame的命令行环境和运行脚本的环境是否是同一个Python。可以分别用pip list和python -m pygame --version检查。是否使用了虚拟环境venv在PyCharm或VS Code中确保终端激活了正确的虚拟环境并在其中重新安装pygame。问题窗口一闪而过或者打开后无法关闭。排查这通常是游戏循环的逻辑问题。检查while循环的条件是否可能永远为真或者事件处理中是否没有正确响应pygame.QUIT事件。确保你的代码中有for event in pygame.event.get():和if event.type pygame.QUIT:这段逻辑。5.2 游戏逻辑异常与行为纠错问题蛇可以瞬间掉头直接撞死自己。排查检查方向控制的代码。确保在判断“上”键时排除了当前方向是“下”的情况即snake_dir ! (0, GRID_SIZE)。其他方向同理。这是贪吃蛇游戏最常见的逻辑漏洞之一。问题食物有时会出现在蛇的身体上。排查检查食物生成的代码。在吃到食物后生成新食物的循环while True:中必须确保if food not in snake:条件成立才能break。最好在循环内加一个打印语句或者在生成后立即绘制检查看看食物坐标是否合法。问题蛇移动起来一卡一卡的或者速度不稳定。排查这通常和clock.tick(FPS)有关。首先确认FPS的值是否合理10左右比较像经典贪吃蛇。其次确保clock.tick(FPS)是在每一帧循环的最后调用。如果把它放在循环开头或中间会导致帧率计算不准。5.3 性能与体验优化点绘制优化我们的draw()函数每一帧都重绘整个屏幕。对于这个简单游戏没问题。但如果元素很多可以考虑“脏矩形”更新只重绘发生变化的部分。不过在真正遇到性能问题之前不要过早优化。输入处理我们当前只处理了KEYDOWN按键按下事件。这会导致如果长按方向键蛇的移动会取决于键盘的重复速率可能不跟手。一个更流畅的方案是在事件处理中只记录按下的方向键设置一个next_dir变量然后在游戏逻辑更新时用next_dir去更新snake_dir。这样可以确保每一帧只响应一次有效的方向改变更符合直觉。代码组织当功能越来越多时考虑将游戏状态蛇、食物、分数等封装成一个类如GameState将绘制逻辑封装成函数或另一个类。这样主循环会变得非常干净只有handle_events(state),update(state),draw(state)几个调用可读性和可维护性会大大提升。写这个30行的贪吃蛇就像搭乐高。你手头的积木Python基础语法是有限的但通过清晰的逻辑游戏循环把它们组合起来就能创造出有生命力的互动程序。这个过程里遇到的每一个错误解决的每一个问题都比单纯看语法书印象深十倍。试着去改它加分数、改颜色、变规则在动手试错中编程才会从知识变成你的技能。

相关新闻

Unity安卓14闪退:解决Attempt to load writable dex file错误

Unity安卓14闪退:解决Attempt to load writable dex file错误

1. 项目概述:一次典型的Unity安卓14兼容性危机最近在社区里看到不少Unity开发者朋友都在讨论同一个棘手的问题:项目在安卓14设备上突然闪退,而日志里赫然出现一行令人困惑的错误——Attempt to load writable dex file: /data/user/0/xxx/cac…

2026/10/14 8:53:27 阅读更多 →
前端开发者必看:收藏这份2026年AI Agent开发实战路线图,轻松转型“智能体指挥官”

前端开发者必看:收藏这份2026年AI Agent开发实战路线图,轻松转型“智能体指挥官”

本文为前端开发者量身打造了一份AI Agent开发学习路线图,结合2026年最新技术栈,帮助读者从基础的前端技能转型为掌握大模型与工具集成的“智能体指挥官”。内容涵盖Node.js/Deno、LLM调用库、Prompt Engineering、工具调用、记忆系统、流式响应、任务规划…

2026/10/11 1:14:06 阅读更多 →
收藏必备!小白程序员轻松入门大模型:揭秘 RL-LLM 的 Aha Moment 与推理链奥秘

收藏必备!小白程序员轻松入门大模型:揭秘 RL-LLM 的 Aha Moment 与推理链奥秘

本文深入探讨了 DeepSeek 团队的 R1-Zero 实验中,模型在没有 CoT 监督数据的情况下,通过 RL 训练自发产生推理链的现象,即“Aha Moment”。文章详细解析了 CoT 能力的两种来源:模仿型(SFT)和探索型&#xf…

2026/10/14 10:50:35 阅读更多 →

最新新闻

GORM开发必备:Go结构体字段用值类型还是指针?

GORM开发必备:Go结构体字段用值类型还是指针?

做Go后端开发,天天跟GORM打交道的人迟早会纠结一个问题:表结构对应的struct字段,到底要不要用指针?这问题没有标准答案,但我可以从一个线上事故讲起。之前维护一个订单管理系统,前端提交"把商品数量改…

2026/10/14 21:01:39 阅读更多 →
执行上下文进阶:this绑定、TDZ与异步调度的运行机制

执行上下文进阶:this绑定、TDZ与异步调度的运行机制

这个系列写到第四篇了。前几篇我分别聊了执行上下文的基本模型、调用栈怎么管理函数调用,以及作用域链和闭包到底是怎么串起来的。这三块内容放在一起,其实已经能把“JavaScript 代码运行时的空间模型”讲出一个比较完整的框架。但很多同学继续往下读的时…

2026/10/14 21:01:39 阅读更多 →
Python操作Excel必备:openpyxl核心读写与自动化实战

Python操作Excel必备:openpyxl核心读写与自动化实战

1. 先别急着写代码,搞清楚 openpyxl 到底解决什么问题说实话,我在日常处理数据的时候,Excel 几乎是无处不在的。别人发来的业绩报表是 xlsx,领导要的周报模板是 xlsx,就连我自己的记账表、项目进度表也都是 xlsx。数据…

2026/10/14 21:01:39 阅读更多 →
搬砖者的进阶指南:效率、工具与长期主义

搬砖者的进阶指南:效率、工具与长期主义

“搬砖”这个词,这几年算是彻底出圈了。以前大家说起搬砖,脑子里浮现的还是工地上烈日暴晒的场景——弯腰弓背,一块一块把砖头码上肩。现在呢?写代码的说自己在搬砖,做运营的说自己在搬砖,连小区门口摆摊卖…

2026/10/14 21:01:39 阅读更多 →
第57篇-论文骨架上-需求工程数据库建模与系统安全性主题

第57篇-论文骨架上-需求工程数据库建模与系统安全性主题

【软考高级系统分析师全链路通关实战】第 57 篇:论文骨架(上)——需求工程、数据库建模与系统安全性主题 本系列定位:面向有开发经验、从零备考软考高级「系统分析师」的工程师,以《系统分析师教程(第 2 版…

2026/10/14 21:01:39 阅读更多 →
EdgeDB / Gel 版本发布全史:基于官方 Changelog 的 v1 至 v7 技术演进与升级指南

EdgeDB / Gel 版本发布全史:基于官方 Changelog 的 v1 至 v7 技术演进与升级指南

数据库图数据库关系型数据库 【免费下载链接】edgedb Gel supercharges Postgres with a modern data model, graph queries, Auth & AI solutions, and much more. 项目地址: https://gitcode.com/gh_mirrors/ed/edgedb 点击查看 免费下载 本文以本仓库 docs/…

2026/10/14 21:00:37 阅读更多 →

日新闻

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 阅读更多 →