Nunjucks模板语法完全指南

Nunjucks模板语法完全指南

本文深入探讨了Nunjucks模板引擎的核心功能,包括变量输出和表达式语法、过滤器系统、控制结构(if条件判断和for循环)以及模板继承和block块系统。文章详细解析了每种功能的语法规则、使用方法和最佳实践,帮助开发者掌握Nunjucks的强大功能,创建灵活高效的模板。

变量输出和表达式语法详解

Nunjucks作为一款功能强大的JavaScript模板引擎,其变量输出和表达式语法是整个模板系统的核心基础。本节将深入探讨Nunjucks中变量输出的各种方式、表达式语法规则以及最佳实践。

基础变量输出

在Nunjucks中,最基本的变量输出使用双花括号语法:

{{ username }}
{{ user.profile.name }}
{{ items[0].title }}

这种语法会从模板上下文中查找对应的变量值并输出。如果变量不存在或值为null/undefined,则不会输出任何内容。

属性访问语法

Nunjucks支持两种属性访问方式,与JavaScript保持一致:

{# 点表示法 - 最常用 #}
{{ user.name }}
{{ article.author.email }}

{# 方括号表示法 - 适用于动态属性名 #}
{{ user["name"] }}
{{ article["author"]["email"] }}
{{ items[index].title }}

表达式语法详解

Nunjucks支持丰富的表达式语法,包括算术运算、比较运算、逻辑运算等:

算术运算
{{ 5 + 3 }}       {# 输出: 8 #}
{{ 10 - 4 }}      {# 输出: 6 #}
{{ 6 * 7 }}       {# 输出: 42 #}
{{ 15 / 3 }}      {# 输出: 5 #}
{{ 17 % 5 }}      {# 输出: 2 #}
比较运算
{{ 5 == 5 }}      {# 输出: true #}
{{ 5 != 3 }}      {# 输出: true #}
{{ 10 > 5 }}      {# 输出: true #}
{{ 5 < 10 }}      {# 输出: true #}
{{ 5 >= 5 }}      {# 输出: true #}
{{ 5 <= 10 }}     {# 输出: true #}
逻辑运算
{{ true and false }}  {# 输出: false #}
{{ true or false }}   {# 输出: true #}
{{ not false }}       {# 输出: true #}

复杂表达式示例

Nunjucks支持复杂的嵌套表达式:

{{ (user.age >= 18) and (user.country == "US") }}
{{ (items.length > 0) and (items[0].price > 100) }}
{{ (score * 100) / totalQuestions }}

表达式执行流程

mermaid

变量查找机制

Nunjucks的变量查找遵循特定的作用域链:

查找顺序作用域类型描述
1局部变量当前块级作用域内定义的变量
2模板参数渲染时传入的上下文变量
3全局变量通过addGlobal添加的全局变量
4过滤器参数过滤器函数中的参数

安全输出处理

Nunjucks默认启用自动转义功能,防止XSS攻击:

{# 自动转义HTML特殊字符 #}
{{ userInput }}  {# 安全输出 #}

{# 需要输出原始HTML时使用safe过滤器 #}
{{ htmlContent | safe }}

空值处理策略

Nunjucks对空值的处理非常智能:

{# 以下情况都不会输出内容 #}
{{ undefinedVar }}
{{ nullVar }}
{{ nonexistent.property }}
{{ emptyArray[5] }}

{# 但零值和空字符串会正常输出 #}
{{ 0 }}          {# 输出: 0 #}
{{ "" }}         {# 输出: (空字符串) #}
{{ false }}      {# 输出: false #}

表达式性能优化

为了提高模板渲染性能,建议:

  1. 避免深层嵌套属性访问{{ user.profile.contact.address.city }}{{ city }}
  2. 缓存重复使用的表达式:使用set标签缓存复杂表达式结果
  3. 减少不必要的计算:在JavaScript中预处理数据,而不是在模板中进行复杂计算

实际应用示例

{# 用户信息展示 #}
<div class="user-card">
  <h2>{{ user.firstName }} {{ user.lastName }}</h2>
  <p>年龄: {{ user.age }}岁</p>
  <p>会员状态: {{ user.isVIP ? 'VIP会员' : '普通会员' }}</p>
  <p>积分: {{ user.points | default(0) }}</p>
</div>

{# 条件价格显示 #}
<span class="price">
  {{ product.onSale ? product.salePrice : product.regularPrice }}
</span>

{# 复杂表达式计算 #}
{% set discount = (originalPrice - salePrice) / originalPrice * 100 %}
<p>折扣率: {{ discount | round }}%</p>

通过掌握Nunjucks的变量输出和表达式语法,开发者可以创建出既灵活又高效的模板,为Web应用提供强大的视图渲染能力。记住合理运用表达式语法,既能保持模板的简洁性,又能确保应用的性能表现。

过滤器系统:内置过滤器与自定义过滤器

Nunjucks的过滤器系统是其模板引擎的核心功能之一,提供了强大的数据处理和格式化能力。过滤器通过管道符号(|)应用于变量,能够对数据进行各种转换、格式化和处理操作。

内置过滤器概览

Nunjucks提供了丰富的内置过滤器,涵盖了字符串处理、数组操作、数据格式化等多个方面。以下是一些常用的内置过滤器:

过滤器名称功能描述示例用法
capitalize首字母大写{{ "hello" \| capitalize }} → "Hello"
lower转换为小写{{ "HELLO" \| lower }} → "hello"
upper转换为大写{{ "hello" \| upper }} → "HELLO"
title每个单词首字母大写{{ "hello world" \| title }} → "Hello World"
trim去除首尾空白字符{{ " hello " \| trim }} → "hello"
default提供默认值{{ undefined \| default("N/A") }} → "N/A"
length获取长度{{ [1,2,3] \| length }} → 3
first获取第一个元素{{ [1,2,3] \| first }} → 1
last获取最后一个元素{{ [1,2,3] \| last }} → 3
join数组元素连接{{ [1,2,3] \| join(",") }} → "1,2,3"
sort数组排序{{ [3,1,2] \| sort }} → [1,2,3]
escapeHTML转义{{ "<div>" \| escape }} → "<div>"
safe标记为安全HTML{{ "<div>safe</div>" \| safe }} → 渲染为div元素

过滤器的工作原理

Nunjucks过滤器的执行流程可以通过以下序列图来理解:

mermaid

字符串处理过滤器详解

字符串处理是过滤器最常见的应用场景。Nunjucks提供了多种字符串操作过滤器:

// 字符串大小写转换
{{ "hello world" | capitalize }}  // → "Hello world"
{{ "Hello World" | lower }}       // → "hello world" 
{{ "hello world" | upper }}       // → "HELLO WORLD"

// 字符串格式化
{{ "  hello  " | trim }}          // → "hello"
{{ "hello\nworld" | nl2br }}      // → "hello<br />\nworld"

// 字符串截取和填充
{{ "hello" | truncate(3) }}       // → "hel..."
{{ "hi" | center(10) }}           // → "    hi    "

数组和对象操作过滤器

对于集合数据的处理,Nunjucks提供了强大的数组和对象操作过滤器:

// 数组基本操作
{{ [3, 1, 2] | sort }}            // → [1, 2, 3]
{{ [1, 2, 3] | reverse }}         // → [3, 2, 1]
{{ [1, 2, 3, 4] | batch(2) }}     // → [[1, 2], [3, 4]]

// 对象操作
{{ {b: 2, a: 1} | dictsort }}     // → [["a", 1], ["b", 2]]
{{ {name: "John", age: 30} | list }} // → [{key: "name", value: "John"}, {key: "age", value: 30}]

// 条件筛选
{{ [1, 2, 3, 4] | select("odd") }} // → [1, 3]
{{ users | selectattr("active") }} // → 筛选出active为true的用户

自定义过滤器开发

Nunjucks允许开发者创建自定义过滤器来扩展模板功能。自定义过滤器的注册方法如下:

const nunjucks = require('nunjucks');
const env = nunjucks.configure('views');

// 简单的自定义过滤器
env.addFilter('shorten', function(str, count) {
    return str.slice(0, count || 5);
});

// 带上下文的自定义过滤器
env.addFilter('greet', function(name) {
    return `Hello, ${name}! Today is ${this.ctx.date}`;
});

// 异步自定义过滤器
env.addFilter('fetchData', async function(url) {
    const response = await fetch(url);
    return await response.json();
}, true);

过滤器链式调用

Nunjucks支持过滤器的链式调用,允许对同一个值进行多次处理:

// 多重过滤处理
{{ "  HELLO WORLD  " | trim | lower | capitalize }} 
// → "Hello world"

{{ products | sort | reverse | first }}
// → 获取排序后倒序的第一个产品

{{ user.input | escape | nl2br }}
// → 先转义HTML再转换换行符

过滤器参数传递

过滤器支持传递多个参数,参数之间用逗号分隔:

// 带参数的过滤器调用
{{ "hello world" | truncate(5, true, "...") }} 
// → "hello..."

{{ [1, 2, 3, 4, 5] | batch(2, "empty") }}
// → [[1, 2], [3, 4], [5, "empty"]]

{{ items | join(", ", "name") }}
// → 提取name属性并用逗号连接

安全相关的过滤器

在处理用户输入时,安全过滤器至关重要:

// HTML转义保护
{{ userContent | escape }}        // 防止XSS攻击
{{ userContent | forceescape }}   // 强制转义,即使已经是SafeString

// 安全内容标记
{{ trustedHTML | safe }}          // 标记为安全HTML内容

高级过滤器模式

对于复杂场景,可以创建更高级的过滤器:

// 条件过滤器
env.addFilter('iff', function(value, truthy, falsy) {
    return value ? truthy : falsy;
});

// 数学运算过滤器
env.addFilter('add', function(a, b) {
    return a + b;
});

env.addFilter('multiply', function(a, b) {
    return a * b;
});

// 日期格式化过滤器
env.addFilter('formatDate', function(date, format) {
    return new Intl.DateTimeFormat('zh-CN', { 
        year: 'numeric', 
        month: '2-digit', 
        day: '2-digit' 
    }).format(new Date(date));
});

过滤器性能优化

在使用过滤器时,需要注意性能考虑:

mermaid

最佳实践建议

  1. 优先使用内置过滤器:内置过滤器经过优化且功能稳定
  2. 避免过度链式调用:过多的过滤器链会影响性能
  3. 异步过滤器谨慎使用:在模板中避免频繁的异步操作
  4. 安全性第一:处理用户输入时务必使用escape过滤器
  5. 自定义过滤器测试:确保自定义过滤器的边界情况处理正确

通过合理使用Nunjucks的过滤器系统,可以极大地增强模板的表达能力和数据处理能力,同时保持代码的简洁性和可维护性。

控制结构:if条件判断和for循环

Nunjucks作为一款功能强大的JavaScript模板引擎,提供了丰富的控制结构来处理复杂的逻辑和数据处理需求。其中,if条件判断和for循环是最核心且常用的两种控制结构,它们让模板能够根据数据状态动态生成内容。

if条件判断

if语句在Nunjucks中的行为与JavaScript中的if语句完全一致,允许你根据条件表达式的结果选择性显示内容。

基本语法
{% if condition %}
  <!-- 条件为真时显示的内容 -->
{% endif %}

condition表达式结果为真值时(非falsenullundefined0NaN或空字符串),块内内容会被渲染。

多条件分支

Nunjucks支持完整的条件分支结构,包括elif(或elseif)和else

{% if user.role === 'admin' %}
  <div class="admin-panel">管理员控制台</div>
{% elif user.role === 'editor' %}
  <div class="editor-tools">编辑工具</div>
{% else %}
  <div class="user-welcome">欢迎,普通用户!</div>
{% endif %}
逻辑运算符

支持andor等逻辑运算符来组合多个条件:

{% if user.isLoggedIn and user.hasPermission('write') %}
  <button>发布文章</button>
{% endif %}

{% if product.stock === 0 or product.discontinued %}
  <span class="out-of-stock">缺货</span>
{% endif %}
复杂条件表达式

Nunjucks支持复杂的条件表达式,包括比较运算符和函数调用:

{% if items.length > 0 and items[0].price < 100 %}
  <div class="affordable">实惠商品</div>
{% endif %}

{% if date | isFuture %}
  <span class="upcoming">即将到来</span>
{% endif %}

for循环迭代

for循环是处理集合数据的强大工具,支持数组、对象、Map、Set等多种数据结构。

数组迭代
{% for product in products %}
  <div class="product-item">
    <h3>{{ product.name }}</h3>
    <p>价格: {{ product.price | currency }}</p>
  </div>
{% endfor %}
对象迭代

迭代对象时可以使用键值对形式:

{% for key, value in settings %}
  <div class="setting">
    <label>{{ key }}:</label>
    <span>{{ value }}</span>
  </div>
{% endfor %}
空集合处理

使用else子句处理空集合情况:

{% for item in cartItems %}
  <div class="cart-item">{{ item.name }} - {{ item.quantity }}</div>
{% else %}
  <div class="empty-cart">购物车为空</div>
{% endfor %}
循环控制变量

在循环内部,可以通过loop对象访问循环状态信息:

变量名描述示例值
loop.index当前迭代序号(从1开始)1, 2, 3...
loop.index0当前迭代序号(从0开始)0, 1, 2...
loop.first是否为第一次迭代true/false
loop.last是否为最后一次迭代true/false
loop.length集合的总长度5
{% for user in users %}
  <tr class="{% if loop.first %}first-row{% endif %} {% if loop.last %}last-row{% endif %}">
    <td>{{ loop.index }}</td>
    <td>{{ user.name }}</td>
    <td>{{ user.email }}</td>
  </tr>
{% endfor %}
多维数组解构

Nunjucks支持ES6风格的多维数组解构:

{% for x, y, z in coordinates %}
  <div>坐标: ({{ x }}, {{ y }}, {{ z }})</div>
{% endfor %}

高级用法示例

条件循环组合
{% for item in items %}
  {% if item.visible %}
    <div class="item {% if loop.index % 2 == 0 %}even{% else %}odd{% endif %}">
      <h4>{{ item.title }}</h4>
      {% if item.description %}
        <p>{{ item.description | truncate(100) }}</p>
      {% endif %}
    </div>
  {% endif %}
{% else %}
  <div class="no-items">暂无项目</div>
{% endfor %}
嵌套循环
{% for category in categories %}
  <h3>{{ category.name }}</h3>
  <ul>
    {% for product in category.products %}
      <li>{{ product.name }} - {{ product.price }}</li>
    {% endfor %}
  </ul>
{% endfor %}
循环范围控制

虽然Nunjucks没有内置的breakcontinue,但可以通过条件判断模拟:

{% for item in items %}
  {% if loop.index > 5 %}{% break %}{% endif %}
  <div>{{ item }}</div>
{% endfor %}

性能优化建议

  1. 避免深层嵌套:过深的嵌套会影响模板渲染性能
  2. 预处理数据:在JavaScript中预处理复杂数据,减少模板中的逻辑
  3. 使用过滤器:将复杂逻辑封装为过滤器,提高模板可读性
  4. 缓存结果:对不经常变化的数据使用缓存机制

mermaid

通过合理运用if条件判断和for循环,你可以构建出既灵活又高效的模板结构,满足各种复杂的业务场景需求。这些控制结构是Nunjucks模板强大表达能力的核心所在。

模板继承和block块系统

Nunjucks的模板继承系统是其最强大的功能之一,它允许你创建可重用的模板结构,通过block块机制实现内容的灵活替换和扩展。这套系统借鉴了Jinja2的设计理念,为构建复杂的Web应用提供了优雅的解决方案。

继承机制的核心概念

模板继承基于两个核心标签:{% extends %}{% block %}。extends标签用于指定父模板,而block标签定义了可被覆盖的内容区域。

{# base.html - 父模板 #}
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}默认标题{% endblock %}</title>
</head>
<body>
    <header>{% block header %}网站头部{% endblock %}</header>
    <main>{% block content %}{% endblock %}</main>
    <footer>{% block footer %}© 2023 我的网站{% endblock %}</footer>
</body>
</html>

Block块的定义和使用

Block块是模板继承的基本单元,每个block都有一个唯一的名称,子模板可以通过重新定义同名的block来覆盖父模板中的内容。

{# child.html - 子模板 #}
{% extends "base.html" %}

{% block title %}个性化标题 - 我的页面{% endblock %}

{% block content %}
    <h1>欢迎来到我的页面</h1>
    <p>这是自定义的内容区域</p>
{% endblock %}

Super函数:继承与扩展

Nunjucks提供了super()函数,允许子模板在覆盖父模板内容的同时,保留并扩展父模板的原始内容。

{% block header %}
    {{ super() }}
    <nav>
        <a href="/">首页</a>
        <a href="/about">关于</a>
        <a href="/contact">联系</a>
    </nav>
{% endblock %}

多级继承体系

Nunjucks支持多级模板继承,你可以构建复杂的模板层次结构:

mermaid

Block块的嵌套和作用域

Block块可以嵌套使用,并且具有独立的作用域。在block内部定义的变量不会影响到外部的模板上下文。

{% block sidebar %}
    {% set sidebarItems = ['最新文章', '热门标签', '归档'] %}
    <div class="sidebar">
        <ul>
        {% for item in sidebarItems %}
            <li>{{ item }}</li>
        {% endfor %}
        </ul>
    </div>
{% endblock %}

动态模板继承

Nunjucks支持动态继承,你可以根据条件选择不同的父模板:

{% if user.isAdmin %}
    {% extends "admin-layout.njk" %}
{% else %}
    {% extends "user-layout.njk" %}
{% endif %}

{% block content %}
    {# 内容会根据不同的布局模板进行渲染 #}
{% endblock %}

Block块的默认内容

父模板中的block可以包含默认内容,当子模板没有覆盖该block时,默认内容会被渲染:

{% block meta_description %}
    <meta name="description" content="这是一个默认的网站描述">
{% endblock %}

最佳实践和模式

为了有效地使用模板继承系统,推荐以下模式:

模式类型描述示例
基础布局模式创建包含基本HTML结构的模板包含<html>, <head>, <body>
区域划分模式将页面划分为逻辑区域header, main, sidebar, footer
组件复用模式创建可重用的UI组件navigation, card, modal
条件继承模式根据条件选择不同的父模板用户角色、设备类型

错误处理和调试

当使用模板继承时,需要注意常见的错误情况:

  • 循环继承:避免模板之间形成循环引用
  • 未定义block:确保所有使用的block都在父模板中有定义
  • 作用域冲突:注意变量在block内部和外部的可见性
{# 错误的循环继承示例 #}
{% extends "child.njk" %}  {# 如果child.njk又extends这个模板就形成循环 #}

性能考虑

模板继承系统在编译阶段会解析所有的extends和block关系,生成优化的渲染代码。多级继承不会带来显著的性能开销,因为Nunjucks会预先编译整个继承链。

通过合理使用模板继承和block块系统,你可以创建出结构清晰、易于维护的模板架构,大大提高前端开发的效率和代码的可重用性。

总结

Nunjucks作为一款功能强大的JavaScript模板引擎,提供了完整的模板解决方案。通过变量输出和表达式语法,开发者可以灵活处理数据;过滤器系统支持丰富的数据转换和格式化操作;控制结构(if条件判断和for循环)实现了复杂的逻辑处理;模板继承和block块系统则提供了优雅的代码复用机制。掌握这些核心功能,能够显著提升前端开发效率和代码质量,构建出结构清晰、易于维护的Web应用。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值