前端开发者早就习惯了 Flexbox,但一说到生成 PDF,很多人还以为只能手算坐标、用绝对定位硬排。dompdf.js 直接解析 CSS 布局,Flexbox 完全受支持:弹性方向、主轴对齐、交叉轴对齐、换行、伸缩比例、间距全部按标准 CSS 工作。这意味着你可以把网页里写好的 flex 布局原样搬进 PDF,导航栏、卡片列表、底部信息栏都能自动排布,不再为对齐和居中写死像素,也不再害怕内容长度变化导致版式崩塌。本文从 Flexbox 的核心概念讲起,逐一拆解容器属性与项目属性在 PDF 渲染中的表现,给出真实可运行的代码示例,并重点讲解 Flexbox 与分页的配合方式(长列表换页、防止容器被截断)以及常见坑点与降级方案,帮你彻底告别手写坐标的 PDF 开发方式,把精力放回内容本身。每个属性都配有实际场景说明,读完即可直接应用到自己的模板中,遇到问题也能按图索骥。
Flexbox 由容器(display: flex 的元素)与项目(容器内的直接子元素)构成,沿主轴与交叉轴两个方向排布。主轴方向由 flex-direction 决定,默认 row 从左到右,column 则从上到下;理解轴线是掌握 Flexbox 的第一步,也是排查布局异常的基础。
dompdf.js 按标准盒模型处理 flex 容器:项目在主轴上的排列受 justify-content 控制,交叉轴上的对齐受 align-items 控制,两者配合即可实现居中对齐、两端对齐、基线对齐等常见需求,与浏览器表现一致,网页模板可以无缝迁移。
与块级布局不同,flex 项目默认可以收缩以适应容器宽度,这正是弹性布局名称的由来。需要提醒的是,PDF 页面宽度固定,flex 容器会自动适应页面内容区宽度,配合百分比或自动尺寸,可以做出自适应的版式,无需关心具体页面尺寸。
理解主轴与交叉轴还有一个实际意义:主轴方向决定 flex 布局的伸缩基准,交叉轴方向决定对齐方式;比如 flex-direction: column 时,宽度在交叉轴上,align-items: stretch 会让子元素自动撑满整行,这正是纵向布局实现等宽列的基础。
flex 容器默认会占据父容器全部宽度,块级特性保留;如果希望容器宽度只包裹内容,可以配合 display: inline-flex,适合按钮组、标签行等行内场景,在 PDF 模板中同样适用,布局更紧凑。
flex-direction 控制主轴方向:row 适合横向排列的按钮组、标签行,column 适合纵向排列的信息块、导航菜单;row-reverse 与 column-reverse 反向排列,适合特殊视觉需求,比如从右到左的编号列表。
justify-content 沿主轴分布项目:flex-start 靠左、center 居中、space-between 两端对齐、space-around 与 space-evenly 均匀分布,报表头部、卡片墙、页脚信息条都可以用这几个值快速排版,不需要计算任何间距。
align-items 沿交叉轴对齐:stretch 默认拉伸填满、center 居中、flex-start 顶部对齐、baseline 按文字基线对齐;flex-wrap 控制换行,配合 gap 设置项目间距,注意 gap 在 flex 布局中是标准属性,dompdf.js 支持用它替代 margin 实现间距,代码更简洁。
space-between 与 space-around 的差异经常被忽略:space-between 两端项目贴边、中间均匀分布,space-around 每个项目两侧都有等距空间;页脚信息条常用 space-between,标签组常用 space-around,按需选择即可。
gap 同时设置主轴与交叉轴间距,比给每个项目加 margin 更省心,也避免 margin 叠加导致间距不均;如果目标环境对 gap 支持不完整,可以用 margin 加负值技巧模拟等距,但模板可维护性会下降,优先推荐 gap。
flex-grow、flex-shrink、flex-basis 三个属性控制项目的伸缩行为,简写 flex: 1 等价于 flex: 1 1 0%,表示项目平均分配剩余空间,是卡片等宽排列的常用写法;flex: 0 0 auto 表示不伸缩,按内容尺寸排布。
order 属性控制项目的显示顺序,不需要改动 HTML 结构即可重排,适合在模板中调整强调块的优先级;align-self 覆盖容器级 align-items,允许单个项目独立对齐,比如信息条里一行文字顶对齐、一个按钮底对齐,灵活度很高。
理解 flex-basis 与宽度的关系很重要:flex-basis 决定项目在主轴上的初始尺寸,宽度只在交叉轴上起作用。设置 flex: 1 1 200px 表示基准 200px、可伸缩,容器空间不足时按比例收缩,空间富余时按比例扩张,这是响应式卡片布局的核心写法。
flex 的伸缩计算规则可以简单概括:先按 flex-basis 分配基准尺寸,剩余空间按 flex-grow 比例分配,空间不足时按 flex-shrink 比例收缩;理解这个顺序,就能准确预判任意 flex 组合的最终尺寸,不再靠试错。
min-width 与 min-height 在 flex 项目中默认生效,内容较长时项目不会收缩到内容以下,这可能导致容器溢出;需要压缩内容时显式设置 min-width: 0,允许项目收缩到内容宽度以下,是长文本布局的常用修复。
import { DomPDF } from 'dompdf.js';
const html = `
<style>
.row { display: flex; align-items: center; }
.cards { display: flex; gap: 12px; flex-wrap: wrap; }
.card {
flex: 1 1 200px;
border: 1px solid #d5dbe3;
border-radius: 8px;
padding: 16px;
background-color: #fbfcfe;
}
.footer {
display: flex;
justify-content: space-between;
border-top: 1px solid #d5dbe3;
padding-top: 12px;
margin-top: 24px;
font-size: 12px;
color: #666;
}
.badge {
margin-left: auto; /* 推到最右 */
background-color: #27ae60;
color: #fff;
border-radius: 999px;
padding: 2px 10px;
font-size: 12px;
}
</style>
<div class='row'>
<h2>产品概览</h2>
<span class='badge'>热卖</span>
</div>
<div class='cards'>
<div class='card'><h3>键盘</h3><p>机械手感,静音设计</p></div>
<div class='card'><h3>鼠标</h3><p>无线充电,人体工学</p></div>
<div class='card'><h3>显示器</h3><p>4K 广色域,护眼模式</p></div>
</div>
<div class='footer'>
<span>共 3 款商品</span>
<span>合计 ¥ 4,299</span>
</div>`;
const pdf = new DomPDF({ format: 'A4', margin: '20mm' });
pdf.addPage(html, { format: 'A4' });
pdf.save('flexbox-demo.pdf');
长内容换页是 PDF 与网页最大的区别:flex 容器内项目较多时,dompdf.js 会在项目之间寻找合适的分页点,把容器拆到多页。建议为卡片等重复单元设置合理的固定高度,避免单个项目跨页被截断,保持每页版式整齐。
防止容器被截断可以用分页控制属性:给卡片容器设置合适的 page-break-inside 规则,让一组卡片尽量保持在同一页;给每个卡片单独设置 break-inside 规则,避免单张卡片被拦腰截断,长列表的分页体验会明显改善。
flex 容器跨页时,容器自身的边框与背景会按分页拆分,底部边框可能只出现在最后一页;如果需要每页都显示边框,建议改用独立的区块设计,或者用 @page 边距盒实现页级装饰,避免对容器边框的跨页行为做过多假设。
分页场景下还有一个小技巧:给列表容器设置固定的行高与间距,可以让每页显示的行数稳定可预期;配合 break-inside: avoid 让单行项目不跨页,长列表的分页体验会明显提升,排版也更整齐。
如果 flex 布局跨页后出现错位,先检查容器是否设置了明确宽度,再检查项目是否依赖了父容器高度;PDF 页面高度固定,高度相关的弹性布局需要谨慎设计,尽量让弹性作用在宽度维度上,稳定性更高。
Q: flex 项目挤成一团没有按预期伸缩?A: 检查是否缺少 flex-basis 或宽度约束,项目默认可以收缩;给项目设置 flex: 1 1 具体数值或 min-width,能控制最小尺寸,避免内容被压缩到不可读。
Q: justify-content: space-between 在 PDF 里看起来不均匀?A: 确认容器宽度是否确定:容器宽度不确定时,两端对齐没有参照;给 flex 容器设置 width: 100% 或明确宽度,分布结果即可与浏览器一致。
Q: 嵌套 flex 布局渲染异常?A: 逐层检查每一级容器的对齐与伸缩设置,嵌套层级过深时建议简化结构;大部分嵌套问题源于某层容器缺少宽度约束,给每层容器明确宽度或 flex-basis 能快速定位问题。
降级方案:如果个别环境对 flex 支持不完整,表格布局(display: table 系列)是最稳妥的替代,适合等宽多列场景;简单行内排列可以用 inline-block 加 text-align 实现,功能上等价但代码稍显繁琐,作为最后手段使用。
内容超长时 flex 项目默认会溢出容器而不是自动换行,这是与普通块级布局最大的差异;需要换行时显式设置 flex-wrap: wrap,配合 flex-basis 控制每行容纳的项目数,卡片墙与标签云都是这样实现的。
最后提醒:flex 布局的渲染结果与浏览器高度一致,但 PDF 没有交互与动态布局,所有尺寸在渲染时确定;模板里避免依赖 JavaScript 动态计算尺寸,全部用 CSS 声明完成,输出结果才可预期、可复现。
下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:
这是由 dompdf.js 渲染的示例 PDF 内容。