Falco.Markup 1.1.1
dotnet add package Falco.Markup --version 1.1.1
NuGet\Install-Package Falco.Markup -Version 1.1.1
<PackageReference Include="Falco.Markup" Version="1.1.1" />
paket add Falco.Markup --version 1.1.1
#r "nuget: Falco.Markup, 1.1.1"
// Install Falco.Markup as a Cake Addin #addin nuget:?package=Falco.Markup&version=1.1.1 // Install Falco.Markup as a Cake Tool #tool nuget:?package=Falco.Markup&version=1.1.1
Falco.Markup
open Falco.Markup
let doc =
Elem.html [] [
Elem.body [ Attr.class' "100-vh" ] [
Text.h1 "Hello world!" ] ]
renderHtml doc
Falco.Markup 是一个 XML 标记模块,可以用来生成任何形式的尖括号标记(即 HTML、SVG、XML 等)。
关键特性
- 使用原生的 F# 生成任何形式的 尖括号标记。
- 简单创建可重用的代码块(例如,部分视图和组件)。
- 通过创建 自定义标签和属性 来轻松扩展。
- 作为您的程序集的一部分编译,从而提高了 性能 并简化了部署。
- 提供与完全匹配 HTML 规范 的强类型函数。
设计目标
概述
Falco.Markup 被分解为三个主要模块,分别是 Elem
、Attr
和 Text
,它们分别用于生成元素、属性和文本节点。每个模块都包含一系列与各种元素/属性/节点名称对应的函数。也可以扩展以创建自定义元素和属性。
主要元素分为两种类型,ParentNode
或 SelfClosingNode
。
ParentNode
元素是可以包含其他元素的元素。以接收两个输入的函数表示:属性以及可选元素。
let markup =
Elem.div [ Attr.class' "heading" ] [
Text.h1 "Hello world!" ]
SelfClosingNode
元素是自闭合标签。以接收单个输入的函数表示:属性。
let markup =
Elem.div [ Attr.class' "divider" ] [
Elem.hr [] ]
文本使用 TextNode
表示,并通过 Text
模块中的一个函数创建。
let markup =
Elem.div [] [
Text.comment "An HTML comment"
Text.p "A paragraph"
Elem.p [] [ Text.rawf "Hello %s" "Jim" ]
Elem.code [] [ Text.enc "<div>Hello</div>" ] // HTML encodes text before rendering
]
属性也包含两个子类型,即代表键/值属性的 KeyValueAttr
或代表布尔属性的 NonValueAttr
。
let markup =
Elem.input [ Attr.type' "text"; Attr.required ]
大多数 JavaScript 事件 也已在 Attr
模块中映射。所有这些事件都以单词 "on" 为前缀(例如,Attr.onclick
、Attr.onfocus
等)。
let markup =
Elem.button [ Attr.onclick "console.log(\"hello world\")" ] [ Text.raw "Click me" ]
HTML
尽管 Falco.Markup 可以用于生成任何标记。它首先是一个 HTML 库。
组合视图以创建复杂输出
open Falco.Markup
// Components
let divider =
Elem.hr [ Attr.class' "divider" ]
// Template
let master (title : string) (content : XmlNode list) =
Elem.html [ Attr.lang "en" ] [
Elem.head [] [
Elem.title [] [ Text.raw title ]
]
Elem.body [] content
]
// Views
let homeView =
master "Homepage" [
Text.h1 "Homepage"
divider
Text.p "Lorem ipsum dolor sit amet, consectetur adipiscing."
]
let aboutView =
master "About Us" [
Text.h1 "About"
divider
Text.p "Lorem ipsum dolor sit amet, consectetur adipiscing."
]
强类型视图
open Falco.Markup
type Person =
{ FirstName : string
LastName : string }
let doc (person : Person) =
Elem.html [ Attr.lang "en" ] [
Elem.head [] [
Elem.title [] [ Text.raw "Sample App" ]
]
Elem.body [] [
Elem.main [] [
Text.h1 "Sample App"
Text.p $"{person.First} {person.Last}"
]
]
]
表单
表单是 HTML 应用的命脉。以下是一个使用 markup 模块的基本表单示例
let dt = DateTime.Now
Elem.form [ Attr.methodPost; Attr.action "/submit" ] [
Elem.label [ Attr.for' "name" ] [ Text.raw "Name" ]
Elem.input [ Attr.id "name"; Attr.name "name"; Attr.typeText ]
Elem.label [ Attr.for' "birthdate" ] [ Text.raw "Birthday" ]
Elem.input [ Attr.id "birthdate"; Attr.name "birthdate"; Attr.typeDate; Attr.valueDate dt ]
Elem.input [ Attr.typeSubmit ]
]
在此基础上,我们可以创建一个更复杂的表单,涉及多个输入和输入类型,如下所示
Elem.form [ Attr.method "post"; Attr.action "/submit" ] [
Elem.label [ Attr.for' "name" ] [ Text.raw "Name" ]
Elem.input [ Attr.id "name"; Attr.name "name" ]
Elem.label [ Attr.for' "bio" ] [ Text.raw "Bio" ]
Elem.textarea [ Attr.name "id"; Attr.name "bio" ] []
Elem.label [ Attr.for' "hobbies" ] [ Text.raw "Hobbies" ]
Elem.select [ Attr.id "hobbies"; Attr.name "hobbies"; Attr.multiple ] [
Elem.option [ Attr.value "programming" ] [ Text.raw "Programming" ]
Elem.option [ Attr.value "diy" ] [ Text.raw "DIY" ]
Elem.option [ Attr.value "basketball" ] [ Text.raw "Basketball" ]
]
Elem.fieldset [] [
Elem.legend [] [ Text.raw "Do you like chocolate?" ]
Elem.label [] [
Text.raw "Yes"
Elem.input [ Attr.typeRadio; Attr.name "chocolate"; Attr.value "yes" ] ]
Elem.label [] [
Text.raw "No"
Elem.input [ Attr.typeRadio; Attr.name "chocolate"; Attr.value "no" ] ]
]
Elem.fieldset [] [
Elem.legend [] [ Text.raw "Subscribe to our newsletter" ]
Elem.label [] [
Text.raw "Receive updates about product"
Elem.input [ Attr.typeCheckbox; Attr.name "newsletter"; Attr.value "product" ] ]
Elem.label [] [
Text.raw "Receive updates about company"
Elem.input [ Attr.typeCheckbox; Attr.name "newsletter"; Attr.value "company" ] ]
]
Elem.input [ Attr.typeSubmit ]
]
一个简单但有用的 元 元素 Elem.control
可以减少创建表单输出所需的冗余。此表单的外观如下所示
Elem.form [ Attr.method "post"; Attr.action "/submit" ] [
Elem.control "name" [] [ Text.raw "Name" ]
Elem.controlTextarea "bio" [] [ Text.raw "Bio" ] []
Elem.controlSelect "hobbies" [ Attr.multiple ] [ Text.raw "Hobbies" ] [
Elem.option [ Attr.value "programming" ] [ Text.raw "Programming" ]
Elem.option [ Attr.value "diy" ] [ Text.raw "DIY" ]
Elem.option [ Attr.value "basketball" ] [ Text.raw "Basketball" ]
]
Elem.fieldset [] [
Elem.legend [] [ Text.raw "Do you like chocolate?" ]
Elem.control "chocolate" [ Attr.id "chocolate_yes"; Attr.typeRadio ] [ Text.raw "yes" ]
Elem.control "chocolate" [ Attr.id "chocolate_no"; Attr.typeRadio ] [ Text.raw "no" ]
]
Elem.fieldset [] [
Elem.legend [] [ Text.raw "Subscribe to our newsletter" ]
Elem.control "newsletter" [ Attr.id "newsletter_product"; Attr.typeCheckbox ] [ Text.raw "Receive updates about product" ]
Elem.control "newsletter" [ Attr.id "newsletter_company"; Attr.typeCheckbox ] [ Text.raw "Receive updates about company" ]
]
Elem.input [ Attr.typeSubmit ]
]
属性值
在语法复杂性较高的地方之一是 Attr.value
,它期望(与所有 Attr
函数一样)string
输入。存在一些辅助工具可以简化这一点。
let dt = DateTime.Now
Elem.input [ Attr.typeDate; Attr.valueStringf "yyyy-MM-dd" dt ]
// you could also just use:
Elem.input [ Attr.typeDate; Attr.valueDate dt ] // formatted to ISO-8601 yyyy-MM-dd
// or,
Elem.input [ Attr.typeMonth; Attr.valueMonth dt ] // formatted to ISO-8601 yyyy-MM
// or,
Elem.input [ Attr.typeWeek; Attr.valueWeek dt ] // formatted to Gregorian yyyy-W#
// it works for TimeSpan too:
let ts = TimeSpan(12,12,0)
Elem.input [ Attr.typeTime; Attr.valueTime ts ] // formatted to hh:mm
// there is a helper for Option too:
let someTs = Some ts
Elem.input [ Attr.typeTime; Attr.valueOption Attr.valueTime someTs ]
合并属性
markup 模块允许您轻松创建组件,这是在 UI 中减少代码重复的一个很好的方法。为了支持运行时自定义化,建议确保组件(或可重用标记块)保留与标准元素类似的功能 "形状"。即 XmlAttribute list -> XmlNode list -> XmlNode
。
这意味着您不可避免地需要将预定义的 XmlAttribute list
与在运行时提供的列表组合。为此,Attr.merge
函数将按键对属性进行分组,并在增量属性的情况下智能地连接值(例如,class
、style
和 accept
)。
open Falco.Markup
// Components
let heading (attrs : XmlAttribute list) (content : XmlNode list) =
// safely combine the default XmlAttribute list with those provided
// at runtime
let attrs' =
Attr.merge [ Attr.class' "text-large" ] attrs
Elem.div [] [
Elem.h1 [ attrs' ] content
]
// Template
let master (title : string) (content : XmlNode list) =
Elem.html [ Attr.lang "en" ] [
Elem.head [] [
Elem.title [] [ Text.raw title ]
]
Elem.body [] content
]
// Views
let homepage =
master "Homepage" [
heading [ Attr.class' "red" ] [ Text.raw "Welcome to the homepage" ]
Text.p "Lorem ipsum dolor sit amet, consectetur adipiscing."
]
let homepage =
master "About Us" [
heading [ Attr.class' "purple" ] [ Text.raw "This is what we're all about" ]
Text.p "Lorem ipsum dolor sit amet, consectetur adipiscing."
]
自定义元素 & 属性
为了确保 HTML 和 SVG 规范与模块中的函数相匹配,已尽力而为。如果您需要缺少的元素或属性,可以提交一个 问题,或者在项目中扩展该模块更为简单。
创建自定义 XML 元素并使用它们创建结构化 XML 文档的示例
open Falco.Makrup
module Elem =
let books = Elem.create "books"
let book = Elem.create "book"
let name = Elem.create "name"
module Attr =
let soldOut = Attr.createBool "soldOut"
let xmlDoc =
Elem.books [] [
Elem.book [ Attr.soldOut ] [
Elem.name [] [ Text.raw "To Kill A Mockingbird" ]
]
]
let xml = renderXml xmlDoc
SVG
SVG 规范的大部分功能已被映射到元素和属性函数中。还有一个 SVG 模板,可以帮助初始化一个新的绘制,具有有效的视图框。
open Falco.Markup
open Falco.Markup.Svg
// https://mdn.org.cn/en-US/docs/Web/SVG/Element/text#example
let svgDrawing =
Templates.svg (0, 0, 240, 80) [
Elem.style [] [
Text.raw ".small { font: italic 13px sans-serif; }"
Text.raw ".heavy { font: bold 30px sans-serif; }"
Text.raw ".Rrrrr { font: italic 40px serif; fill: red; }"
]
Elem.text [ Attr.x "20"; Attr.y "35"; Attr.class' "small" ] [ Text.raw "My" ]
Elem.text [ Attr.x "40"; Attr.y "35"; Attr.class' "heavy" ] [ Text.raw "cat" ]
Elem.text [ Attr.x "55"; Attr.y "55"; Attr.class' "small" ] [ Text.raw "is" ]
Elem.text [ Attr.x "65"; Attr.y "55"; Attr.class' "Rrrrr" ] [ Text.raw "Grumpy!" ]
]
let svg = renderNode svgDrawing
性能
以下是一个简单的 基准测试 的结果,其中Falco.Markup 与原生的 StringBuilder
使用以及一些其他标记库进行了比较。
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.2604 (21H2)
Intel Core i7-7500U CPU 2.70GHz (Kaby Lake), 1 CPU, 4 logical and 2 physical cores
.NET SDK=7.0.201
[Host] : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT DEBUG
DefaultJob : .NET 6.0.14 (6.0.1423.7309), X64 RyuJIT
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Allocated |
|-------------- |----------:|----------:|----------:|------:|--------:|--------:|----------:|
| StringBuilder | 2.419 us | 0.0481 us | 0.0591 us | 1.00 | 0.00 | 6.6643 | 14 KB |
| Falco | 3.829 us | 0.0338 us | 0.0300 us | 1.58 | 0.04 | 8.1253 | 17 KB |
| Giraffe | 7.402 us | 0.0735 us | 0.0688 us | 3.04 | 0.08 | 9.0027 | 18 KB |
| Scriban | 26.125 us | 0.3734 us | 0.2915 us | 10.73 | 0.38 | 16.5405 | 34 KB |
发现了一个错误?
为此有一个 问题。
许可证
在多伦多由 Pim Brouwers 制作。根据 Apache License 2.0 许可。
产品 | 版本 兼容的和额外的计算目标框架版本。 |
---|---|
.NET | net5.0 已计算。 net5.0-windows 已计算。 net6.0 已计算。 net6.0-android 已计算。 net6.0-ios 已计算。 net6.0-maccatalyst 已计算。 net6.0-macos 已计算。 net6.0-tvos 已计算。 net6.0-windows 已计算。 net7.0 已计算。 net7.0-android 已计算。 net7.0-ios 已计算。 net7.0-maccatalyst 已计算。 net7.0-macos 已计算。 net7.0-tvos 已计算。 net7.0-windows 已计算。 net8.0 已计算。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。 |
.NET Core | netcoreapp2.0 已计算。 netcoreapp2.1 已计算。 netcoreapp2.2 已计算。 netcoreapp3.0 已计算。 netcoreapp3.1 已计算。 |
.NET Standard | netstandard2.0 兼容。 netstandard2.1 已计算。 |
.NET Framework | net461 已计算。 net462 已计算。 net463 已计算。 net47 已计算。 net471 已计算。 net472 已计算。 net48 已计算。 net481 已计算。 |
MonoAndroid | monoandroid 已计算。 |
MonoMac | monomac 已计算。 |
MonoTouch | monotouch 已计算。 |
Tizen | tizen40 已计算。 tizen60 已计算。 |
Xamarin.iOS | xamarinios 已计算。 |
Xamarin.Mac | xamarinmac 已计算。 |
Xamarin.TVOS | xamarintvos 已计算。 |
Xamarin.WatchOS | xamarinwatchos 已计算。 |
-
.NETStandard 2.0
- FSharp.Core (>= 4.5.2)
NuGet 包 (3)
显示依赖 Falco.Markup 的前 3 个 NuGet 包
包 | 下载 |
---|---|
Falco
一款以函数优先的工具包,用于使用 F# 构建出色的 ASP.NET Core 应用程序。 |
|
Fss-lib.Falco
为使用 Fss 和 Falco 视图引擎提供辅助函数。 |
|
Falco.Bulma
包描述 |
GitHub 仓库
此包未用于任何流行的 GitHub 仓库。