用 Leaf 渲染动态网页 封面
Vapor 教程

用 Leaf 渲染动态网页

接口返回 JSON 是给前端 App 用的;但要做一个人人能直接打开的网站,你得返回 HTML。Leaf 就是 Vapor 自带的模板语言:把变量塞进页面,动态生成最终 HTML。

一、为什么需要模板引擎

你可以手写字符串拼 HTML,但一旦页面多了、还要套用公共头部和底部,就会又臭又长。模板引擎的任务是:在静态 HTML 的骨架上,留出变量坑位,运行时把数据填进去。Leaf 就是 Vapor 官方推荐的那个引擎,纯 Swift 生态、零额外依赖。

二、先把 Leaf 加进来

Package.swift 的 dependencies 里加入 Leaf:

.package(url: "https://github.com/vapor/leaf.git", from: "4.0.0")

并在 target 的依赖里引用它。然后在 configure.swift 中把 Leaf 注册为一种「视图渲染服务」:

import Leaf

app.views.use(.leaf)

新建目录 Resources/Views/,模板文件就放在这里,后缀是 .leaf

三、你的第一个模板

建一个 Resources/Views/index.leaf,内容就是普通 HTML,区别是用 #(变量名) 占位:

<!DOCTYPE html>
<html>
<head><title>#(title) | Acronyms</title></head>
<body>
  <h1>#(title)</h1>
  <p>欢迎来到用 Swift 写的网站。</p>
</body>
</html>

这里的 #(title) 就是坑位,运行时由 Swift 填值。

四、用上下文把数据传进去

控制器里通常定义一个遵循 Encodable 的「上下文」结构体,专门描述这个页面需要哪些数据:

struct IndexContext: Encodable {
    let title: String
}

渲染时把上下文一并传进去:

import Vapor
import Leaf

struct WebsiteController: RouteCollection {
    func boot(routes: RoutesBuilder) throws {
        routes.get(use: indexHandler)
    }

    func indexHandler(_ req: Request) throws -> EventLoopFuture<View> {
        let context = IndexContext(title: "首页")
        return req.view.render("index", context)
    }
}

req.view.render("index", context) 会去找 index.leaf,把 title 填进 #(title),最后返回一页完整 HTML。

本质是服务端渲染:Leaf 在服务器上就把页面拼好再发给浏览器,所以首屏快、对 SEO 友好。它跟前端的 React/Vue 是两套思路,按需选择。

五、常用模板语法

Leaf 不止能填变量,还支持基础逻辑:

例如列出所有缩略语:

#for(acronym in acronyms)
  <li>#(acronym.short) — #(acronym.long)</li>
#endfor

六、下一步

页面要显示真实数据,得接数据库。下一篇我们用 Fluent 把「缩略语」存进数据库,并在控制器里查询后交给 Leaf 渲染。


本系列其他文章