PHP模板引擎如何优雅输出前端组件所需的数据结构

阿乐
阿乐 管理员 年卡会员
发布于 2026-09-04 18:47 ·31 浏览 ·0 回复

先别急着“模板套数据”

当年用 PHP 写页面,最爽的就是“HTML 里混 PHP”。现在写前端组件,最不爽的也是“HTML 里混 PHP”。时代变了。当你的页面上不再是整块整块的服务器渲染 HTML,而是由 Vue、React 或者小程序的组件去消费数据时,你在 Blade、Twig 或 Smarty 里拼出来的 `<div>` 串,对前端来说就是一堆无法消费的“死肉”。

问题来了——PHP 模板引擎本身只是字符串替换工具,但我们要输出的东西,已经从“一串视图”变成了“一份数据”。所以怎么写模板,直接决定了前后端对接是丝滑还是互骂。

第一种坑:在模板里做 JSON 拼装

很多人为了图省事,直接在模板里去手写 JSON 字符串,甚至把组件需要的数据在模板里 `json_encode` 一下,然后塞进 `data-*` 属性或 `<script>` 标签里。

<div data-props='@json($user)'>

看着挺美,实际上隐患超大。

如果 `$user` 里有个嵌套对象或带转义的字符串,模板引擎和前端输出很容易打架。更关键的是,模板文件一旦承担了“数据组织”的职责,逻辑就会开始泄漏。你会在模板里发现十多个 `if` 判断去决定某个字段要不要暴露给前端——这已经是 Controller 和 DTO 该干的活了。

在模板层拼 JSON,本质上是在把后端逻辑往回拉。

第二种玩法:用 DTO 先把数据“捏好”

更优雅的方式是:让模板引擎只做一件事,把它收到的对象忠实地序列化出来。

如果我们要给一个表格组件、一个图表组件或者一个瀑布流组件喂数据,正确的流程是先定义好结构,这个结构是由 PHP 的数据对象来组织,再由模板引擎负责“翻译”并输出。最常用的是 Laravel 的 `Data Object` 风格,或者简单地用 `DTO` / `Resource`。

假设有个产品卡片组件,需要以下数据结构:

{
  "id": 1,
  "title": "咖啡壶",
  "price": "¥299",
  "tags": ["爆款", "限时"],
  "image_url": "/img/kettle.jpg"
}

我们肯定不想在模板里写 `{{ json_encode($product->toArray()) }}` 了事。更合理的做法是让 Product 实现一个“面向组件的展示结构”接口,或者用一个专门的 `PresentTransformer`,把那几个字段规规整整地掏出来规整好,模板里只做一次调用。

第三步:上下文切换得干脆

所谓“优雅”,本质上是一个上下文分离的问题。

当你输出 HTML 时,模板引擎的上下文是 DOM;但当你输出组件数据时,模板引擎的上下文是 JS 运行时。这两者绝不能混着来。一旦你把数据直接 `echo` 在一个 `<script>` 标签里,就需要面临一个很现实的问题:`</script>` 会被转义吗?`<!--` 会不会因为是注释直接被前端吞掉?

我有一个偏执但实用的习惯——所有传给前端组件用的数据,都写成 JSON 独立文件或独立块,不在 HTML 中间件里内联。比如在 Twig 里:

{% set props = dto.toComponentArray() %}
<script>
  window.__PRODUCT_DATA__ = {{ props|json_encode|raw }};
</script>

注意这里的 `json_encode|raw`,并不是为了炫技,而是完全信任 DTO 里已经做好了转义与安全处理。当纯数据用 JSON 输出时,必须确保模板没有主动破坏结构。每一次歧义都是 bug 的温床。

另一种更“懒”的浪漫:聚合数据渲染

实践中很多架构师会采用“一次模板调用,多个组件数据合并”的方式。

模板输出的不再是页面片段,而是面向整个路由页的“组件数据地图”。比如我们在模板里定义区块,每个区块对应一个组件:

{% for component in components %}
  <div data-component="{{ component.name }}"
       data-props="{{ component.props|e('html_attr') }}">
  </div>
{% endfor %}

有意思的是,我们并不把这个组件拉出来拼装 HTML,而是交给前端 JS 去异步 mount。这让模板引擎变成了纯粹的数据网关。你需要定义的只有“组件名”和“组件对应的 props 结构”两个规则。用 PHP 的好处这时反而露出来了——因为 PHP 的数组和对象的转换极为自然,你有非常多的姿势来从 Eloquent 模型或自定义 sources 里把字段收拾成前端想要的样子。

这种模式被很多现代 Laravel Inertia 方案取代,但它毕竟依赖服务端渲染和 JsonResponse 输出,与模板引擎的协作并不多。可如果你确实没法放弃 Blade 和 Twig,那这种“模板定壳,数据入魂”逻辑反而比硬转 JSON 更舒服。

总结:模板引擎也该有边界感

模板引擎不负责“设计数据”,它只负责“配合数据”。真要优雅输出前端组件所需的数据结构,核心策略就三句话:

- 数据结构在 PHP 层定好,别在模板里凭感觉调整;
- 输出纯数据要独立出口,不要混在 HTML 标签流里当逃兵;
- 模板只是一种表现接口,你的数据格式应当能被人为测试与抽象复用,不能粘死在某个页面文件里。

这样,前端拿到的是明明白白的结构,后端看到的是清清爽爽的逻辑,模板这份“遗老”技术,也在组件时代找到属于自己的生态位。

他们都看过 1 人浏览过
断了的弦

全部回复 0

还没有回复,来抢沙发~