Handlebars.Net 2.1.6

前缀已保留
dotnet add package Handlebars.Net --version 2.1.6                
NuGet\Install-Package Handlebars.Net -Version 2.1.6                
此命令旨在在 Visual Studio 的包管理器控制台中使用,因为它使用了 NuGet 模块的 Install-Package 版本。
<PackageReference Include="Handlebars.Net" Version="2.1.6" />                
对于支持 PackageReference 的项目,将此 XML 节点复制到项目文件中以便引用此包。
paket add Handlebars.Net --version 2.1.6                
#r "nuget: Handlebars.Net, 2.1.6"                
#r 指令可用于 F# Interactive 和 Polyglot Notebooks。将此复制到交互式工具或脚本源代码中以便引用包。
// Install Handlebars.Net as a Cake Addin
#addin nuget:?package=Handlebars.Net&version=2.1.6

// Install Handlebars.Net as a Cake Tool
#tool nuget:?package=Handlebars.Net&version=2.1.6                

Handlebars.Net

CI Nuget performance

Quality Gate Status Reliability Rating Security Rating

Bugs Code Smells Coverage


Stack Exchange questions GitHub issues questions GitHub issues help wanted


在您的 .NET 应用程序中快速执行 Handlebars.js 模板

Handlebars.js 是由 Chris Wanstrath 创建的 Mustache 模板语言的扩展。Handlebars.js 和 Mustache 都是逻辑无关的模板语言,它们像我们所有人都知道的那样将视图和代码分开。

查看 handlebars.js 文档 了解如何编写 Handlebars 模板。

Handlebars.Net 不使用脚本引擎来运行 JavaScript 库 - 它 将 Handlebars 模板直接编译为 IL 字节码。它还尽可能地模拟 JS 库的 API。

安装

dotnet add package Handlebars.Net

扩展

以下项目扩展了 Handlebars.Net

用法

string source =
@"<div class=""entry"">
  <h1>{{title}}</h1>
  <div class=""body"">
    {{body}}
  </div>
</div>";

var template = Handlebars.Compile(source);

var data = new {
    title = "My new post",
    body = "This is my first post!"
};

var result = template(data);

/* Would render:
<div class="entry">
  <h1>My New Post</h1>
  <div class="body">
    This is my first post!
  </div>
</div>
*/

注册部分视图

