news 2026/9/3 15:44:28

从Termux到build.sh:手机构建Java项目全流程实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从Termux到build.sh:手机构建Java项目全流程实战

之前在手机上写 Java 时,总是绕不开“代码写好了,但环境跑不起来”的尴尬。手机上不是不能装编译器,而是很多人不知道该怎么把 JDK、源码目录、编译命令、运行参数串成一整套可复用的流程。网上资料大多只讲“装个 Termux、装个 JDK、敲 javac 编译”,一旦遇到多文件项目、第三方 jar 包、重复编译清理,就又回到手动敲命令的老路。这篇文章就把“手机构建 Java 项目脚本”这件事完整拆开,从环境搭建到脚本设计,再到打包、备份、常见报错排查,给出一套能直接照做的方案。不管是 Java 入门练手,还是临时写个工具类,都能在这套流程里快速跑通。

1. 背景与核心概念

1.1 手机为什么能构建 Java 项目

一提到 Java 项目构建,本能反应是“用 IDEA 或 Eclipse”。但 Java 的核心工具链其实非常轻量:只要有javac(Java 编译器)和java(Java 运行时),就能完成编译和运行。手机端的 Android 系统虽然不能直接运行桌面版 JDK,但我们可以通过 Termux 这类终端模拟器,在手机上创建一个 Linux 用户态环境,再安装 OpenJDK。这样手机就具备了和电脑相似的 Java 开发能力。

所谓“手机构建 Java 项目脚本”,就是在 Termux 环境中编写一个 shell 脚本,把javacjavajarrmfind等命令封装成统一的入口。程序员只需要执行./build.sh build./build.sh run,就能完成编译和运行,不需要记住一串文件名和参数。

这里要先区分一个概念:手机上运行 Java 程序,不等于开发 Android 应用。Android 应用需要 Android SDK、Gradle、AAPT 等一系列工具链,而且在手机上做完整 APK 构建非常吃力。本文讨论的是普通 Java 项目,比如算法练习、命令行工具、小型后端原型,这类项目在手机上完全可以跑得很顺畅。

1.2 适合与不适合的场景

先说说适合的场景。

第一个是 Java 语法学习和算法练习。手机上写个Main.java,里面实现冒泡排序、链表反转等经典题目,编译运行非常方便。第二个是写小型命令行工具。比如做一个批量文件重命名工具、日志分析脚本,用 Java 写完后直接编译执行。第三个是临时验证代码思路。比如调试某个正则表达式、测试某个 jar 包的 API 行为,不需要桌面 IDE 的重型工程。

不适合的场景也很明显。大型多模块 Maven/Gradle 项目,依赖解析和增量构建在手机上非常缓慢,而且磁盘和内存都受限。完整 Android 应用构建则依赖大量 SDK 组件,Termux 中配置难度极高,不建议尝试。需要重度调优和断点调试的场景,手机端的命令行调试工具明显不如桌面 IDE 顺滑。

1.3 一条完整的手机构建链路

要在手机上实现“编写 Java 代码 → 脚本构建 → 运行结果”,需要三层结构:

第一层是终端环境。Termux 提供了类 Linux 的包管理能力,通过pkg命令安装软件。第二层是 Java 工具链。OpenJDK 包含javacjavajarjavadoc等命令,是构建过程的核心。第三层是自动化脚本。用 bash 编写命令组合,处理目录、classpath、编译参数、运行参数。

下面开始从环境准备讲起,一步步把所有环节打通。

2. 环境准备与版本说明

2.1 在 Termux 中安装 JDK

Termux 的安装方式很简单:在酷安、GitHub Releases 或 F-Droid 中搜索 Termux,安装后打开。初次启动会执行基础文件初始化,然后就可以使用 apt/pkg 包管理器了。

这里强调一点:Termux 的软件源版本会随时间变化,不同时期的 OpenJDK 包名可能不同。最稳妥的方式是先更新源,再搜索可用的 JDK 包。

pkg update && pkg upgrade pkg search openjdk

执行pkg search openjdk后,会看到类似openjdk-17的包名。以常见的 openjdk-17 为例,安装命令如下:

pkg install openjdk-17

