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)
é ç½®èªå®ä¹æ ç¾
app.leaf.tags["relative"] = CustomTag()
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
æä»¶å¤¹ä¸ç模æ¿
#extend("partials/detail-layout"):
#export("body"): 详æ
é¡µé¢ #endexport
#endextend
+%>==||#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 == "user" && count(users) > 0):
You have users!
#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"]
)
èªå®ä¹æ ç¾ä½¿ç¨ Data
struct NowTag: LeafTag {
func render(
_ ctx: LeafContext
) throws -> LeafData {
let name = ctx.data["name"]?.string
}
}
LeafContext å
å«ä¸¤ä¸ªéè¦ç屿§
struct RelativePathTag: LeafTag {
func render(_ ctx: LeafContext) throws -> LeafData {
guard ctx.parameters.count == 1, let filename = ctx.parameters[0].string else {
throw "Missing #relative parameters"
}
if let filepath = ctx.request?.url.path, filename.hasPrefix("/") == false {
return .string("\(relativePrefix(for: filepath, targetFile: filename))")
}
return .string("\(filename)")
}
private func relativePrefix(for pagePath: String, targetFile: String) -> String {
var components = pagePath
.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
.split(separator: "/")
if let last = components.last, last.contains(".") {
components = components.dropLast()
}
let cleanTarget = targetFile.hasPrefix("./")
? String(targetFile.dropFirst(2))
: targetFile
return String(repeating: "../", count: components.count) + cleanTarget
}
}
é ç½®æ ç¾
app.leaf.tags["relative"] = RelativePathTag()
ç°å¨å¯ä»¥å¨ Leaf ä¸ä½¿ç¨æä»¬çèªå®ä¹æ ç¾äº
<link rel="stylesheet" href="#relative("main.css")" />