
我之前给学校数模队维护过一套论文排版模板最开始大家都图省事把所有内容塞进一个 main.tex封面、摘要、正文、附录混在一起全文几百行。结果每次比赛前都有人来问我模板里摘要页的格式在哪改为什么我改了封面却把正文也带坏了后来我花了一周时间把这套模板代码彻底重构按照模块化设计的思路拆成了十几个独立文件从那之后再没遇到过这种改一处崩一片的问题。这篇文章就围绕模板代码模块化设计来聊聊为什么模板应该拆开写、怎么拆最合理再结合数学建模LaTeX模板这个典型场景把我实际操作中的做法和踩过的坑一并写出来给需要定制模板、维护团队公共模板的人做个参考。1. 模板代码模块化先搞清楚为什么非拆不可1.1 单文件模板的痛点从哪来很多人对模板的第一反应是能编译能用就行于是模板做成一个巨大的单文件所有格式设置、封面代码、正文内容全在一个 .tex 文件里。这在很少改动的情况下确实够用但一旦模板被多个人、多个项目反复使用问题就会集中爆发。第一是定位困难。你想改页边距得在几百行里翻找 geometry 宏包的设置你想调整摘要页的字体大小得先找到摘要页的代码块在哪。这种大海捞针式的操作非常消耗耐心而且每次找的时候都容易误改到别的东西。第二是协作冲突。数学建模论文通常是三个人分工写不同的模型和算法如果大家都在同一个文件里写文件合并时不可避免会出现冲突。哪怕你用在线协作编辑同一段落被两个人同时改的场景也时有发生。单文件模板天然排斥并行修改这是它最大的结构性缺陷。第三是复用性差。你在这套模板里定义了一个漂亮的表格样式下个项目想复用这部分样式发现它和正文代码耦合在一起根本没法单独拎出来。模板的意义在于复用而单文件的写法让复用变成了一件几乎做不到的事情。第四是编译排错困难。LaTeX 编译报错时通常会提示行号但单文件模板里行号对应的位置可能根本不是真正的错误源——比如某个宏包加载顺序有问题错误抛在几十行之后才出现。这时候你需要在错误行附近排查上下文而一个几百行的文件会让排查范围变得非常大。1.2 拆开之后实际得到的收益模块化设计把一个单体模板拆成主文件 若干功能模块这一拆带来几个非常实际的好处。首先是维护成本下降。每个模块只管一件事settings/packages.tex 只管加载宏包settings/config.tex 只管页面参数front/cover.tex 只管封面排版。你改封面的字体时不需要担心会碰坏正文的样式因为两者的代码不在同一个文件里物理隔离带来了逻辑隔离。其次是协作变得顺畅。三个人可以各自负责一个正文模块文件比如 02-model.tex 和 03-solving.tex 分别由两个人独立编辑最后在主文件里用一行命令按顺序引入即可完全不会互相干扰。即使有人把某个模块写崩了也不影响其他人继续在自己负责的文件里工作。第三是定向复用成为可能。我后来把 settings 目录下的配置模块单独抽出来作为个人论文排版的通用配置新项目只要改一下主文件的结构和内容模块就能立刻投入使用省去了大量重复设置工作。第四是调试效率提升。模块化之后你可以在主文件里只保留出错模块的引入命令临时注释掉其他模块快速定位问题范围。比如编译报错说缺少 \end{abstract}你只需要检查 front/abstract.tex 这一个文件而不是在几百行里翻找。1.3 模块粒度怎么把握拆模块不是越细越好拆得过度会让模板变成一堆碎片文件反而增加导航成本。我在实践中找到一个比较合适的粒度标准每个模块文件对应一个逻辑单元编译后大约一屏到几屏能看完职责单一且功能连贯。比如把所有自定义命令放一个文件、把所有页面参数放一个文件、把封面放一个文件这是合理的。但如果你把十几条自定义命令拆成十几个文件每个文件就两三行代码那就属于过度设计了。判断标准很简单如果合并后的文件长度不超过 100 行而里面内容的逻辑又高度相关那就应该合并成一个模块。另一个判断参考是修改频率和复用概率。修改频繁、可能被多个文档复用的部分比如宏包加载和常用命令定义适合独立成模块只有这份模板用到的单次内容比如某一场比赛的编号信息留在文档内或单独一个文件都可以不需要单独设置一个模块目录。2. 数学建模LaTex模板模块化设计的最佳试验场2.1 数学建模论文模板的需求特点数学建模竞赛无论是国赛还是美赛对论文格式有严格的要求封面、摘要、正文结构、参考文献格式都要对标标准模板。这些要求导致论文模板天生就有很强的结构性封面和摘要页是相对独立的板块正文部分按照问题重述、模型假设、模型建立、模型求解、模型的评价与推广来组织附录则通常放代码和详细推导过程。我曾经见过一份典型的数学建模LaTeX模板它虽然也用了 \input 命令来引文件但各文件的边界非常模糊。比如封面文件里夹着页面设置代码摘要文件里带着自定义命令正文的第一个文件里写着参考文献样式设置。这种半拆分状态比完全不拆更危险因为它给了你模块化的错觉真去改的时候还是需要跨文件搜索。还有一个隐藏需求是结构复用。数模比赛一年有好几轮校赛、省赛、国赛每一次都需要重新写论文。如果赛前准备了一套模块化模板正文目录结构可以直接复制使用只需要替换具体的内容模块就可以了。这一点在时间紧张的比赛场景里非常重要。2.2 一套可落地的目录结构方案我给数模队设计的模板目录结构如下这套结构本质上就是一个模板代码模块化的具体范例math-model-template/ ├── main.tex ├── settings/ │ ├── packages.tex # 宏包集中管理 │ ├── config.tex # 页面参数、字体、颜色等 │ └── commands.tex # 自定义命令与环境 ├── front/ │ ├── cover.tex # 封面 │ └── abstract.tex # 摘要与关键词 ├── body/ │ ├── 01-introduction.tex # 问题背景与重述 │ ├── 02-assumptions.tex # 模型假设 │ ├── 03-model.tex # 模型建立 │ ├── 04-solving.tex # 模型求解 │ ├── 05-analysis.tex # 结果分析 │ └── 06-conclusion.tex # 模型评价与推广 ├── appendix/ │ ├── appendix.tex # 附录 │ └── code.tex # 核心代码 └── refs/ └── references.bib # 参考文献库这个结构里每个目录都有明确的职责settings 放格式与配置front 放前置部分body 放正文内容appendix 放补充材料refs 放参考文献数据。第一次用的人看到这个目录不用打开代码就能知道每个文件是干什么的这种自解释能力是模块化设计的一个重要收益。2.3 配置、样式、内容三层分离这套目录结构背后有一个核心思想把模板代码分成配置层、样式层和内容层。配置层对应 settings 目录管理这篇文档长什么样的全局参数。页面边距、正文字号、图形目录深度、参考文献风格都算配置。配置层的原则是所有可调参数集中放这样即使不懂 LaTeX 语法的人也可以打开 config.tex 改几个数字实现整体改版。样式层包括自定义命令、环境、封面版式设计。它直接引用配置层中的参数负责把内容源代码渲染成有美感的页面。样式层尽量不包含具体的文字内容比如封面文件里的2026 年数学建模竞赛这种标题应该是唯一例外的因为封面本身既是样式也是内容。内容层就是 body 和 front 目录下实际写论文内容的文件。写作者只需要关注自己这一段写什么完全不用管页边距和标题样式因为那些都在配置层和样式层里定义好了。这三层分离之后不同角色的协作方式就非常清晰负责格式的人改 settings负责版式的人改样式模块负责写作的人在 body 目录里填内容。各层之间的依赖是单向的配置层被样式层引用样式层被内容层引用永远不会出现内容层反过来影响配置层的情况。3. 实操从零搭建一个模块化模板3.1 主文件只做编排不写实现模块化模板中主文件是一个总指挥它的职责是确定模块加载顺序而不是承载任何具体内容或样式实现。一个标准的主文件通常包含三部分文档类声明、加载配置与样式模块、按顺序引入内容模块。% main.tex \documentclass[12pt]{article} % 第一步加载配置与样式 \input{settings/packages.tex} \input{settings/config.tex} \input{settings/commands.tex} \begin{document} % 第二步前置部分 \input{front/cover.tex} \input{front/abstract.tex} % 第三步正文 \input{body/01-introduction.tex} \input{body/02-assumptions.tex} \input{body/03-model.tex} \input{body/04-solving.tex} \input{body/05-analysis.tex} \input{body/06-conclusion.tex} % 第四步参考文献与附录 \bibliography{refs/references} \appendix \input{appendix/appendix.tex} \end{document}写完这个主文件模板的骨架就定了。有意思的是主文件一旦稳定下来后面基本不需要再改团队成员日常只需要在 body 目录下填充内容这其实就是模块化设计追求的稳定入口可变内部。3.2 配置模块与宏包集中管理如果说主文件是骨架那 settings 目录里的配置模块就是模板的心脏。我先说 packages.tex它的作用是集中管理所有宏包。% settings/packages.tex \usepackage{amsmath, amssymb, amsthm} % 数学公式 \usepackage{graphicx} % 插图 \usepackage{booktabs} % 三线表 \usepackage{caption} % 图表标题 \usepackage{geometry} % 页面设置 \usepackage{xcolor} % 颜色 \usepackage[hidelinks]{hyperref} % 超链接这里有一个重要的实战经验把宏包按功能分组并加注释比简单罗列要实用得多。我见过有人在 packages.tex 里列了三十个宏包但没有任何注释其他人根本不知道哪些能删、哪些是核心依赖。加上分组注释后删减和排查宏包时效率能提升不少。再看 config.tex它主要负责具体的页面参数。比如% settings/config.tex \geometry{top2.5cm, bottom2.5cm, left3.0cm, right2.5cm} \setlength{\parskip}{0.3em} \linespread{1.3} \captionsetup{fontsmall, labelfontbf}页面边距这类参数最好统一集中到这里而不是散落在不同模块里。如果封面需要单独设置边距建议在封面文件内用 \newgeometry 临时调整用完后 \restoregeometry 恢复这样就不会破坏 config.tex 中的全局参数配置。3.3 样式模块自定义命令与环境的核心commands.tex 是模板里最有个性的文件它的好坏直接影响论文的排版效率。数学建模论文里经常出现模型假设模型说明这类固定结构我习惯把它们定义为自定义环境这样正文里就可以用很简洁的写法来表达。% settings/commands.tex \newtheorem{assumption}{假设} \newtheorem{definition}{定义} \newcommand{\HRule}{\rule{\linewidth}{0.5mm}} \newenvironment{modelbox} {\begin{tcolorbox}[colbackgray!5, colframeblue!40, title模型输入]} {\end{tcolorbox}}这里的思路是把复杂的排版实现封装成简单的命令和环境让写正文的人不需要关心底层代码。比如定义了 \HRule 之后封面里想要一条横线只需要写一行代码其他地方想要同款横线也只需要调用同一个命令。这就把样式统一性问题从靠自觉变成了靠机制。我自己踩过一个坑在一份模板里把摘要的关键词样式写成了固定格式关键词ABC导致不同论文里关键词显示不一致。后来我在 commands.tex 里定义了一个 \keywords{...} 命令在命令内部统一处理分割符和格式这个问题才彻底解决。这就是样式模块的价值——统一入口统一输出。3.4 正文与附录模块的拆分策略正文模块的拆分可以直接按照论文的章节结构来。以数学建模论文为例通常就是六到八个章节每个章节对应一个独立的 .tex 文件。文件命名建议加上数字前缀这样按名称排序时就和论文的阅读顺序一致。在正文模块内部我习惯每个文件开头用一行注释说明本文件的功能比如% body/03-model.tex % 本章建立核心数学模型分别给出目标函数与约束条件。这个习惯看起来不起眼但在多人协作和后续修改中非常救命。因为半年之后你回头维护模板时对着文件名很难回忆起每个文件的具体内容而一行注释能让你瞬间恢复上下文。附录模块我单独强调一下。数模论文的附录经常放代码代码量可能比正文还多。如果把代码和附录说明混在一个文件里编译速度会明显变慢因为 LaTeX 每次编译都要处理一大段代码。建议把附录拆成 appendix.tex文字说明和 code.tex代码部分文字和代码分开改代码时不需要反复编译文字部分。4. 模块化模板的常见问题与排错实录4.1 宏包冲突与加载顺序是个老问题模块化之后宏包被集中到一个文件里反而更容易发现冲突了。最常见的冲突是 hyperref 和某些宏包的兼容问题具体表现为编译报错Package hyperref Warning: Token not allowed in a PDF string。这类问题的通用处理思路是调整 hyperref 的加载位置一般放在其他宏包之后或者给 hyperref 加选项参数比如 \usepackage[hidelinks]{hyperref} 来关闭链接边框这样既不影响跳转功能也不会在 PDF 里产生奇怪的标记。还有一类型冲突来自同一功能被多个宏包实现。比如表格方面 array、tabularx、booktabs 都可以用但如果你同时加载了 ltablex 和 tabularx就可能出现列宽命令冲突。遇到这种问题我的排查方式是先把宏包分组注释bebug 模式逐个恢复通常十分钟内就能锁定问题宏包。4.2 编译引擎与中文字体问题数学建模模板在国内使用的场景里必然要使用中文所以推荐使用 xelatex 编译配合 ctex 宏包。ctex 宏包会帮你配置中文字体但要注意它也可能带来编译变慢的问题因为字体加载和字形渲染的开销比较大。我在实践中遇到的典型报错是Font shape ... undefined尤其是论文里突然出现了某个特殊字体字形的时候。这时候先别急着加 \usepackage{fontspec}先检查是不是正文内容里混入了特殊符号。通常加一个 \usepackage{newunicodechar} 并定义符号映射就能解决而不是盲目改字体配置。另外模块化情况下编译命令运行在根目录 main.tex 所在目录最省心。如果你在子文件目录里编译某个模块需要确保相对路径引用没问题否则会出现File not found错误。这个经验适用于所有模块化 LaTeX 项目而不仅仅是模板。4.3 多人协作的格式冲突与目录职责约定模块化模板不会自动消灭协作冲突它只是把冲突范围缩小到每个模块内部。如果两个人同时改同一个正文模块文件该冲突还是会发生。我在实际推模板时给团队定了几条约定每个成员负责不同的 body 文件避免同时编辑同一个文件。所有格式调整必须先提给模板维护者修改 settings 和 commands 文件后统一发布不准个人偷偷改。正文内部尽量不新定义命令新命令一律加在 commands.tex 并注明用途。这几条约定的本质是内容与格式分离和单一修改通道。模块化提供了技术上的可能性但这些约定保障了协作上的秩序。没有这些约定就算目录分得再清晰也会有人把代码写到别人的模块里。4.4 模板的版本管理与回溯模板代码一旦模块化版本管理的价值就显现出来了。我强烈建议对模板目录做 Git 版本管理哪怕只是个人使用。理由很简单模板的样式调整是一个反复试错的过程改坏了想回退是常态。我第一次重构模板时把原有的单文件备份成 template_v1.tex然后在备份基础上一通操作。后来发现新版样式有问题想对比旧版发现旧文件中很多段落已经被我改得面目全非了。用 Git 之后每个 commit 都是一次可回滚的快照而且提交信息可以记录改了摘要标题的字体增加了算法伪代码环境这类变更记录回溯时一目了然。版本管理还有个隐藏好处当你换了电脑、换了工作环境直接从仓库拉取模板就能恢复完整的写作环境不用重新整理目录结构。这点在团队协作中尤其明显新成员只需要 clone 一份仓库马上就能开始写自己负责的章节。说到最后我想强调一下模板代码模块化设计表面看是文件拆分问题实际上是对模板使用者工作方式的重塑。它让写内容和调格式这两件事解耦让每个人都可以专注在自己最擅长的环节。如果你手头有一份总是改一处崩一片的模板我建议你找个比赛结束后的空窗期按这套思路重构一次把主文件、配置模块、样式模块和内容模块依次拆开再用真实文档跑一遍编译。我第一次做完这件事之后最大的感受是以后再也不用靠记忆去找代码位置了模板的结构就是最好的导航。那份经历也让我在后来的写作和项目里养成了先分模块再动手的习惯其实受益的不只是 LaTeX 模板任何需要长期维护的代码项目都是同一个道理。