🔥 歡迎來到 Node.js 實戰專欄!在這里,每一行代碼都是解鎖高性能應用的鑰匙,讓我們一起開啟 Node.js 的奇妙開發之旅!
Node.js 特訓專欄主頁
專欄內容規劃詳情
Express模板引擎選型與使用全解析:打造動態Web頁面的利器
在基于Express構建Web應用時,模板引擎是生成動態頁面的核心組件。它允許開發者將數據與HTML結構分離,通過簡單的語法將后端數據動態填充到頁面中。市面上存在多種模板引擎,每種都有其獨特的特性和適用場景。本文將深入探討常見Express模板引擎的選型要點與具體使用方法,幫助開發者根據項目需求做出最佳選擇。
一、模板引擎基礎概念
1.1 什么是模板引擎?
模板引擎是一種工具,它使用特定的語法將模板文件(通常是HTML)與數據結合,生成最終的HTML頁面。在Express應用中,模板引擎可以接收來自控制器的數據,并將其嵌入到模板中,實現動態內容展示。例如,通過模板引擎可以將數據庫中查詢到的用戶列表數據填充到HTML頁面,動態生成用戶展示頁。
1.2 模板引擎的作用
1.2.1 分離邏輯與展示
模板引擎的核心優勢在于實現了業務邏輯與頁面展示的清晰分離。在MVC架構中:
- 業務邏輯(如數據處理、數據庫操作等)由控制器和模型層完成
- 頁面展示則由模板負責處理
具體表現為:
- 控制器只負責傳遞數據(如
render('index', {title: '首頁'})
) - 模板文件(如
index.html
)只需專注于數據呈現 - 開發人員可以獨立修改業務邏輯或UI界面而互不干擾
典型應用場景:
- 后端開發人員與前端設計師的協作開發
- 同一套業務邏輯支持多套皮膚/主題切換
- 長期維護的復雜項目
1.2.2 提高開發效率
模板引擎通過以下機制顯著提升開發效率:
-
模板繼承:
- 定義基礎模板(如
base.html
)包含公共頭部/尾部 - 子模板只需擴展特定內容區域
<!-- base.html --> <html> <head>{% block title %}{% endblock %}</head> <body>{% include 'header.html' %}{% block content %}{% endblock %}{% include 'footer.html' %} </body> </html>
- 定義基礎模板(如
-
循環語句:
- 批量生成列表項
<ul>{% for item in items %}<li>{{ item.name }}</li>{% endfor %} </ul>
-
條件渲染:
{% if user.isVIP %}<div class="vip-badge"></div> {% endif %}
-
局部模板:
- 將重復組件(如商品卡片)提取為獨立模板文件
- 通過
include
指令復用
統計表明,合理使用模板特性可減少40%-60%的視圖層代碼量。
1.2.3 動態數據展示
模板引擎支持多種動態數據呈現方式:
-
數據綁定:
- 直接輸出變量:
<h1>{{ product.name }}</h1>
- 表達式計算:
<span>總價:{{ quantity * price }}</span>
- 直接輸出變量:
-
個性化渲染:
歡迎回來,{{ user.nickname || user.username }}! {% if user.newMessage %} <div class="message-bubble">{{ user.newMessageCount }}</div> {% endif %}
-
國際化支持:
{{ __('welcome_message') }} <!-- 根據用戶語言環境輸出不同文本 -->
-
數據結構處理:
- 列表分組顯示
- 樹形結構遞歸渲染
- 分頁數據展示
實際案例:
- 電商網站根據用戶瀏覽歷史推薦商品
- 新聞站點的個性化首頁布局
- SaaS產品的多租戶界面定制
這種動態能力使單個模板可以適應成千上萬種數據組合場景。
二、常見Express模板引擎對比與選型
2.1 EJS(Embedded JavaScript)
核心特點
-
語法簡潔性
- 采用
<% %>
和<%= %>
等直觀的嵌入式標簽 - 完全支持標準JavaScript語法,無需額外學習新語法規則
- 采用
-
開發效率
- 支持直接在HTML中編寫控制邏輯(如if/else、for循環等)
- 通過
<%= %>
標簽實現數據綁定輸出
-
模板繼承
- 支持
<%- include('header') %>
等模板包含語法 - 可實現布局復用和模塊化開發
- 支持
詳細語法示例
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>用戶管理系統</title><style>.user-item { padding: 10px; border-bottom: 1px solid #eee; }</style>
</head><body><%- include('navbar') %><h1>用戶列表(共<%= users.length %>人)</h1><% if (users.length === 0) { %><div class="alert">暫無用戶數據</div><% } else { %><ul class="user-list"><% users.forEach((user, index) => { %><li class="user-item"><span><%= index + 1 %>.</span><strong><%= user.username %></strong><% if (user.isAdmin) { %><span class="badge">管理員</span><% } %><p>郵箱:<%= user.email || '未填寫' %></p></li><% }); %></ul><% } %><%- include('footer') %>
</body></html>
進階應用場景
-
動態頁面生成
- 電商網站產品列表頁
- 博客系統的文章詳情頁
-
數據可視化
- 結合Chart.js等庫生成動態圖表
- 報表數據的HTML模板渲染
-
郵件模板
- 用戶注冊歡迎郵件
- 訂單確認通知郵件
-
開發調試
- 快速構建管理后臺原型
- API接口數據的可視化調試
性能優化建議
- 預編譯模板提升渲染效率
- 配合Express.js等框架使用res.render()方法
- 合理使用模板緩存機制
2.2 Pug(原名Jade)
特點
Pug是一款高效的HTML模板引擎,采用獨特的縮進式語法結構,顯著減少了傳統HTML中的冗余標簽。其核心特點是:
- 簡潔的語法:通過嚴格的縮進來表示DOM元素的層級關系,完全省略了閉合標簽
- 增強的可讀性:代碼結構清晰直觀,類似Python的縮進風格
- 豐富的功能:支持變量插值、條件語句、循環等編程特性
- 高性能編譯:模板會被預編譯為JavaScript函數,渲染速度快
不過,對于長期使用傳統HTML的開發人員來說,需要適應:
- 嚴格的縮進規則(必須使用空格,不能混用Tab)
- 全新的語法范式
- 缺少閉合標簽的視覺提示
詳細語法示例
// 文檔類型聲明
doctype html
// 根元素帶屬性
html(lang='en')// 頭部區域headmeta(charset='UTF-8')// 動態標題title #{pageTitle} | 我的網站// 條件判斷if stylesheetlink(rel="stylesheet" href=stylesheet)// 正文內容body// 包含其他模板include ./header.pug// 主內容區main.containerh1.main-title 用戶管理系統// 循環輸出用戶列表ul.user-listeach user, index in usersli.user-item(class=user.isAdmin ? 'admin' : '')span= index + 1 + '.'a(href="/users/"+user.id)= user.nameif user.isAdminspan.badge 管理員// 頁腳footerp Copyright ? #{new Date().getFullYear()}
適用場景
-
大型Web應用:
- 適合多人協作的復雜項目
- 模板復用性強,可通過
include
和extends
機制組織代碼 - 例如電商后臺管理系統、CMS內容平臺
-
需要快速迭代的項目:
- 修改模板時只需調整少量代碼
- 配合前端框架(如Vue、React)使用時效率更高
-
對代碼整潔度要求高的項目:
- 在代碼審查時更容易發現結構問題
- 比傳統HTML減少約40%的代碼量
-
全棧JavaScript項目:
- 與Node.js/Express完美集成
- 可直接在模板中使用JavaScript表達式
開發建議
- 使用編輯器插件(如VS Code的Pug插件)獲得語法高亮和縮進提示
- 建立統一的縮進規范(推薦2或4個空格)
- 復雜邏輯盡量寫在路由/控制器中,保持模板簡潔
- 合理使用
mixin
功能創建可復用組件
2.3 Handlebars
特點
Handlebars 是一款輕量級的模板引擎,其核心特點包括:
- 簡潔語法:采用直觀的雙大括號
{{}}
作為占位符,例如{{title}}
表示變量插值,{{#if}}
表示條件判斷,語法簡單易學 - 邏輯純凈:刻意限制模板中的編程邏輯,不支持復雜的腳本語法,強制開發者將業務邏輯與視圖分離
- 數據驅動:強調數據綁定機制,通過上下文對象將數據注入模板,保持模板的聲明式特性
- 擴展性強:支持自定義helper函數,可以擴展模板功能而不破壞其簡潔性
詳細語法示例
基礎數據綁定:
<p>歡迎,{{user.name}}!您已登錄{{loginCount}}次。</p>
條件語句:
{{#if isAdmin}}<button class="admin">管理面板</button>
{{else}}<button class="user">普通視圖</button>
{{/if}}
循環遍歷(帶索引和上下文切換):
<table>{{#each products as |product index|}}<tr class="{{if index 'even' 'odd'}}"><td>{{product.name}}</td><td>{{formatPrice product.price}}</td></tr>{{/each}}
</table>
完整應用示例
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>電商平臺 - 產品列表</title><style>.product-card { border: 1px solid #ddd; padding: 15px; margin: 10px }.discount { color: red; font-weight: bold }</style>
</head>
<body><header><h1>{{store.name}} - {{formatDate today}}</h1>{{#if promoBanner}}<div class="banner">{{promoBanner}}</div>{{/if}}</header><main><div class="filter">排序方式:{{select sortOptions selected=currentSort}}</div><div class="product-grid">{{#each products}}<div class="product-card"><h3>{{name}}</h3><p>{{description}}</p><div class="price">{{#if onSale}}<span class="original-price">{{originalPrice}}</span><span class="discount">{{salePrice}} (省{{discountPercent}}%)</span>{{else}}{{price}}{{/if}}</div>{{> productBadge}}</div>{{else}}<p class="empty">暫無商品</p>{{/each}}</div></main><footer>{{> common/footerLinks}}</footer>
</body>
</html>
適用場景詳解
-
企業級應用開發:
- 適用于需要嚴格分離關注點的大型項目
- 典型用例:電商平臺的產品展示頁、內容管理系統的列表視圖
-
多團隊協作項目:
- 前端團隊可以獨立開發模板結構
- 后端團隊只需提供JSON數據接口
- 示例:銀行系統的客戶門戶網站
-
靜態網站生成:
- 與靜態網站生成器(如Gatsby、Eleventy)配合使用
- 優勢:保持HTML的可讀性同時實現動態內容
-
郵件模板系統:
- 特別適合需要批量生成個性化郵件的場景
- 示例:電商訂單確認郵件、系統通知郵件
-
漸進式Web應用(PWA):
- 在Service Worker中預編譯模板
- 離線時仍能渲染基本界面
最佳實踐建議
-
模板組織:
- 將大型模板拆分為多個partials(部分模板)
- 使用目錄結構組織模板文件(如:
templates/partials/header.hbs
)
-
數據處理:
- 在傳入模板前預先處理好數據格式
- 示例:日期格式化、金額計算等應在數據層完成
-
性能優化:
- 預編譯模板提高運行時性能
- 使用模板緩存機制減少重復編譯
-
調試技巧:
- 使用
{{log}}
helper輸出調試信息 - 在開發環境啟用source maps定位模板錯誤
- 使用
2.4 Nunjucks 模板引擎
核心特點
-
Django模板風格:
- 采用與Django模板相似的語法結構,包括{% %}標簽和{{ }}變量插值
- 學習曲線平緩,特別適合有Python開發經驗的開發者
-
高級模板特性:
- 模板繼承:通過
{% extends "base.html" %}
實現布局復用 - 宏定義:使用
{% macro %}...{% endmacro %}
創建可重用組件 - 區塊控制:
{% block %}...{% endblock %}
實現內容替換
- 模板繼承:通過
-
數據處理能力:
- 內置50+過濾器,如:
{{ var | default("N/A") }}
默認值處理{{ date | format("YYYY-MM-DD") }}
日期格式化{{ text | truncate(50) }}
文本截斷
- 支持自定義過濾器注冊
- 內置50+過濾器,如:
詳細語法示例
{# 基礎模板 base.html #}
<!DOCTYPE html>
<html lang="en">
<head>{% block head %}<meta charset="UTF-8"><title>{% block title %}默認標題{% endblock %}</title>{% endblock %}
</head>
<body>{% include "header.html" %}<main>{% block content %}<!-- 默認內容 -->{% endblock %}</main>{% macro userCard(user) %}<div class="user-card"><h3>{{ user.name | capitalize }}</h3><p>注冊于:{{ user.joinDate | date("YYYY-MM-DD") }}</p></div>{% endmacro %}
</body>
</html>{# 子模板 users.html #}
{% extends "base.html" %}{% block title %}用戶管理系統{% endblock %}{% block content %}
<h1>系統用戶列表</h1>
<div class="user-grid">{% for user in userList %}{{ userCard(user) }}{% if loop.last %}<p class="total-count">總計:{{ loop.length }}位用戶</p>{% endif %}{% endfor %}
</div>
{% endblock %}
典型應用場景
-
企業級應用開發:
- 后臺管理系統模板渲染
- 多語言國際化支持
- 復雜的權限顯示控制
-
內容型網站:
- 新聞門戶的文章模板
- 電商網站的商品展示頁
- 博客系統的主題模板
-
開發優勢體現:
- 通過模板繼承減少60%以上的重復代碼
- 宏定義可復用組件使維護成本降低40%
- 異步渲染支持提升SSR性能
-
擴展能力:
- 自定義過濾器處理業務特定邏輯
- 通過addGlobal注入全局變量
- 支持模板預編譯提升運行時效率
性能優化建議
- 啟用
autoescape
防止XSS攻擊 - 開發環境設置
noCache:true
方便調試 - 生產環境開啟
watch:false
提升性能 - 復雜模板建議使用
precompile
預編譯
三、在Express中使用模板引擎
3.1 配置模板引擎
在Express框架中,模板引擎是實現動態網頁渲染的核心組件。以常用的EJS(Embedded JavaScript)模板為例,在Express項目中配置模板引擎的完整流程如下:
安裝EJS模板引擎
首先需要通過npm安裝EJS包:
npm install ejs --save
安裝完成后,EJS會自動添加到項目的package.json
依賴中。
基本配置
在Express主文件(通常是app.js
或server.js
)中進行配置:
const express = require('express');
const app = express();// 設置視圖引擎為ejs
app.set('view engine', 'ejs');
// 設置視圖文件存放目錄(默認是./views)
app.set('views', path.join(__dirname, 'views'));
配置說明:
view engine
:指定使用的模板引擎名稱views
:設置模板文件所在的路徑,使用path.join
確保跨平臺兼容性
示例應用
下面是一個完整的路由示例,展示如何使用EJS模板:
// 渲染首頁
app.get('/', (req, res) => {const users = [{ username: 'user1', email: 'user1@example.com',joinDate: new Date(2023, 0, 15)},{ username: 'user2',email: 'user2@example.com',joinDate: new Date(2023, 1, 20)}];// 傳遞數據到模板并渲染res.render('index', { title: '用戶列表',users,currentYear: new Date().getFullYear()});
});app.listen(3000, () => {console.log('服務器在3000端口運行');console.log('訪問地址:http://localhost:3000');
});
模板文件結構
在views
目錄下創建index.ejs
文件:
views/
└── index.ejs
模板文件可以這樣編寫:
<!DOCTYPE html>
<html>
<head><title><%= title %></title>
</head>
<body><h1><%= title %></h1><ul><% users.forEach(user => { %><li><%= user.username %> - <%= user.email %><small>(加入于: <%= user.joinDate.toLocaleDateString() %>)</small></li><% }) %></ul><footer>? <%= currentYear %> 我的網站</footer>
</body>
</html>
注意:
- 使用
<%= %>
輸出變量值 - 使用
<% %>
執行JavaScript代碼 - 可以在模板中調用傳遞的數據對象的方法
這種配置方式適用于大多數Express應用場景,開發者可以根據項目需求調整視圖目錄或使用其他模板引擎如Pug、Handlebars等。
3.2 傳遞數據到模板
在Express框架中,我們可以向模板引擎傳遞各種類型的數據,包括但不限于基本數據類型、數組、對象以及函數等。這種靈活性使得我們能夠構建更加動態和功能豐富的視圖層。
復雜數據傳遞示例
以下是一個完整的控制器示例,展示了如何傳遞多種數據類型到EJS模板:
// 定義一個路由處理函數
app.get('/', (req, res) => {// 定義用戶數組const users = [{ firstName: '張',lastName: '三',username: 'zhangsan',email: 'zhangsan@example.com',joinDate: new Date('2020-01-15')},{firstName: '李',lastName: '四',username: 'lisi',email: 'lisi@example.com',joinDate: new Date('2019-05-20')}];// 定義工具函數const formatDate = (date) => {return date.toLocaleDateString('zh-CN');};const getFullName = (user) => {return user.firstName + user.lastName;};// 渲染模板并傳遞數據res.render('index', { title: '用戶管理系統',users,helpers: {getFullName,formatDate}});
});
模板中使用示例
在index.ejs
模板中,我們可以這樣使用傳遞過來的數據和函數:
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title><%= title %></title><style>.user-item {padding: 10px;border-bottom: 1px solid #eee;}</style>
</head><body><h1><%= title %></h1><div class="user-container"><% users.forEach(function(user) { %><div class="user-item"><h3><%= helpers.getFullName(user) %></h3><p>用戶名: <%= user.username %></p><p>郵箱: <a href="mailto:<%= user.email %>"><%= user.email %></a></p><p>注冊日期: <%= helpers.formatDate(user.joinDate) %></p></div><% }); %></div>
</body></html>
實際應用場景
- 用戶列表展示:如示例所示,可以用來展示網站用戶列表
- 數據報表生成:傳遞計算函數和格式化函數來生成復雜報表
- 動態表單渲染:傳遞表單驗證函數和表單配置對象
- 權限控制:傳遞權限檢查函數來控制頁面元素的顯示/隱藏
通過這種方式,我們可以將業務邏輯與展示邏輯分離,保持代碼的整潔性和可維護性。控制器負責準備數據,模板負責展示數據,各司其職。
3.3 模板繼承與復用
以Nunjucks為例,詳細介紹模板繼承的使用方法及其優勢:
基礎模板架構
創建基礎模板base.html
作為所有頁面的框架:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{% block title %}默認標題{% endblock %}</title>{% block styles %}<!-- 公共樣式資源 --><link rel="stylesheet" href="/css/reset.css"><link rel="stylesheet" href="/css/common.css">{% endblock %}
</head><body><header class="site-header"><nav><a href="/">首頁</a><a href="/about">關于</a></nav></header><main class="main-content">{% block content %}<!-- 主要內容區域由子模板填充 -->{% endblock %}</main><footer class="site-footer"><p>? 2023 公司名稱. All rights reserved.</p></footer>{% block scripts %}<!-- 公共腳本資源 --><script src="/js/jquery.min.js"></script><script src="/js/common.js"></script>{% endblock %}
</body></html>
子模板實現示例
創建index.html
繼承并擴展基礎模板:
{% extends 'base.html' %}<!-- 自定義頁面標題 -->
{% block title %}用戶列表頁面 - 網站名稱{% endblock %}<!-- 添加頁面特定樣式 -->
{% block styles %}
{{ super() }} <!-- 保留基礎模板中的樣式 -->
<link rel="stylesheet" href="/css/user-list.css">
{% endblock %}<!-- 定義頁面主要內容 -->
{% block content %}
<div class="page-header"><h1>用戶列表</h1><p class="description">當前系統注冊用戶列表</p>
</div><div class="user-list">{% if users.length > 0 %}<ul class="user-items">{% for user in users %}<li class="user-item"><span class="username">{{ user.username }}</span><span class="email">{{ user.email }}</span><span class="reg-date">{{ user.registerDate | date }}</span></li>{% endfor %}</ul>{% else %}<p class="no-users">暫無用戶數據</p>{% endif %}
</div>
{% endblock %}<!-- 添加頁面特定腳本 -->
{% block scripts %}
{{ super() }} <!-- 保留基礎模板中的腳本 -->
<script src="/js/user-list.js"></script>
{% endblock %}
實際應用場景
- 多頁面網站:新聞網站中不同的欄目頁面(如新聞、體育、娛樂)可以共用基礎模板
- 后臺管理系統:所有管理頁面繼承同一基礎模板,保持統一風格
- 移動端適配:通過基礎模板統一管理不同設備的viewport設置
使用注意事項
- 使用
{{ super() }}
保留父模板的塊內容 - 塊命名要有明確語義,如
header_scripts
、footer_content
等 - 避免嵌套過深的繼承關系,建議不超過3層
- 對于頻繁修改的區塊,可以拆分為獨立的include文件
通過模板繼承機制,可以實現:
- 90%以上的公共代碼復用
- 統一維護站點結構和資源
- 快速創建風格一致的新頁面
- 方便進行全局樣式調整
四、模板引擎選型建議
-
項目規模與復雜度:
- 小型項目(如個人博客、企業宣傳網站):可以選擇語法簡單的EJS或Handlebars。這些模板引擎學習曲線平緩,內置功能精簡,能快速實現基本的數據綁定和條件渲染功能。例如,使用EJS的
<% %>
標簽可以輕松插入JavaScript邏輯,適合需要快速開發的原型項目。 - 大型復雜項目(如電商平臺、SAAS應用):推薦使用Nunjucks或Pug。Nunjucks提供模板繼承、宏(macro)等功能,可以有效管理多層嵌套的頁面結構。比如通過
{% extends "base.html" %}
實現布局復用,顯著減少重復代碼。Pug則通過其獨特的縮進語法和mixin特性,特別適合構建組件化的前端架構。
- 小型項目(如個人博客、企業宣傳網站):可以選擇語法簡單的EJS或Handlebars。這些模板引擎學習曲線平緩,內置功能精簡,能快速實現基本的數據綁定和條件渲染功能。例如,使用EJS的
-
團隊技術棧:
- 團隊熟悉JavaScript:EJS是理想選擇。它不僅支持完整的JavaScript表達式(如
<%= user.name %>
),還允許直接編寫JS邏輯(<% if(user) { %>
)。這種與JavaScript近乎無縫的集成能大幅降低團隊的學習門檻。 - 團隊有Django/Python背景:Nunjucks的語法設計(如
{% if %}...{% endif %}
)與Django模板語言高度相似,團隊成員可以立即運用熟悉的控制流和過濾器概念。例如,兩者都支持管道操作符({{ name|capitalize }}
),這種一致性可以節省大量培訓時間。
- 團隊熟悉JavaScript:EJS是理想選擇。它不僅支持完整的JavaScript表達式(如
-
性能需求:
- 對性能要求極高(如高并發頁面):Pug憑借其預編譯機制和精簡的HTML輸出表現優異。測試顯示,Pug模板編譯后的運行效率比解釋型引擎快30%-40%,且生成的HTML代碼體積更小(例如將
div.container
編譯為<div class="container"></div>
),這對首屏加載速度要求嚴苛的項目尤為重要。 - 性能要求一般(如后臺管理系統):各主流模板引擎(EJS、Handlebars等)在常規場景下的渲染耗時差異通常在毫秒級,此時更應關注開發效率。例如Handlebars的helper函數可以快速封裝業務邏輯,而無需過度糾結模板解析的微秒級性能差異。
- 對性能要求極高(如高并發頁面):Pug憑借其預編譯機制和精簡的HTML輸出表現優異。測試顯示,Pug模板編譯后的運行效率比解釋型引擎快30%-40%,且生成的HTML代碼體積更小(例如將
五、總結
Express模板引擎的選擇和使用是Web應用開發中的重要環節。不同的模板引擎各有優劣,開發者需要根據項目需求、團隊技術棧和性能要求等因素綜合考慮,做出最合適的選擇。通過合理使用模板引擎,能夠實現動態頁面的高效開發,提升Web應用的用戶體驗和開發效率。在實際項目中,不斷實踐和探索,掌握模板引擎的高級特性,將有助于打造出更優質的Web應用。
📌 下期預告:RESTful API設計規范與實現
??????:如果你覺得這篇文章對你有幫助,歡迎點贊、關注本專欄!后續還有更多 Node.js 實戰干貨持續更新,別錯過提升開發技能的好機會~有任何問題或想了解的內容,也歡迎在評論區留言!👍🏻 👍🏻