去年做旅行产品运营后台时,我接了个看似不起眼的需求:运营每周都要更新目的地推荐,然后把新目的地做成页面上的卡片。一开始他们是用复制粘贴改HTML的办法,改一次要动十几个地方,图片路径、价格、标签稍不留神就漏掉一个。后来我直接基于最普通的PHP和HTML,写了一套“动态生成旅行卡片”的页面。思路不复杂:把旅行地数据放进PHP数组,在HTML模板里用循环渲染成卡片,再配合筛选参数让页面自己“动起来”。这篇文章不做框架演示,也不引入构建工具,就用PHP原生的数组、foreach和HTML/CSS,把一个完整案例的思考过程和实现细节讲清楚。
无论你是刚开始学后端渲染的新手,还是平时用框架比较多、想回头看看原生PHP怎么干活的人,这个案例都值得花十分钟过一遍。它能帮你理解“数据到底是怎么变成HTML标签的”,也顺带解决一批新手极容易踩的编码、转义、筛选状态保留的坑。整套代码可以直接复制到本地跑。
1. 项目需求分析与整体设计思路
1.1 为什么选 PHP + HTML 这种组合
纯静态HTML不是不能做卡片页,但遇到“每周更新、数据结构差不多、只是内容变了”的场景,静态方案就非常难受:每加一个目的地,就要复制一段几百行的卡片结构,改错一个class或标签,整张卡片样式就崩了。纯前端JavaScript方案我也考虑过,数据用JSON保存、由JS动态生成DOM。它的问题在于内容不在页面源代码里,搜索引擎和分享链接打开时看不到数据,而且如果运营的电脑JS环境有异常,页面会直接空白。
PHP在这里最大的优势是“在服务器端就把HTML拼好”,用户拿到的就是完整的、可以直接展示的网页。它没有Node、Vite那套依赖链,一台能跑PHP的服务器或者本地命令行就能工作。实际开发中,如果项目已经用了ThinkPHP、Laravel这类框架,当然可以走框架的模板引擎;但很多小工具、内部页面、临时活动页,用原生PHP反而最省事。我要做的不是造一个框架,而是拿最少的代码解决“批量生成结构化卡片”这个问题。
1.2 先想清楚卡片上要展示哪些字段
做动态页面前,我养成了一个习惯:先对着设计稿或运营提的需求列一个字段表,而不是急着写代码。旅行卡片看起来只是几行文字配一张图,但它背后对应着一组完整的数据结构。我这次准备展示这几种信息:
| 字段名 | 示例值 | 说明 |
|---|---|---|
| id | 1 | 唯一标识,将来接数据库、做筛选时有用 |
| city | 杭州 | 目的地名称 |
| country | 中国 | 所属国家,也可以叫区域 |
| type | domestic / abroad | 用于筛选:国内或国外 |
| image | images/hangzhou.jpg | 卡片头图地址 |
| price | 2680 | 跟团或套餐价格 |
| rating | 4.8 | 评分 |
| tags | 江南水乡, 摄影 | 展示用的标签,可以存成数组 |
| summary | 西湖、灵隐寺、茶山两日游 | 一句话摘要 |
字段不一定要一次定死,但提前想清楚的好处是:写HTML时有明确的输出目标,写PHP时能决定数据到底用字符串、数组还是数字。价格和评分尽量用数字类型,不要写着“¥2680”“4.8分”这样的带单位字符串,否则后面做大小排序时非常痛苦。图片地址建议统一存相对路径或完整URL,别有的存相对地址、有的存完整地址。数据规范性等于是给自己减少麻烦。
1.3 动态渲染的整体流程
动态生成卡片的本质是“把重复的HTML变成模板,把变化的内容交给数据”。流程可以概括成四步:
- PHP准备数据,数据可以来自写死的数组、独立文件、数据库或者接口。
- 根据用户请求参数(如分类、排序方式)对数据做过滤和整理。
- 在HTML模板中通过foreach循环逐条输出卡片。
- 浏览器拿到服务器返回的完整HTML,配合CSS渲染成整齐的卡片。
理解这个流程对后面的代码很有帮助。整个过程没有“点击后局部刷新”,而是每次请求都会带着当前筛选条件重新生成页面。这样虽然不酷,但胜在直观、稳。真正的单页应用以后可以再演进,目前的需求用这个方案已经非常合适。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 静态页面先行:搭一个漂亮卡片的 HTML/CSS
2.1 卡片的结构别偷懒
我习惯先写一个静态的HTML卡片,把样式调满意了,再改成动态循环。如果一上来就用PHP foreach拼标签,HTML结构出错时排查成本很高。静态卡片的标准结构大致是这样:
html复制<article class="card">
<div class="card-cover">
<img src="images/hangzhou.jpg" alt="杭州">
<span class="tag">国内</span>
</div>
<div class="card-body">
<div class="card-meta">
<span class="country">中国</span>
<span class="rating">4.8</span>
</div>
<h3>杭州</h3>
<p class="summary">西湖、灵隐寺、茶山,一次逛遍江南春色。</p>
<div class="tags">
<span>江南水乡</span>
<span>摄影</span>
</div>
<div class="card-footer">
<span class="price">¥2680</span>
<a href="detail.php?id=1">查看详情</a>
</div>
</div>
</article>
我特意把标签放在图片左上方,而不是让正文出现一长串分类。卡片高度有限,信息层级要分明:图片负责吸引视线,标题和价格是最需要突出的,摘要和标签排在后面。这个语义化结构里每个区块都有独立类名,后续交给PHP循环时不需要破坏内部层级,只需把对应位置换成输出语句即可。写结构时还要注意img必须有alt属性,一方面是无障碍访问的基本要求,另一方面当图片地址失效时,alt能告诉用户这里本来该展示什么。
2.2 用 CSS Grid 安排卡片网格
卡片本身设计好之后,页面上的卡片排列可以交给CSS Grid。Grid做二维网格非常顺手,比手动计算百分比加margin靠谱得多。我的基础布局是这样:
css复制.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1f