如果你的网络环境较慢,可以考虑使用国内镜像源,这一步在 Termux 的官方文档中有详细说明。安装完成后,验证 Java 环境:

java -version javac -version

预期输出类似:

openjdk version "17.0.x" 2024-xx-xx LTS OpenJDK Runtime Environment (build 17.0.x+xx) OpenJDK 64-Bit Server VM (build 17.0.x+xx, mixed mode, sharing)

2.2 需要提前准备的配置项

Termux 默认只允许访问应用内部目录/data/data/com.termux/files/home。如果需要读写手机的公共目录,比如/sdcard/Download,需要先执行:

termux-setup-storage

执行后手机会弹窗请求存储权限,选择允许即可。之后 Termux 会在用户目录下生成storage/快捷目录,可以通过~/storage/downloads访问下载目录。如果访问外置 SD 卡遇到权限问题,说明没有授权或手机文件系统策略较严,这时建议把项目放在 Termux 内部目录中,避免干扰。

为了后续脚本稳定运行,还需要确认 bash 环境正常。Termux 默认自带 bash,但有些精简包可能没有安装。如果不确定,可以执行:

pkg install bash

版本说明:本文示例面向通用环境,JDK 版本以 17 为例,如果你安装的是 21,命令也是通用的。关键是javajavac能正常打印版本号。

2.3 规划项目目录结构

一个规范的 Java 项目,无论放在手机还是服务器上,目录结构都建议保持一致。推荐在手机主目录下创建如下结构:

~/java-demo/ ├── src/ # Java 源码目录 │ ├── Main.java │ └── com/ │ └── demo/ │ └── Greet.java ├── lib/ # 第三方 jar 包目录 ├── out/ # 编译输出目录 ├── logs/ # 运行日志目录 └── build.sh # 构建脚本

src放源码,lib放外部依赖,out由脚本自动生成,logs记录运行输出,build.sh是统一入口。这种结构符合大多数 Java 项目和 CI 工具的习惯,以后把项目转移到电脑或服务器,也能无缝衔接。

创建目录的命令:

mkdir -p ~/java-demo/src ~/java-demo/lib ~/java-demo/logs

接下来进入编码环节,先写几个示例源码文件。

3. 从手动编译到脚本构建的进化

3.1 单文件编译运行

先看最简单的单文件 Java 程序。在src目录下创建Main.java

// 文件路径:~/java-demo/src/Main.java public class Main { public static void main(String[] args) { System.out.println("Hello from Phone Java!"); for (String arg : args) { System.out.println("arg: " + arg); } } }

手动编译运行需要两步:

cd ~/java-demo javac -encoding UTF-8 -d out src/Main.java java -cp out Main 你好 世界

预期输出:

Hello from Phone Java! arg: 你好 arg: 世界

这个流程虽然能跑,但每次都需要手动输入javacjava命令。如果文件多了,或者引用了外部 jar 包,命令会越来越长,这时候脚本的作用就体现出来了。

3.2 多文件与包结构

Java 项目通常会有包结构。比如我们再创建一个Greet类,放在com.demo包下。

// 文件路径:~/java-demo/src/com/demo/Greet.java package com.demo; public class Greet { public static void say(String name) { System.out.println("Hello, " + name + "!"); } }

修改Main.java,让主类使用Greet

// 文件路径:~/java-demo/src/Main.java import com.demo.Greet; public class Main { public static void main(String[] args) { System.out.println("Hello from Phone Java!"); Greet.say("CSDN"); for (String arg : args) { System.out.println("arg: " + arg); } } }

手动编译时,需要把两个 java 文件都传给javac

javac -encoding UTF-8 -d out src/Main.java src/com/demo/Greet.java

如果源码文件有几十个,手动列出文件就不现实了。更通用的做法是用find命令收集所有 java 文件:

find src -name "*.java" -type f | sort

在脚本中,我们会把这条命令的结果作为javac的输入,这样无论项目有多少源码文件,都能一次编译。

3.3 引入第三方依赖库

实际项目很少零依赖。假设我们的 Java 项目需要使用某个第三方库,比如 JSON 处理库、Apache Commons 工具包等。第一步是下载 jar 包到lib目录,然后编译时通过-cp指定依赖路径。

