一个现代且高效的 HTML 渲染库 - 灵感来源于 SwiftUI,专为 Web 构建。
struct MainPage: HTMLDocument {
var title = "Elementary"
var head: some HTML {
meta(.name(.description), .content("Typesafe HTML in modern Swift"))
}
var body: some HTML {
main {
h1 { "Features" }
FeatureList(features: [
"HTML in pure Swift",
"SwiftUI-inspired composition",
"Lightweight and fast",
"Framework agnostic and unopinionated",
])
a(.href("https://github.com/sliemeobn/elementary"), .class("fancy-style")) {
"Learn more"
}
}
}
}
struct FeatureList: HTML {
var features: [String]
var content: some HTML {
ul {
for feature in features {
li { feature }
}
}
}
}
将依赖项添加到 Package.swift
.package(url: "https://github.com/sliemeobn/elementary.git", from: "0.3.2")
.product(name: "Elementary", package: "elementary")
与 Hummingbird 集成
.package(url: "https://github.com/hummingbird-community/hummingbird-elementary.git", from: "0.3.0")
.product(name: "HummingbirdElementary", package: "hummingbird-elementary")
与 Vapor 集成
.package(url: "https://github.com/vapor-community/vapor-elementary.git", from: "0.1.0")
.product(name: "VaporElementary", package: "vapor-elementary")
查看 Hummingbird + Tailwind 示例应用。
有关 ElementaryHTMX 的演示,请参阅此 Hummingbird + HTMX 演示。
有关 Vapor 示例,请参阅 Vapor + HTMX 演示。
Elementary 直接渲染为文本,针对从 Hummingbird 或 Vapor 服务器应用提供生成的 HTML 进行了优化。
任何符合 HTML
类型的类型都可以单独渲染,非常适合测试或使用 htmx 发送片段。
默认的渲染机制生成 HTML 块以实现高效的响应流式传输,因此浏览器可以在服务器仍在生成其余部分时开始加载页面。 Swift 并发用于处理背压,因此即使对于大型页面,您的内存占用量也保持较低。
// Stream HTML, optimized for responsiveness and back pressure-aware
try await MainPage().render(into: responseStreamWriter)
或者,您可以简单地将渲染的 HTML 收集到一个字符串中。
let html: String = div(.class("pretty")) { "Hello" }.render()
// <div class="pretty">Hello</div>
let fragment: String = FeatureList(features: ["Anything conforming to HTML can be rendered"]).render()
// <ul><li>Anything conforming to HTML can be rendered</li></ul>
// For testing purposes, there is also a formatted version
print(
div {
p(.class("greeting")) { "Hi mom!" }
p { "Look how pretty." }
}.renderFormatted()
)
// <div>
// <p class="greeting">Hi mom!</p>
// <p>Look how pretty.</p>
// </div>
Elementary 零依赖(甚至不依赖 Foundation),并且不使用运行时反射或存在类型容器(代码库中没有一个 any
)。
按照设计,它不附带布局引擎、响应式状态跟踪或内置 CSS 样式:它只渲染 HTML。
使用受 SwiftUI 启发的组合 API 构建您的 HTML 结构。
struct List: HTML {
var items: [String]
var importantIndex: Int
var content: some HTML {
// conditional rendering
if items.isEmpty {
p { "No items" }
} else {
ul {
// list rendering
for (index, item) in items.enumerated() {
// seamless composition of elements
ListItem(text: item, isImportant: index == importantIndex)
}
}
}
}
}
struct ListItem: HTML {
var text: String
var isImportant: Bool = false
var content: some HTML {
// conditional attributes
li { text }
.attributes(.class("important"), when: isImportant)
}
}
Elementary 利用 Swift 强大的泛型来提供一个属性系统,该系统知道什么属性应该放在哪里。每个元素都知道它用于哪个标签。
与 HTML 中一样,属性紧跟在“开始标签”之后。
// staying close to HTML syntax really helps
div(.data("hello", value: "there")) {
a(.href("/swift"), .target(.blank)) {
img(.src("/swift.png"))
span(.class("fancy")) { "Click Me" }
}
}
属性也可以通过使用修饰符语法来更改,这允许轻松处理条件属性。
div {
p { "Hello" }
.attributes(.id("maybe-fancy"))
.attributes(.class("fancy"), when: isFancy)
}
通过公开 content
的标签类型,属性将向下传递并被正确应用。
struct Button: HTML {
var text: String
// by exposing the HTMLTag type information...
var content: some HTML<HTMLTag.input> {
input(.type(.button), .value(text))
}
}
div {
// ... Button will know it really is an <input> element ...
Button(text: "Hello")
.attributes(.autofocus) // ... and pass on any attributes
}
作为合理的默认设置,class 和 style 属性会被合并(分别使用空格或分号)。所有其他属性默认情况下会被覆盖。
Elementary 在 HTML 内容中支持 Swift 并发。只需在您的 HTML 中 await
某些内容,同时第一个字节已经飞向浏览器。
div {
let text = await getMyData()
p { "This totally works: \(text)" }
MyComponent()
}
struct MyComponent: HTML {
var content: some HTML {
AsyncContent {
"So does this: \(await getMoreData())"
}
}
}
通过使用 AsyncForEach
元素,任何 AsyncSequence
都可以高效地直接渲染为 HTML。
ul {
// the full result never needs to be stored in memory...
let users = try await db.users.findAll()
// ...as each async sequence element...
AsyncForEach(users) { user in
// ...is immediately streamed out as HTML
li { "\(user.name) \(user.favoriteProgrammingLanguage)" }
}
}
Elementary 利用 TaskLocal
来提供轻量级的环境系统。
enum MyValues {
// task-locals act as keys, ...
@TaskLocal static var userName = "Anonymous"
}
struct MyComponent: HTML {
// ... their values can be accessed ...
@Environment(MyValues.$userName) var userName
var content: some HTML {
p { "Hello, \(userName)!" }
}
}
div {
// ... and provided in a familiar way
MyComponent()
.environment(Values.$userName, "Drax the Destroyer")
}
内置属性列表远未完成,但添加它们非常简单(也可以在外部包中完成)。
欢迎提交 PR,添加模型中缺少的其他属性。
Plot、HTMLKit 和 Swim 都是用于执行类似操作的优秀包。
我创建 Elementary 的主要动机是创造类似于这些包的体验(Swift 论坛帖子 了解更多背景信息),但是
any
的列表Tokamak 是一个很棒的项目,非常鼓舞人心。它可以生成 HTML,但它的主要重点是一个非常不同的领域。去看看!
swift-html 和 swift-dom 也能很好地生成 HTML,但它们使用不同的语法来组合 HTML 元素。