PlotVapor 是一个小型的软件包,允许在 Vapor 服务器端 Swift Web 框架中轻松渲染 Plot 生成的 HTML。
此软件包仅添加了一个小型桥接层,使 Plot
可以连接到 Vapor
,这使得此库非常轻量。该库的使用方式与 LeafKit 的 LeafRender 类类似,以增加熟悉度。
⚠️ 此库中的代码按原样提供,主要用作教育目的的参考。 它可能缺乏在生产应用程序中支持使用的文档、稳定性以及/或者功能。 因此,我强烈建议直接从该代码库复制文件,而不是将其作为依赖项引入,以避免任何重大更改影响您的工作流程。如果您有任何问题或反馈,请随时提出 issue/PR 或与我联系。
在继续之前,您应该阅读 Plot README,并对该库的工作原理有深入的了解。
我们可以在 configureRoutes(_:)
方法中为 /home
路径添加一个新路由,并渲染一个不执行任何复杂操作的示例 HTML
对象。
func configureRoutes(_ app: Application) throws {
app.get("home") { req -> EventLoopFuture<View> in
let html = HTML(
.head(
.title("My website"),
.stylesheet("styles.css")
),
.body(
.div(
.h1("Hello, world!"),
.p("Writing HTML in Swift is pretty great!")
)
)
)
return req.plot.render(html)
}
}
由于实际上没有人希望拥有一个巨大的路由文件,因此该库还提供了 Page
和 PageTemplate
协议,以帮助保持代码库的组织性和可维护性。
遵循 Page
协议,可以快速轻松地定义页面。
struct MyPage: Page {
let title = "My website"
var content: Component {
Div {
H1("Hello, world!")
Paragraph("Writing HTML in Swift is pretty great!")
}
}
}
然后,在定义路由的任何地方渲染它。
func configureRoutes(_ app: Application) throws {
app.get("home") { req -> EventLoopFuture<View> in
return req.plot.render(MyPage())
}
}
上面的例子只会渲染一个没有样式的简单页面,这在大多数情况下是不切实际的。 因此,当您不可避免地需要修改 <head>
元素时,只需覆盖 Page
的 head
属性,如下所示。
以下示例将渲染与本 README 中的第一个代码段相同的 HTML 页面。
struct MyPage: Page {
let title = "My website"
var head: Node<HTML.DocumentContext> {
.head(
.title(self.title),
.stylesheet("styles.css")
)
}
var content: Component {
Div {
H1("Hello, world!")
Paragraph("Writing HTML in Swift is pretty great!")
}
}
}
如果需要添加 <script>
元素等,您也可以直接覆盖 body
属性。 该属性的默认实现如下所示。
struct MyPage: Page {
...
var body: Node<HTML.DocumentContext> {
.body(
.component(page.content)
)
}
...
}
⚠️ head
、body
和content
属性以不同的方式处理的原因是,在编写本文时,Plot
没有为<head>
和<body>
元素提供Component
语法的用法。 有关更多信息,请参见Plot
README 的 Components 部分。
您可能希望在您的网站上包含多个页面,并且您不希望为您创建的每个页面覆盖 head
和 body
属性,因为这会导致代码重复且难以维护。
相反,您可以简单地创建一个可重用的 PageTemplate
。 以下 DefaultPageTemplate
代码段包含在 PlotVapor
中,主要用作示例。
public struct DefaultPageTemplate: PageTemplate {
public static func head(with page: Page) -> Node<HTML.DocumentContext> {
.head(
.title(page.title)
)
}
public static func body(with page: Page) -> Node<HTML.DocumentContext> {
.body(
.component(page.content)
)
}
}
然后,您可以遵循 TemplatedPage
而不是遵循 Page
,并像这样定义 Template
typealias
。
struct MyPage: TemplatedPage {
typealias Template = DefaultPageTemplate
...
}
为了更加方便,您可以像这样定义一个全局默认模板。 这样,您只需要为使用不同模板的页面覆盖 Template
typealias
。
extension TemplatedPage {
typealias Template = MyPageTemplate
}
默认情况下,所有 HTML
在渲染之前都会被最小化。 要修改此行为,请使用 indentedBy
参数传递 Indentation.Kind
。
func configureRoutes(_ app: Application) throws {
app.get("home") { req -> EventLoopFuture<View> in
let html = HTML(
// elements, etc.
)
return req.plot.render(html, indentedBy: .spaces(2))
}
}
PlotVapor
还与 Swift 5.5 的新 async/await 功能兼容。
func configureRoutes(_ app: Application) throws {
app.get("home") { req async throws -> View in
let html = HTML(
// elements, etc.
)
return req.plot.render(html, indentedBy: .spaces(2))
}
}
有关在 Vapor 中使用 Swift 并发性的更多信息,请查看 Vapor 文档。