手动命令会变成这样:

javac -encoding UTF-8 -d out -cp "lib/commons-lang3.jar" src/Main.java src/com/demo/Greet.java

运行时的 classpath 也要同时包含outlib下的 jar:

java -cp "out:lib/commons-lang3.jar" Main

依赖增多后,classpath 会变得很长。bash 脚本里可以用循环拼接所有 jar 包路径,也可以直接使用通配符。有一点需要注意:java -cp支持目录后直接跟*表示加载该目录下所有 jar,但javacjava*的处理存在差异,为了跨环境稳定,建议在脚本中用循环拼出完整 classpath。

下面进入重头戏,编写完整的构建脚本。

4. 完整构建脚本实战

4.1 脚本功能设计

构建脚本要解决的核心问题是:无论源码结构多复杂,使用者都只需要记住极少数命令。我设计的功能如下:

  • init:初始化项目目录结构。
  • build:编译所有源码到out目录。
  • run:编译后运行主类,支持传入参数。
  • clean:清理编译输出目录。
  • pack:将编译结果打成可执行 Jar 包。

脚本要求:

  • 出错立即退出,避免“编译失败还继续运行”的情况。
  • 自动收集源码文件,不手动维护文件列表。
  • 自动拼接 classpath,加载lib目录下所有 jar。
  • 输出友好提示,方便在手机上直接查看构建状态。

4.2 完整 build.sh 源码

#!/data/data/com.termux/files/usr/bin/bash # 手机构建 Java 项目脚本 # 用法:./build.sh [init|build|run|clean|pack] set -e PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)" SRC_DIR="$PROJECT_DIR/src" LIB_DIR="$PROJECT_DIR/lib" OUT_DIR="$PROJECT_DIR/out" LOG_DIR="$PROJECT_DIR/logs" MAIN_CLASS="Main" # 收集所有 Java 源码文件 collect_sources() { find "$SRC_DIR" -name "*.java" -type f | sort } # 拼接 classpath:out 目录 + lib 目录下所有 jar make_classpath() { LOCAL_CP="$OUT_DIR" if [ -d "$LIB_DIR" ]; then for jar in "$LIB_DIR"/*.jar; do if [ -e "$jar" ]; then LOCAL_CP="$LOCAL_CP:$jar" fi done fi echo "$LOCAL_CP" } # 初始化目录结构 init() { mkdir -p "$SRC_DIR" "$LIB_DIR" "$LOG_DIR" echo "[INFO] 目录结构已就绪:src / lib / logs" } # 编译项目 build() { echo "[INFO] 开始编译项目..." mkdir -p "$OUT_DIR" SOURCES="$(collect_sources)" if [ -z "$SOURCES" ]; then echo "[ERROR] $SRC_DIR 目录下没有找到 .java 文件" exit 1 fi javac -encoding UTF-8 -d "$OUT_DIR" -cp "$(make_classpath)" $SOURCES echo "[INFO] 编译完成,class 文件输出到 $OUT_DIR" } # 运行主类 run() { if [ ! -d "$OUT_DIR" ]; then echo "[ERROR] 尚未编译,请先执行 ./build.sh build" exit 1 fi java -cp "$(make_classpath)" "$MAIN_CLASS" "$@" } # 清理编译输出 clean() { if [ -d "$OUT_DIR" ]; then rm -rf "$OUT_DIR" echo "[INFO] 已清理 $OUT_DIR" else echo "[INFO] 没有需要清理的目录" fi } # 打包 Jar pack() { if [ ! -d "$OUT_DIR" ]; then build fi MANIFEST_FILE="$OUT_DIR/MANIFEST.MF" printf 'Main-Class: %s\n' "$MAIN_CLASS" > "$MANIFEST_FILE" jar cfm "$PROJECT_DIR/app.jar" "$MANIFEST_FILE" -C "$OUT_DIR" . echo "[INFO] 已生成 $PROJECT_DIR/app.jar" } case "$1" in init) init ;; build) build ;; run) shift run "$@" ;; clean) clean ;; pack) pack ;; *) echo "用法: $0 {init|build|run|clean|pack}" exit 1 ;; esac

