使用 Swift 宏编写 HTML。通过全局属性支持 HTMX。
使用 #html(encoding:attributes:innerHTML:)
宏。 宏和默认 HTML 元素的所有参数默认为可选。 默认 HTML 元素由内部宏生成。
#html(
encoding: HTMLEncoding = .string,
attributes: [<global attribute>] = [],
<element specific attribute>: <element specific attribute value>? = nil,
_ innerHTML: CustomStringConvertible...
)
所有默认 HTML 元素都符合 HTMLElement
协议,并包含其相应的元素属性。 可以在初始化元素时声明它们,也可以在初始化后通过直接访问属性变量来更改它们。
用于创建 HTML 元素的默认初始化程序遵循以下语法
<html element name>(
attributes: [<global attribute>] = [],
<element specific attribute>: <value>? = nil,
_ innerHTML: CustomStringConvertible...
)
// <div class="dark"><p>Macros are beautiful</p></div>
#html(
div(attributes: [.class(["dark"])],
p("Macros are beautiful")
)
)
// <a href="https://github.com/RandomHashTags/litleagues" target="_blank"></a>
#html(
a(href: "https://github.com/RandomHashTags/litleagues", target: ._blank)
)
// <input id="funny-number" max="420" min="69" name="funny_number" step="1" type="number" value="69">
#html(
input(
attributes: [.id("funny-number")],
max: 420,
min: 69,
name: "funny_number",
step: 1,
type: .number,
value: "69"
)
)
// html example
let test:String = #html(
html(
body(
div(
attributes: [
.class(["dark-mode", "row"]),
.draggable(.false),
.hidden(.true),
.inputmode(.email),
.title("Hey, you're pretty cool")
],
"Random text",
div(),
a(
div(
abbr()
),
address()
),
div(),
button(disabled: true),
video(autoplay: true, controls: false, preload: .auto, src: "https://github.com/RandomHashTags/litleagues", width: .centimeters(1)),
)
)
)
)
编译后的输出会自动转义仅在编译时已知的破坏源 HTML 的字符。
您还可以使用 #escapeHTML(innerHTML:)
宏来转义编译时已知的数据。
如果您正在处理运行时数据
<string>.escapeHTML(escapeAttributes:)
self
,转义 HTML,并可选择转义属性字符<string>.escapeHTMLAttributes()
self
,仅转义属性字符<string>.escapingHTML(escapeAttributes:)
self
的副本,转义 HTML,并可选择转义属性字符<string>.escapingHTMLAttributes()
self
的副本,仅转义属性字符使用字符串插值。
您会收到一个编译器警告,提示插值可能会引入原始 HTML。
是否禁止显示此警告或使用上述方法之一在运行时转义 HTML 取决于您。
Swift HTMLKit 尝试在编译时使用等效的
StaticString
提升已知的插值,以获得最佳性能。 由于宏扩展是沙盒式的,并且宏参数类型无法使用词法上下文/AST,因此目前受到限制。 这意味着在 html 宏中引用内容不会提升到其预期值。 阅读有关此限制的更多信息。
let string:String = "any string value", integer:Int = -69, float:Float = 3.141592
// ✅ DO
let _:String = #html(p("\(string); \(integer); \(float)"))
let _:String = #html(p("\(string)", "; ", String(describing: integer), "; ", float.description))
let integer_string:String = String(describing: integer), float_string:String = String(describing: float)
let _:String = #html(p(string, "; ", integer_string, "; ", float_string))
// ❌ DON'T; compiler error; compile time value cannot contain interpolation
let _:StaticString = #html(p("\(string); \(integer); \(float)"))
let _:StaticString = #html(p("\(string)", "; ", String(describing: integer), "; ", float.description))
let _:StaticString = #html(p(string, "; ", integer_string, "; ", float_string))
使用默认的 custom(tag:isVoid:attributes:innerHTML:)
html 元素。
我们想展示 Apple Pay 按钮
#html(
custom(
tag: "apple-pay-button",
isVoid: false,
attributes: [
.custom("buttonstyle", "black"),
.custom("type", "buy"),
.custom("locale", "el-GR")
]
)
)
变成
<apple-pay-button buttonstyle="black" type="buy" locale="el-GR"></apple-pay-button>
使用 .custom(id:value:)
全局属性。
我们想展示 Apple Pay 按钮
#html(
custom(
tag: "apple-pay-button",
isVoid: false,
attributes: [
.custom("buttonstyle", "black"),
.custom("type", "buy"),
.custom("locale", "el-GR")
]
)
)
变成
<apple-pay-button buttonstyle="black" type="buy" locale="el-GR"></apple-pay-button>
警告
内联事件处理程序是一种过时的事件处理方式。
普遍认为这是一种“不良做法”,您不应该将 HTML 和 JavaScript 混合在一起。
此功能仍然已被弃用,以鼓励使用其他技术。
使用 .event(<event type>, "<value>")
全局属性。
#html(
div(
attributes: [
.event(.click, "doThing()"),
.event(.change, "doAnotherThing()")
]
)
)
在 #html
宏中声明您想要的编码。
#html(
encoding: HTMLEncoding = .<type>
)
string
-> String
/StaticString
utf8Bytes
-> [UInt8]
utf16Bytes
-> [UInt16]
utf8CString
-> ContiguousArray<CChar>
foundationData
-> Foundation.Data
import Foundation
才能使用它!byteBuffer
-> NIOCore.ByteBuffer
import NIOCore
才能使用它! Swift HTMLKit 不依赖于 swift-nio
!custom("<encoding logic>")
-> 符合 CustomStringConvertible
的自定义类型$0
引用编译后的 HTML(作为没有分隔符的字符串)使用 .htmx(<htmx attribute>)
全局属性。 支持所有 HTMX 2.0 属性(包括服务器发送事件和 Web 套接字)。
// <div hx-boost="true"></div>
var string:StaticString = #html(div(attributes: [.htmx(.boost(.true))]))
// <div hx-get="/test"></div>
string = #html(div(attributes: [.htmx(.get("/test"))]))
// <div hx-on::abort="bruh()"></div>
string = #html(div(attributes: [.htmx(.on(.abort, "bruh()"))]))
// <div hx-on::after-on-load="test()"></div>
string = #html(div(attributes: [.htmx(.on(.afterOnLoad, "test()"))]))
// <div hx-on:click="thing()"></div>
string = #html(div(attributes: [.htmx(.onevent(.click, "thing()"))]))
// <div hx-preserve></div>
string = #html(div(attributes: [.htmx(.preserve(true))]))
// <div sse-connect="/connect"></div>
string = #html(div(attributes: [.htmx(.sse(.connect("/connect")))]))
// <div ws-connect="/chatroom"></div>
string = #html(div(attributes: [.htmx(.ws(.connect("/chatroom")))]))
// <div hx-ext="ws" ws-send></div>
string = #html(div(attributes: [.htmx(.ext("ws")), .htmx(.ws(.send(true)))]))
测试机器
基准测试命令: swift package --allow-writing-to-package-directory benchmark --target Benchmarks --metric throughput --format jmh
此库在性能和效率方面明显领先。静态网页提供最佳性能,而动态页面仍然名列前茅(我正在积极研究和测试动态页面的改进)。
欢迎大家贡献。有关最佳实践,请参阅 CONTRIBUTIONS.md。