HypertextLiteral 是一个 Swift 包,用于生成 HTML、XML 和其他 SGML 方言。
它使用自定义字符串插值,基于上下文追加和转义值,内置了对常见模式的支持,以及一个可扩展的架构来定义您自己的行为。
import HypertextLiteral
let attributes = [
"style": [
"background": "yellow",
"font-weight": "bold"
]
]
let html: HTML = "<span \(attributes)>whoa</span>"
// <span style="background: yellow; font-weight: bold;">whoa</span>
HypertextLiteral 体积小巧,自包含,没有外部依赖项。您可以在几分钟内上手,而无需学习任何新的 API 或领域特定语言 (DSL)。减少与工具的斗争意味着有更多时间生成 Web 内容。
这个项目受到 Mike Bostock(@mbostock)的 Hypertext Literal 的启发,并借鉴了其实现细节。您可以在这里阅读更多相关信息。
在您的 Package.swift
文件中,将 HypertextLiteral 包添加到您的目标依赖项中
import PackageDescription
let package = Package(
name: "YourProject",
dependencies: [
.package(
url: "https://github.com/NSHipster/HypertextLiteral",
from: "0.0.3"
),
]
)
然后运行 swift build
命令来构建您的项目。
超文本字面量会根据插值内容出现的上下文自动对其进行转义。
<
、>
、&
、"
和 '
作为命名的字符引用(例如,<
变为 <
)\"
)<!--
和 -->
)都会被删除为了更好地了解它的实际工作方式,请考虑以下示例
let level: Int = 1
"<h\(level)>Hello, world!</h\(level)>" as HTML
// <h1>Hello, world!</h1>
let elementName: String = "h1"
"<\(elementName)>Hello, world!</\(elementName)>" as HTML
// <h1>Hello, world!</h1>
let startTag: String = "<h1>", endTag: String = "</h1>"
"\(startTag)Hello, world!\(endTag)" as HTML
// <h1>Hello, world!</h1>
对元素名称的部分或全部进行插值可以按预期工作,但对标签本身进行插值会导致字符串的尖括号 (<
和 >
) 被转义。
如果您不希望这种情况发生,例如当您将 HTML 内容嵌入到模板中时,您可以将该内容作为 HTML
值传递,或者使用 unsafeUnescaped
参数标签进行插值。
let startTag: HTML = "<h1>", endTag: HTML = "</h1>"
"\(startTag)Hello, world!\(endTag)" as HTML
// <h1>Hello, world!</h1>
"\(unsafeUnescaped: "<h1>")Hello, world!\(unsafeUnescaped: "</h1>")" as HTML
// <h1>Hello, world!</h1>
注意:使用
unsafeUnescaped
参数标签进行插值会直接追加提供的文字,这可能会导致无效的结果。因此,首选使用HTML
值进行组合。
超文本字面量中的属性可以用或不用引号指定,可以是单引号 ('
) 或双引号 ("
)。
let id: String = "logo
let title: String = #"Swift.org | "Welcome to Swift.org""#
let url = URL(string: "https://swiftlang.cn/")!
#"<a id='\#(logo)' title="\#(title)" href=\#(url)>Swift.org</a>"# as HTML
/* <a id='logo'
title="Swift.org | \"Welcome to Swift.org\""
href="https://swiftlang.cn/">Swift.org</a>
*/
某些属性具有特殊的内置值插值规则。
当您为元素的 class
属性插入字符串数组时,生成的值是空格分隔的列表。
let classNames: [String] = ["alpha", "bravo", "charlie"]
"<div class=\(classNames)>…</div>" as HTML
// <div class="alpha bravo charlie">…</div>
如果您为元素的 style
属性的值插入字典,它会自动转换为 CSS。
let style: [String: Any] = [
"background": "orangered",
"font-weight": 700
]
"<span style=\(style)>Swift</span>" as HTML
// <span style="background: orangered; font-weight: 700;">Swift</span>
布尔值 true
根据属性的不同插值为不同的值。
"""
<input type="text" aria-enabled=\(true)
autocomplete=\(true)
spellcheck=\(true)
translate=\(true) />
""" as HTML
/* <input type="text" aria-enabled="true"
autocomplete="on"
spellcheck="spellcheck"
translate="yes"/> */
您可以通过插入以字符串为键的字典来一次指定多个属性。
let attributes: [String: Any] = [
"id": "primary",
"class": ["alpha", "bravo", "charlie"],
"style": [
"font-size": "larger"
]
]
"<div \(attributes)>…</div>" as HTML
/* <div id="primary"
class="alpha bravo charlie"
style="font-size: larger;">…</div> */
具有通用 aria-
或 data-
前缀的属性可以使用嵌套字典指定。
let attributes: [String: Any] = [
"id": "article",
"aria": [
"role": "article",
],
"data": [
"index": 1,
"count": 3,
]
]
"<section \(attributes)>…</section>" as HTML
/* <section id="article"
aria-role="article"
data-index="1"
data-count="3">…</section> */
除了 HTML 之外,您还可以将超文本字面量用于 XML 和其他 SGML 格式。 以下是如何使用 HypertextLiteral
生成 SVG 文档的示例。
import HypertextLiteral
typealias SVG = HTML
let groupAttributes: [String: Any] = [
"stroke-width": 3,
"stroke": "#FFFFEE"
]
func box(_ rect: CGRect, radius: CGVector = CGVector(dx: 10, dy: 10), attributes: [String: Any] = [:]) -> SVG {
#"""
<rect x=\#(rect.origin.x) y=\#(rect.origin.y)
width=\#(rect.size.width) height=\#(rect.size.height)
rx=\#(radius.dx) ry=\#(radius.dy)
\#(attributes)/>
"""#
}
let svg: SVG = #"""
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>
<g \#(groupAttributes)>
\#(box(CGRect(x: 12, y: 28, width: 60, height: 60), attributes: ["fill": "#F06507"]))
\#(box(CGRect(x: 27, y: 18, width: 55, height: 55), attributes: ["fill": "#F2A02D"]))
\#(box(CGRect(x: 47, y: 11, width: 40, height: 40), attributes: ["fill": "#FEC352"]))
</g>
</svg>
"""#
MIT
Mattt (@mattt)