脚本说明:

  • set -e:任何一条命令返回非零状态,脚本立即退出,避免后续步骤在错误状态下继续执行。
  • collect_sources:使用find收集所有.java文件,并通过管道传给javac,解决了多文件手动列举的问题。
  • make_classpath:把out目录和lib下每个 jar 都拼到 classpath 中,防止频繁修改依赖路径。
  • case子命令:让脚本具备“类工具”体验,后续加新功能不需要改调用方。

4.3 使用方式与预期输出

首先给脚本添加执行权限:

chmod +x ~/java-demo/build.sh

初始化目录结构,然后执行构建:

cd ~/java-demo ./build.sh init ./build.sh build

预期输出:

[INFO] 目录结构已就绪:src / lib / logs [INFO] 开始编译项目... [INFO] 编译完成,class 文件输出到 /data/data/com.termux/files/home/java-demo/out

运行程序并传入参数:

./build.sh run 手机构建 Java

预期输出:

Hello from Phone Java! Hello, CSDN! arg: 手机构建 arg: Java

再验证打包功能:

./build.sh pack java -jar app.jar

预期输出:

[INFO] 已生成 /data/data/com.termux/files/home/java-demo/app.jar Hello from Phone Java! Hello, CSDN!

到这里,一个完整的“手机构建 Java 项目脚本”流程已经跑通。接下来在此基础上扩展一些工程化能力。

5. 进阶能力扩展

5.1 一键打包 Jar 的细节

pack函数中使用了jar cfm命令,这里解释几个容易踩坑的点。

jar命令的参数含义:c表示创建新归档,f表示输出到文件,m表示使用指定的 manifest 文件。写Main-Class时,类名不能带.class后缀,而且必须和包名完全匹配。如果主类在com.demo.Main,就写Main-Class: com.demo.Main

manifest 文件末尾需要换行,否则jar命令可能报错或无法识别。我们在脚本中用printf输出并在结尾加\n,就是为了避免这个问题。

打包目录时,用-C "$OUT_DIR" .表示进入out目录后把所有内容打进 jar。如果当前目录已经有MANIFEST.MF文件,也不需要额外处理,因为它会被一起打入 jar 包内。为了整洁,也可以在打包后删除 manifest 文件,这里保持现状不影响运行。

5.2 自动备份与日志记录

手机上开发项目,最怕误删源码。在脚本中增加一个backup功能,用当前日期命名备份包:

backup() { BACKUP_DIR="$PROJECT_DIR/backups" mkdir -p "$BACKUP_DIR" STAMP="$(date +%Y%m%d_%H%M%S)" tar czf "$BACKUP_DIR/java-demo-$STAMP.tar.gz" \ -C "$PROJECT_DIR" src lib build.sh echo "[INFO] 已备份到 $BACKUP_DIR/java-demo-$STAMP.tar.gz" }

再把这段逻辑加到case分支中:

backup) backup ;;

同时在run中把运行输出写入日志文件,方便回看:

run() { if [ ! -d "$OUT_DIR" ]; then echo "[ERROR] 尚未编译,请先执行 ./build.sh build" exit 1 fi mkdir -p "$LOG_DIR" LOG_FILE="$LOG_DIR/run_$(date +%Y%m%d_%H%M%S).log" java -cp "$(make_classpath)" "$MAIN_CLASS" "$@" | tee "$LOG_FILE" }

tee命令会把标准输出同时打印到终端和日志文件。这样跑完程序后,既能看到结果,也保留了完整记录。手机内存有限,建议定期清理logs目录中的旧文件。

5.3 结合 Git 进行版本管理

Termux 中安装 Git 很简单:

pkg install git

项目初始化后,创建.gitignore文件,忽略编译输出、日志和备份文件:

# 文件路径:~/java-demo/.gitignore out/ logs/ backups/ *.jar *.class

然后执行:

cd ~/java-demo git init git add . git commit -m "初始化手机Java项目"

以后每次代码修改,执行git commit即可形成版本记录。手机端直接使用 Git 命令行操作,和电脑上的流程一致。如果需要远程备份,可以关联 GitHub、Gitee 等仓库,但注意不要在脚本中写死账号密码,推荐使用 SSH Key 或凭据管理器,避免泄露。

