← dompdf.js Studio

Flexbox 布局渲染完全指南:dompdf.js 告别坐标计算

前端开发者早就习惯了 Flexbox,但一说到生成 PDF,很多人还以为只能手算坐标、用绝对定位硬排。dompdf.js 直接解析 CSS 布局,Flexbox 完全受支持:弹性方向、主轴对齐、交叉轴对齐、换行、伸缩比例、间距全部按标准 CSS 工作。这意味着你可以把网页里写好的 flex 布局原样搬进 PDF,导航栏、卡片列表、底部信息栏都能自动排布,不再为对齐和居中写死像素,也不再害怕内容长度变化导致版式崩塌。本文从 Flexbox 的核心概念讲起,逐一拆解容器属性与项目属性在 PDF 渲染中的表现,给出真实可运行的代码示例,并重点讲解 Flexbox 与分页的配合方式(长列表换页、防止容器被截断)以及常见坑点与降级方案,帮你彻底告别手写坐标的 PDF 开发方式,把精力放回内容本身。每个属性都配有实际场景说明,读完即可直接应用到自己的模板中,遇到问题也能按图索骥。

Flexbox 核心概念:容器与轴线

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');

Flexbox 与分页的配合

长内容换页是 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 声明完成,输出结果才可预期、可复现。

⚡ 现场演示(点击生成 PDF)

下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:

Hello from dompdf.js!

这是由 dompdf.js 渲染的示例 PDF 内容。