Leaf 是一种强大的模板语言,其语法受 Swift 启发。
// swift-tools-version:5.2
import PackageDescription
let package = Package(
name: "MyApp",
platforms: [ .macOS(.v10_15) ],
dependencies: [ /// 添加其它依赖
.package(url: "https://github.com/vapor/leaf.git", from: "4.0.0"),
],
targets: [
.target(name: "App", dependencies: [ .product(name: "Leaf", package: "leaf") ]),
]
)
import Vapor
import Leaf
设置工作目录
app.directory.workingDirectory = "...."
设置模板目录
app.directory.viewsDirectory = "...."
设置模板引擎
app.views.use(.leaf)
VaporApp
├── Package.swift
├── Resources
│ ├── Views
│ │ └── hello.leaf
├── Public
│ ├── images (images 资源)
│ ├── styles (css 资源)
└── Sources
└── ...
.leaf 文件FileMiddleware 提供静态文件app.middleware.use(FileMiddleware(
publicDirectory:
app.directory.publicDirectory
))
app.get("hello") {
req -> EventLoopFuture<View> in
return req.view.render("hello", [
"name": "Leaf"
])
}
// 或
app.get("hello") {
req async throws -> View in
return try await req.view.render(
"hello", ["name": "Leaf"]
)
}
在 hello.leaf 模板中使用 name
Hello, #(name)!
打开浏览器访问 /hello 显示 Hello, Leaf!。
一个基本的 Leaf 标签使用示例
There are #count(users) users.
可以使用冒号和结束标签为某些标签提供可选的正文。
#:这表示 leaf 解析器开始寻找的标记。count:标签的标识符。users):可以接受零个或多个参数。#(variable)
#extend("template"): 添加到模板中!#endextend
#export("title"): 欢迎使用 Vapor #endexport
#import("body")
#count(friends)
#for(friend in friends):
<li>#(friend.name)</li>
#endfor
+%>==||#if(1 + 1 == 2):
Hello!
#endif
#if(index % 2 == 0):
This is even index.
#else:
This is odd index.
#endif
Leaf 推荐用 Encodable 结构体传数据,数组需包装,[String: Any] 不支持。
struct WelcomeContext: Encodable {
var title: String
var numbers: [Int]
}
return req.view.render("home",
WelcomeContext(
title: "Hello!",
numbers: [42, 9001]
)
)
title 和 numbers 将暴露给 Leaf 模板,就可以在标签中使用这些变量。
<h1>#(title)</h1>
#for(number in numbers):
<p>#(number)</p>
#endfor
变量是否存在
#if(title):
The title is #(title)
#endif
比较
#if(title == "Welcome"):
This is a friendly web page.
#endif
使用另一个标签作为判断条件的一部分,内部标签应该省略 #
#if(count(users) > 0):
You have users!
#else:
There are no users yet :(
#endif
#if(title == "Welcome"):
Hello new user!
#elseif(title == "Welcome back!"):
Hello old user
#else:
Unexpected page!
#endif
struct SolarSystem: Codable {
let planets = ["Venus", "Earth", "Mars"]
}
return req.view.render(
"solarSystem", SolarSystem()
)
在 Leaf 中循环它们:
<ul>
#for(planet in planets):
<li>#(planet)</li>
#endfor
</ul>
入口页面,通过 #extend("main") 将 mainleaf 模板的内容复制到当前模板中使用
#extend("main"):
#export("body"):
<p>Welcome to Vapor!</p>
#endexport
#endextend
在公共模板 main.leaf 中
<html>
<head>
<title>#(name)</title>
</head>
<body>#import("body")</body>
</html>
呈现如下内容:
<html>
<head>
<title>Leaf</title>
</head>
<body>
<p>Welcome to Vapor!</p>
</body>
</html>
在模板中使用 #export 存储名为 body 的一些 HTML
#export("body"):
<p>Welcome to Vapor!</p>
#endexport
使用 #import 获取传递给 #extend 标签的内容
<body>
#import("body")
</body>
Your search matched #count(matches) pages.
#count 标签返回数组中的项目数量
#lowercased(name)
#lowercased 标签将字符串转成小写字母。
#capitalized(name)
#capitalized 标签将字符串中每个单词的首字母大写,其他字母小写。
#if(contains(planets, "Earth")):
Earth is here!
#else:
Earth is not in this array.
#endif
#contains 标签接受一个数组和一个值作为其两个参数,如果参数一中的数组包含参数二中的值,则返回 true。
#date 标签将日期格式化为可读的字符串。默认情况下,它使用 ISO8601 格式。
render(..., ["now": Date()])
模板中使用
The time is #date(now)
你可以传自定义日期格式作为第二参数,详见 Swift DateFormatter。
The date is #date(now, "yyyy-MM-dd")
标签就像一个变量标签 - 例如 #(variable)。
The time is #unsafeHTML(styledTitle)
它不会转义任何 variable 可能包含的 HTML 标签
#dumpContext 标签将整个上下文渲染为可读的字符串。使用此标记来调试作为上下文提供给当前渲染的内容。
Hello, world!
#dumpContext
创建一个名为 NowTag 的类并遵循 LeafTag 协议
struct NowTag: LeafTag {
func render(_ ctx: LeafContext) throws -> LeafData {
...
}
}
实现 render(_:) 方法。传递给该方法的 LeafContext 参数包含了我们需要的所有内容。
enum NowTagError: Error {
case invalidFormatParameter
case tooManyParameters
}
struct NowTag: LeafTag {
func render(_ ctx: LeafContext) throws -> LeafData {
let formatter = DateFormatter()
switch ctx.parameters.count {
case 0: formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
case 1:
guard let string = ctx.parameters[0].string else {
throw NowTagError.invalidFormatParameter
}
formatter.dateFormat = string
default:
throw NowTagError.tooManyParameters
}
let dateAsString = formatter.string(from: Date())
return LeafData.string(dateAsString)
}
}
实现了 NowTag,告诉 Leaf 并设置标签名称为 #now
app.leaf.tags["now"] = NowTag()
现在可以在 Leaf 中使用我们的自定义标签 #now 了
The time is #now()
parameters: 包含标签参数的数组struct NowTag: LeafTag {
func render(
_ ctx: LeafContext
) throws -> LeafData {
/// ctx.parameters
}
}
render(_:_:) 方法作为上下文视图的数据return try await req.view.render(
"home", ["name": "John"]
)
struct NowTag: LeafTag {
func render(
_ ctx: LeafContext
) throws -> LeafData {
let name = ctx.data["name"]?.string
}
}
LeafContext 包含两个重要的属性