这里强调安全边界:涉及远程仓库操作时,不要使用简单的用户名密码认证,建议使用 SSH 公钥认证,并且不要把私钥文件提交到代码仓库。如果你只是本地学习,Git 本地版本管理已经完全够用。

6. 常见问题与排查思路

手机端构建 Java 项目,最常遇到的问题集中在环境、权限、编码、内存几个维度。下面整理一张排查表:

问题现象常见原因解决思路
java: command not foundJDK 未安装或 PATH 异常执行pkg install openjdk-17,再执行which java确认路径
javac: command not found只安装了 JRE 没有安装 JDK错误安装 openjdk 包,确认包名是否包含完整 JDK
编译报乱码或非法字符源码使用中文但没有指定 UTF-8编译命令加上-encoding UTF-8,Termux 默认 locale 可能不是 UTF-8
Permission denied脚本没有执行权限执行chmod +x build.sh
bad interpreter: /bin/bash^M脚本在 Windows 下编辑过,存在 CRLF 换行符执行sed -i 's/\r$//' build.sh转换换行符
Android 文件读写失败Termux 未获得存储权限执行termux-setup-storage,在系统弹窗中授权
手机息屏后构建中断Termux 进程被系统省电策略回收执行termux-wake-lock保持前台运行,或在系统设置中允许 Termux 后台运行
OutOfMemoryError: insufficient memory手机物理内存不足或未限制堆内存运行命令增加-Xmx256m-Xmx512m参数
打包 jar 后运行找不到主类manifest 的 Main-Class 配置错误检查类名是否包含完整包名,且不包含.class后缀

下面重点展开三个高频问题的排查流程。

第一个是“编译命令找不到”。如果java -version正常但javac -version报错,很可能是安装的包只包含运行时,不包含编译工具。这时需要重新安装完整 JDK 包。Termux 的openjdk-17包一般同时包含javajavac,确认安装成功后重启终端再试。

第二个是“中文乱码”。Termux 编译时如果源码包含中文注释或中文字符串,而终端 locale 不是 UTF-8,就可能出现“编码 GBK 的不可映射字符”之类的报错。解决方式是在脚本编译参数中加入-encoding UTF-8,同时保证 Java 源码文件本身是 UTF-8 编码保存。

第三个是“内存不足”。手机内存和电脑差距很大,JVM 默认堆大小可能不适合手机环境。可以在run()函数的java命令中加上堆内存控制:

java -Xmx256m -cp "$(make_classpath)" "$MAIN_CLASS" "$@"

如果程序对内存不敏感,设置 256MB 到 512MB 即可。实际项目中,更推荐通过脚本参数动态控制,比如增加环境变量JAVA_OPTS

7. 最佳实践与工程建议

7.1 保持项目的小而美

手机端的核心优势是轻量和随时可用,不适合把大型工程塞进手机。建议每个手机上的 Java 项目保持单模块、单入口、源码不超过几十个文件。如果项目规模变大,优先考虑迁移到电脑或服务器,用 CI 工具完成构建。手机上的脚本适合做“快速原型验证”,不适合做“全量生产构建”。

7.2 脚本命名与参数设计

build.sh的子命令设计建议参考常见 CI 工具的习惯。比如:

  • build:编译
  • test:运行测试
  • run:运行主类
  • clean:清理产物
  • pack:打包

这样的命令语义清晰,开发者不需要读脚本就能上手。脚本内部使用函数拆分功能,每个函数只负责一个动作,方便后续维护和扩展。

7.3 重视编译参数与编码规范

所有源码统一使用 UTF-8 编码,编译命令固定加-encoding UTF-8。这是避免中文乱码最直接的手段。javac建议额外加上-Xlint:all参数,让编译器输出更多告警,及早发现潜在问题。例如:

javac -encoding UTF-8 -Xlint:all -d "$OUT_DIR" -cp "$(make_classpath)" $SOURCES

在命令行工具项目中,良好的告警信息能帮助你在运行前发现空指针、资源未关闭等问题。

7.4 安全与权限控制

