3、模板语法基础:变量与插值、条件判断、循环遍历、过滤器与管道
各位好,欢迎来到第三章。说实话,模板语法是每个模板工程师的「吃饭家伙」。你想想看,如果连变量怎么插、条件怎么写都搞不清楚,后面那些复杂的组件复用、布局嵌套根本玩不转。
我个人习惯把模板语法分成三块来记:数据怎么进来(变量与插值)、逻辑怎么控制(条件与循环)、数据怎么加工(过滤器与管道)。咱们一个一个来。
3.1 变量与插值:让模板「活」起来
模板最核心的能力,就是把后端传过来的数据塞到 HTML 里。说白了,就是让页面不再是死板的静态文件。
在大多数模板引擎里(比如 Jinja2、Nunjucks、Thymeleaf),变量插值都用双花括号:
<!-- 基础变量插值 -->
<p>欢迎您,{{ username }}!</p>
<!-- 对象属性访问 -->
<p>您的订单编号:{{ order.id }},金额:{{ order.total }}</p>
<!-- 列表索引 -->
<p>第一个商品:{{ products[0].name }}</p>
嗯,这里要注意:花括号里不能写复杂的 JavaScript 表达式。只能放变量名、点号访问、中括号索引。为什么?因为模板引擎的设计原则就是「逻辑与视图分离」,你非要在模板里写一堆运算逻辑,那还不如直接用 JS 渲染。
user_name),别用驼峰。因为很多后端语言(Python、PHP)习惯用下划线,前后端统一能少踩很多坑。
3.2 条件判断:让模板「会思考」
条件判断是模板里最常用的逻辑控制。我在项目中遇到过最典型的场景:用户登录状态不同,页面展示完全不一样。
以 Jinja2 为例,条件判断长这样:
{% if user.is_login %}
<p>欢迎回来,{{ user.name }}!</p>
{% elif user.is_vip %}
<p>尊贵的 VIP 用户,您有专属折扣!</p>
{% else %}
<p>请先登录或注册。</p>
{% endif %}
注意看,模板引擎用 {% %} 包裹控制语句,用 {{ }} 输出变量。这个区别一定要记牢——控制语句不输出内容,变量插值才输出。
{% if user %},以为能判断「用户对象是否存在」。结果 user 是个空对象,条件为真,页面直接崩了。后来我养成了习惯:{% if user is defined and user %},先判断变量是否存在,再判断值是否为真。
3.3 循环遍历:批量生成内容
循环遍历说白了就是「重复劳动交给模板」。你想想看,一个商品列表有 50 个商品,难道手写 50 个 <li>?当然不。
标准写法:
<ul>
{% for product in products %}
<li>
<span>{{ product.name }}</span>
<span>¥{{ product.price }}</span>
</li>
{% endfor %}
</ul>
很多模板引擎还提供了循环辅助变量,比如:
| 变量名 | 说明 | 示例 |
|---|---|---|
loop.index |
当前循环次数(从1开始) | 第 1 项、第 2 项... |
loop.first |
是否是第一次循环 | 给第一个元素加特殊样式 |
loop.last |
是否是最后一次循环 | 最后一个元素不加分隔线 |
loop.length |
循环总次数 | 显示「共 N 条」 |
我个人习惯在循环里加个 loop.first 判断,给第一个元素加个「新品」标签,用户体验会好很多。
3.4 过滤器与管道:数据加工的「流水线」
过滤器,说白了就是「把数据扔进去,加工一下再吐出来」。比如用户输入了一串英文名,你想让它首字母大写;或者后端返回了一个时间戳,你想格式化成「2024-01-15」这种样子。
在 Jinja2 和 Nunjucks 里,过滤器用管道符 | 连接:
<!-- 字符串大写 -->
<p>{{ "hello world" | upper }}</p>
<!-- 输出:HELLO WORLD -->
<!-- 默认值 -->
<p>{{ user.nickname | default("匿名用户") }}</p>
<!-- 日期格式化 -->
<p>{{ article.publish_time | date("Y-m-d") }}</p>
<!-- 链式调用:先转大写,再截取前5个字符 -->
<p>{{ "hello world" | upper | truncate(5) }}</p>
<!-- 输出:HELLO... -->
为什么叫「管道」?你可以想象成一根水管:数据从一头流进去,经过一个个过滤器(就像滤网),最后流出来的是加工好的数据。这个设计思想在 Linux 命令行里也有,模板引擎借鉴了它。
3.5 实战中的组合使用
来,咱们看一个真实场景。我在做电商后台时,需要渲染一个订单列表:
<table>
<thead>
<tr>
<th>#</th>
<th>商品名称</th>
<th>单价</th>
<th>数量</th>
<th>小计</th>
<th>状态</th>
</tr>
</thead>
<tbody>
{% for item in order.items %}
<tr class="{{ 'bg-light' if loop.first }}">
<td>{{ loop.index }}</td>
<td>{{ item.name | default('未知商品') }}</td>
<td>¥{{ item.price | round(2) }}</td>
<td>{{ item.quantity }}</td>
<td>¥{{ item.price * item.quantity | round(2) }}</td>
<td>
{% if item.status == 'paid' %}
<span class="tag tag-green">已支付</span>
{% elif item.status == 'shipped' %}
<span class="tag tag-blue">已发货</span>
{% else %}
<span class="tag tag-gray">待处理</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
你看,这里把变量插值、条件判断、循环遍历、过滤器全用上了。而且我特意在 loop.first 时加了背景色,让第一行更醒目——这就是模板工程师的「小心机」。
3.6 避坑指南
最后,分享几个我这些年踩过的坑:
- 过滤器顺序很重要:
{{ value | filter1 | filter2 }}和{{ value | filter2 | filter1 }}结果可能完全不同。我建议先做「清洗型」过滤(如去空格、转小写),再做「展示型」过滤(如截断、格式化)。 - 不要在循环里写复杂条件:比如
{% for item in items if item.price > 100 and item.stock > 0 %},这种写法可读性极差。我一般会在后端先过滤好数据,模板只负责展示。 - 变量不存在时怎么办:很多模板引擎默认会报错。我习惯给所有变量加
default过滤器,或者开启模板引擎的「静默模式」——宁可显示空白,也别让页面崩掉。
好了,这一章的内容就这些。模板语法看着简单,但真正用好需要大量实践。下一章咱们聊聊模板继承与布局,那才是真正提升效率的利器。