string source =
@"<h2>Names</h2>
{{#names}}
  {{> user}}
{{/names}}";

string partialSource =
@"<strong>{{name}}</strong>";

Handlebars.RegisterTemplate("user", partialSource);

var template = Handlebars.Compile(source);

var data = new {
  names = new [] {
    new {
        name = "Karen"
    },
    new {
        name = "Jon"
    }
  }
};

var result = template(data);

/* Would render:
<h2>Names</h2>
  <strong>Karen</strong>
  <strong>Jon</strong>
*/

注册辅助函数

Handlebars.RegisterHelper("link_to", (writer, context, parameters) => 
{
    writer.WriteSafeString($"<a href='{context["url"]}'>{context["text"]}</a>");
});

string source = @"Click here: {{link_to}}";

var template = Handlebars.Compile(source);

var data = new {
    url = "https://github.com/rexm/handlebars.net",
    text = "Handlebars.Net"
};

var result = template(data);

/* Would render:
Click here: <a href='https://github.com/rexm/handlebars.net'>Handlebars.Net</a>
*/

此操作将期望你的视图位于 /Views 文件夹中,如下所示:

Views\layout.hbs                |<--shared as in \Views            
Views\partials\somepartial.hbs   <--shared as in  \Views\partials
Views\{Controller}\{Action}.hbs 
Views\{Controller}\{Action}\partials\somepartial.hbs 

注册区块辅助函数

Handlebars.RegisterHelper("StringEqualityBlockHelper", (output, options, context, arguments) => 
{
    if (arguments.Length != 2)
    {
        throw new HandlebarsException("{{#StringEqualityBlockHelper}} helper must have exactly two arguments");
    }

    var left = arguments.At<string>(0);
    var right = arguments[1] as string;
    if (left == right) options.Template(output, context);
    else options.Inverse(output, context);
});

var animals = new Dictionary<string, string>() 
{
	{"Fluffy", "cat" },
	{"Fido", "dog" },
	{"Chewy", "hamster" }
};

var template = "{{#each this}}The animal, {{@key}}, {{#StringEqualityBlockHelper @value 'dog'}}is a dog{{else}}is not a dog{{/StringEqualityBlockHelper}}.\r\n{{/each}}";
var compiledTemplate = Handlebars.Compile(template);
string templateOutput = compiledTemplate(animals);

/* Would render
The animal, Fluffy, is not a dog.
The animal, Fido, is a dog.
The animal, Chewy, is not a dog.
*/

注册装饰器

[Fact]
public void BasicDecorator(IHandlebars handlebars)
{
    string source = "{{#block @value-from-decorator}}{{*decorator 42}}{{@value}}{{/block}}";

    var handlebars = Handlebars.Create();
    handlebars.RegisterHelper("block", (output, options, context, arguments) =>
    {
        options.Data.CreateProperty("value", arguments[0], out _);
        options.Template(output, context);
    });
    
    handlebars.RegisterDecorator("decorator", 
        (TemplateDelegate function, in DecoratorOptions options, in Context context, in Arguments arguments) =>
    {
        options.Data.CreateProperty("value-from-decorator", arguments[0], out _);
    });
    
    var template = handlebars.Compile(source);
    
    var result = template(null);
    Assert.Equal("42", result);
}

更多示例请参阅 DecoratorTests.cs

已知限制
  • 在装饰器内注册的辅助函数不会覆盖现有注册

注册自定义值格式化

如果你需要应用自定义值格式化(例如 DateTime),可以使用 IFormatterIFormatterProvider 接口

public sealed class CustomDateTimeFormatter : IFormatter, IFormatterProvider
{
    private readonly string _format;

    public CustomDateTimeFormatter(string format) => _format = format;

    public void Format<T>(T value, in EncodedTextWriter writer)
    {
        if(!(value is DateTime dateTime)) 
            throw new ArgumentException("supposed to be DateTime");
        
        writer.Write($"{dateTime.ToString(_format)}");
    }

    public bool TryCreateFormatter(Type type, out IFormatter formatter)
    {
        if (type != typeof(DateTime))
        {
            formatter = null;
            return false;
        }

        formatter = this;
        return true;
    }
}

[Fact]
public void DateTimeFormatter(IHandlebars handlebars)
{
    var source = "{{now}}";

    var format = "d";
    var formatter = new CustomDateTimeFormatter(format);
    handlebars.Configuration.FormatterProviders.Add(formatter);

    var template = handlebars.Compile(source);
    var data = new
    {
        now = DateTime.Now
    };
    
    var result = template(data);
    Assert.Equal(data.now.ToString(format), result);
}
备注
  • 格式化程序根据注册顺序反向解析。如果有多个提供程序可以提供类型格式化程序,则最后一个注册的会被使用。

共享环境

默认情况下,Handlebars 将为每个编译模板创建一个独立的副本环境。这样做是为了防止从另一个模板中更改一个模板的行为。

不幸的是,如果运行时有大量的编译模板(无论模板大小如何),它可能对内存占用很大。这可以通过使用 SharedEnvironment 解决。

SharedEnvironment 中编译的模板将共享相同的配置。

限制

已创建共享环境后,只能更改运行时配置属性。对 Configuration.CompileTimeConfiguration 和其他编译时属性的更改将没有任何效果。

示例
[Fact]
public void BasicSharedEnvironment()
{
    var handlebars = Handlebars.CreateSharedEnvironment();
    handlebars.RegisterHelper("registerLateHelper", 
        (in EncodedTextWriter writer, in HelperOptions options, in Context context, in Arguments arguments) =>
        {
            var configuration = options.Frame
                .GetType()
                .GetProperty("Configuration", BindingFlags.Instance | BindingFlags.NonPublic)?
                .GetValue(options.Frame) as ICompiledHandlebarsConfiguration;
            
            var helpers = configuration?.Helpers;

            const string name = "lateHelper";
            if (helpers?.TryGetValue(name, out var @ref) ?? false)
            {
                @ref.Value = new DelegateReturnHelperDescriptor(name, (c, a) => 42);
            }
        });
    
    var _0_template = "{{registerLateHelper}}";
    var _0 = handlebars.Compile(_0_template);
    var _1_template = "{{lateHelper}}";
    var _1 = handlebars.Compile(_1_template);
    
    var result = _1(null);
    Assert.Equal("", result); // `lateHelper` is not registered yet

    _0(null);
    result = _1(null);
    Assert.Equal("42", result);
}

兼容性功能开关

兼容性功能开关定义了一组控制编译/渲染行为的设置。每个设置都会启用某些特性,这可能会破坏与标准 Handlebars 的兼容性。默认情况下,所有开关都设置为 false

传说
  • 区域
    • 编译时间:在模板编译时产生影响
    • 运行时:在模板渲染时产生影响
RelaxedHelperNaming

如果为 true,则启用对 Handlebars.Net 辅助函数命名规则的支持。这允许辅助函数名称不是有效的 Handlebars 标识符(例如 {{ one.two }})。这种命名在 Handlebarsjs 中不受支持,将会破坏兼容性。

区域
  • 编译时间
示例
[Fact]
public void HelperWithDotSeparatedName()
{
    var source = "{{ one.two }}";
    var handlebars = Handlebars.Create();
    handlebars.Configuration.Compatibility.RelaxedHelperNaming = true;
    handlebars.RegisterHelper("one.two", (context, arguments) => 42);

    var template = handlebars.Compile(source);
    var actual = template(null);
    
    Assert.Equal("42", actual);
}
HtmlEncoder

用于在旧版 Handlebars.Net 和标准 Handlebars 规则(或自定义实现)之间切换。
对于 Handlebars.Net 2.x.x,HtmlEncoderLegacy 是默认的。

HtmlEncoder
实现标准 Handlebars 规则。

HtmlEncoderLegacy
不会编码
=(等于)
`(反引号)
'(单引号)

将编码非 ASCII 字符 ,...
为 HTML 实体(&lt;&#226;&#223;,...)。

区域
  • 运行时
示例
[Fact]
public void UseCanonicalHtmlEncodingRules()
{
    var handlebars = Handlebars.Create();
    handlebars.Configuration.TextEncoder = new HtmlEncoder();

    var source = "{{Text}}";
    var value = new { Text = "< �" };

    var template = handlebars.Compile(source);
    var actual = template(value);
            
    Assert.Equal("&lt; �", actual);
}

性能

编译

与渲染相比,编译是一个相当密集的过程。虽然两者都是在毫秒级别测量的,但编译所占用的时间最多。因此,通常最好是只编译一次并将其缓存的结果函数用于重用的生命周期。

渲染

几乎所有的时间都花在将值与模型解析的例程中。不同类型的对象作为模型使用时具有不同的性能特征。

模型类型
  • 绝对最快的模型是 IDictionary(微秒)。
  • 其次是 POCO(对于平均大小的模板和模型通常需要几毫秒),它使用传统的反射并且相当快。
  • 动态对象上的渲染速度开始变慢(到几十毫秒或更多)。
  • 最慢的(高达几百毫秒或更差)往往是具有自定义类型实现的对象(例如 ICustomTypeDescriptor),这些对象不适用于重量级反射。

未来路线图

待定

贡献

欢迎拉取请求!指南相当直接

  • 只添加 Mustache / Handlebars 规范中已有的功能
  • 避免使用 .NET BCL 之外的外部依赖项
  • 保持跨平台兼容性(.NET/Mono;Windows/OSX/Linux 等)
  • 遵循既定的代码格式
产品 兼容的和其它计算目标框架版本。
.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 netcoreapp1.0 已计算。 netcoreapp1.1 已计算。 netcoreapp2.0 已计算。 netcoreapp2.1 已计算。 netcoreapp2.2 已计算。 netcoreapp3.0 已计算。 netcoreapp3.1 已计算。
.NET标准 netstandard1.3 兼容。 netstandard1.4 已计算。 netstandard1.5 已计算。 netstandard1.6 已计算。 netstandard2.0 兼容。 netstandard2.1 兼容。
.NET框架 net451 兼容。 net452 已计算。 net46 已计算。 net461 已计算。 net462 已计算。 net463 已计算。 net47 已计算。 net471 已计算。 net472 已计算。 net48 已计算。 net481 已计算。
MonoAndroid monoandroid 已计算。
MonoMac monomac 已计算。
MonoTouch monotouch 已计算。
Tizen tizen30 已计算。 tizen40 已计算。 tizen60 已计算。
通用Windows平台 uap 已计算。 uap10.0 已计算。
Xamarin.iOS xamarinios 已计算。
Xamarin.Mac xamarinmac 已计算。
Xamarin.TVOS xamarintvos 已计算。
Xamarin.WatchOS xamarinwatchos 已计算。
兼容的目标框架
包含的目标框架(在包中)
了解有关 目标框架.NET Standard 的更多信息。

NuGet 包 (185)

显示依赖 Handlebars.Net 的前 5 个 NuGet 包

下载
Handlebars.Net.AutoRender

Handlebars.Net.AutoRender 的核心功能

EntityFrameworkCore.Scaffolding.Handlebars

允许使用 Entity Framework Core 工具链和 Handlebars 模板自定义从现有数据库反向工程生成的类。

SyncSoft.App.HandlebarsDotNet

一个适用于 SyncSoft Inc. 的应用框架

Handlebars.Net.Extension.Json

Handlebars.Net 的 System.Text.Json 扩展

Handlebars.Net.Extension.NewtonsoftJson

Handlebars.Net 的 Newtonsoft.Json 扩展

GitHub 仓库 (27)

显示依赖 Handlebars.Net 的前 5 个最受欢迎的 GitHub 仓库

仓库 Stars
microsoft/semantic-kernel
快速轻松地将尖端 LLM 技术集成到您的应用中
bitwarden/server
Bitwarden 基础设施/后端(API、数据库、Docker 等)。
dotnetcore/Util
Util 是一个 .Net 平台下的应用框架,旨在提升中小团队的开发能力,由工具类、分层架构基类、UI 组件,配套代码生成模板,权限等组成。
scriban/scriban
一个快速、强大、安全且轻量级的脚本语言和引擎,用于 .NET
ChangemakerStudios/Papercut-SMTP
Papercut SMTP -- 简单桌面电子邮件服务器
版本 下载 最后更新
2.1.6 1,371,898 4/3/2024
2.1.5 87,121 4/1/2024
2.1.4 9,436,062 3/4/2023
2.1.3 356,392 2/15/2023
2.1.2 7,054,566 4/7/2022
2.1.1 269,448 3/26/2022
2.1.1-beta-1 10,816 3/4/2022
2.1.0 1,411,155 1/24/2022
2.0.10 753,416 12/22/2021
2.0.9 1,289,575 8/1/2021
2.0.8 576,121 6/2/2021
2.0.7 397,656 3/26/2021
2.0.6 29,156 3/21/2021
2.0.5 272,959 3/6/2021
2.0.4 4,145,960 1/7/2021
2.0.3 301,031 12/11/2020
2.0.2 20,679 12/8/2020
2.0.1 2,479 12/7/2020
2.0.0 18,105 12/6/2020
2.0.0-rc-2 2,323 12/2/2020
2.0.0-rc-1 1,676 12/2/2020
2.0.0-preview-2 12,986 11/6/2020
2.0.0-preview-1 2,190 10/26/2020
1.11.5 1,465,441 10/10/2020
1.11.4 3,867 10/10/2020
1.10.1 11,024,055 3/23/2019
1.10.0 7,120 3/22/2019
1.9.5 5,494,652 6/16/2018
1.9.4 96,958 5/26/2018
1.9.3 107,171 5/2/2018
1.9.0 3,180,476 5/16/2017
1.8.0 299,198 10/9/2016
1.7.1 197,690 6/4/2016
1.7.0 14,964 5/20/2016
1.6.8 10,703 5/18/2016
1.6.7 3,862 5/15/2016
1.6.6 2,655 5/14/2016
1.6.4 82,153 2/25/2016
1.6.3 8,162 2/22/2016
1.6.2 2,732 2/22/2016
1.6.1 6,426 2/7/2016
1.6.0 5,843 1/29/2016
1.5.3 5,962 1/26/2016
1.5.2 3,615 1/20/2016
1.5.1 3,630 1/5/2016
1.4.8 6,491 11/27/2015
1.4.6 3,924 11/24/2015
1.4.4 36,867 8/9/2015
1.4.3 2,865 8/8/2015
1.4.2 7,991 8/4/2015
1.4.0 25,927 7/20/2015
1.3.5 3,703 7/10/2015
1.3.3 82,502 5/18/2015
1.3.2 20,197 4/11/2015
1.3.1 2,843 4/2/2015
1.3.0 2,683 3/31/2015
1.2.10 7,413 3/17/2015
1.2.9 27,057 3/6/2015
1.2.8 2,778 2/27/2015
1.2.7 2,643 2/27/2015
1.2.6 6,227 2/15/2015
1.2.5 2,822 2/15/2015
1.2.4 2,841 2/13/2015
1.2.3 2,811 2/9/2015
1.2.2 2,775 2/8/2015
1.2.1 2,714 2/7/2015
1.2.0 3,020 1/18/2015
1.1.7 2,657 1/16/2015
1.1.6 2,621 1/16/2015
1.1.5 2,692 1/14/2015
1.1.4 2,680 1/10/2015
1.1.3 2,619 1/10/2015
1.1.2 2,700 1/9/2015
1.1.1 2,623 1/8/2015
1.1.0 3,033 1/7/2015
1.0.6 2,837 1/2/2015
1.0.5 2,954 1/2/2015
1.0.4 2,860 1/2/2015
1.0.3 2,959 12/20/2014
1.0.2 3,052 11/30/2014
1.0.0-beta9 2,562 6/29/2014
1.0.0-beta8 2,413 6/22/2014
1.0.0-beta6 2,459 6/21/2014
1.0.0-beta5 2,407 6/15/2014
1.0.0-beta4 2,352 5/27/2014
1.0.0-beta3 2,368 5/26/2014
1.0.0-beta2 2,350 5/17/2014
1.0.0-beta18 2,707 11/20/2014
1.0.0-beta17 2,602 11/17/2014
1.0.0-beta16 2,545 10/15/2014
1.0.0-beta12 2,383 9/18/2014
1.0.0-beta11 2,386 8/31/2014
1.0.0-beta1 2,478 5/3/2014
1.0.0-alpha5 2,371 5/13/2014
1.0.0-alpha4 2,401 5/11/2014
1.0.0-alpha2 3,256 5/10/2014