脚本中使用rm -rf时要格外小心,尤其是cleanbackup涉及删除或覆盖操作。建议在clean中只删除明确的out目录,不要对项目根目录做递归删除。rm -rf的路径变量必须用双引号包裹,避免路径中存在空格导致误删。

远程仓库相关操作建议使用 SSH 密钥认证,不要把明文密码写在脚本中。对于任何涉及敏感信息的配置,都应该使用环境变量或单独配置文件,并加入.gitignore

7.5 充分利用手机特性

手机构建有一个独特优势:可以随时随地在碎片时间里写一段代码并立刻验证。建议把常用项目放在 Termux 主目录下,利用termux-wake-lock避免息屏中断长时间任务。也可以配合 GalaxyTermux:Tasker这类工具做自动化调用,但对大多数开发者来说,一个build.sh已经足够。

还有一个实用建议:脚本中加入info子命令,打印当前 Java 版本、项目路径、源码文件数量等环境信息,这样在换手机或重新配置环境后,可以快速确认环境是否正常:

info() { echo "Java 版本:" java -version 2>&1 echo "源码数量: $(collect_sources | wc -l)" echo "项目目录: $PROJECT_DIR" }

info加入case分支后,执行./build.sh info即可查看。这种“自检”思路在移动端场景下非常实用,因为手机环境不像电脑那么稳定,随时可能被系统策略影响。

7.6 下一步可以怎么扩展

本文的脚本是一个最小可用的闭环方案。在此基础上,你可以继续扩展以下能力:

  • 增加test子命令,运行 JUnit 测试类。
  • 引入 Maven 或 Gradle Wrapper,在手机上构建小型标准工程。
  • 增加资源文件管理,把配置文件从src分离到resources目录。
  • 结合定时任务脚本,实现手机端的定时 Java 程序执行。

如果后面有机会,我会继续写一篇“手机端运行 Spring Boot 小型接口服务”的实践笔记,把java -jar和内嵌 Tomcat 在 Termux 中的配置细节梳理一遍。当前这篇文章的内容,已经足够你在手机上跑通一套完整的 Java 项目构建流程了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 15:42:51

2026中山工程建筑材料检测排名 TOP5 CMA 资质提供钢材检测、水泥检测、砂石检测 全覆盖联系方式推荐

中山这座制造业重镇,建筑材料检测机构鳞次栉比,却也鱼龙混杂。建筑总包单位、建材生产厂家、市政工程项目、装修建设企业在选材验收时,极易遇上无资质机构出具的检测报告,导致报告无法用于工程报审、竣工验收备案,令人…

作者头像 李华
网站建设 2026/9/3 15:42:14

AI人才流动揭示研发范式迁移:从基础研究到应用迭代的转型

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

作者头像 李华
网站建设 2026/9/3 15:37:24

AB柱越硬越安全?车身碰撞安全的关键不是钢材强度

汽车AB柱越硬越好吗?这是我在和车主、车迷聊车身安全时经常被问到的问题,也是一个特别容易从车企宣传里被带偏的话题。打开不少新车发布材料,你都能看到类似表述:A柱采用多少兆帕强度热成型钢,B柱抗拉强度达到多少级别…

作者头像 李华
网站建设 2026/9/3 15:35:56

点餐预约核销系统开发实战:基于SpringBoot与uniapp的架构设计

点餐预约核销系统开发实战:基于SpringBoot与uniapp的架构设计 点餐预约核销系统,是餐饮行业数字化转型中常见的一类业务系统,它将“线上预约、到店点餐、消费核销”三个环节串联起来,形成完整的交易闭环。从技术视角来看&#xff…

作者头像 李华
网站建设 2026/9/3 15:33:13

2026投票活动批量导入选手完整操作指南:从准备到核对一步到位

2026 年很多投票活动选手有几十上百个,一个个手动添加太费时间,加到半夜还容易出现信息错漏、重复添加的问题,批量导入选手功能能省很多事。但很多第一次用的人不会操作,要么导入失败,要么导进去信息乱了、照片不显示&…

作者头像 李华
网站建设 2026/9/3 15:33:10

AI纹身设计工具深度解析:从模糊想法到可落地纹身方案

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

作者头像 李华