Skip to content
On this page

视图引擎

关于视图引擎

视图引擎负责根据视图模板创建 HTML。视图通常是 HTML 和编程语言的某种混合。支持变量定义、方法调用及逻辑编写。

Penkar 框架中,底层集成了微软提供的 Razor 视图引擎组件并提供更加灵活方便的语法糖。

视图引擎作用

  • 支持 ASP.NET Core 完整的 Razor 语法
  • 根据不同的数据编译模板产生不同的输出
  • 实现强大的插件化机制
  • 实现全站页面静态化
  • 可以用作邮件模板、短信模板、优惠券信息模板等

基础使用

注册服务

使用之前需在 Startup.cs 中注册 视图引擎服务

cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddViewEngine();
}

使用方式

  • 构造函数注入 IViewEngine
cs
using Penkar.DynamicApiController;
using Penkar.ViewEngine;

namespace Penkar.Application
{
    public class ViewEngineService : IDynamicApiController
    {
        private readonly IViewEngine _viewEngine;
        public ViewEngineService(IViewEngine viewEngine)
        {
            _viewEngine = viewEngine;
            var result = _viewEngine.RunCompile("Hello @Model.Name", new { Name = "Penkar" });
        }
    }
}
  • 字符串方式
cs
var result = "Hello @Model.Name".RunCompile(new { Name = "Penkar" });

弱类型模板

cs
var result = _viewEngine.RunCompile("Hello @Model.Name", new { Name = "Penkar" });

结果:

html
Hello Penkar

支持异步 RunCompileAsync

强类型模板

  • 类型定义
cs
namespace YourProject;  // 支持无命名空间写法

public class TestModel
{
    public string Name { get; set; }
    public int[] Items { get; set; }
}
  • 使用强类型
cs
var result = _viewEngine.RunCompile(@"
Hello @Model.Name
@foreach(var item in Model.Items)
{
    <p>@item</p>
}
", new TestModel    // 支持匿名类型
{
    Name = "Penkar",
    Items = new[] { 3, 1, 2 }
});

结果:

html
Hello Penkar
<p>3</p>
<p>1</p>
<p>2</p>

支持异步 RunCompileAsync

高性能模板缓存 🥇

由于模板编译需要消耗大量的性能,所以建议使用带 FromCached 结尾的 RunCompileFromCached 替代。调用该方法后会自动将模板编译成 .dll 以便下次使用。减少第二次之后使用模板的性能损耗。

如,强类型模板:

cs
var result = _viewEngine.RunCompileFromCached(@"
Hello @Model.Name
@foreach(var item in Model.Items)
{
    <p>@item</p>
}
", new TestModel    // 支持匿名类型
{
    Name = "Penkar",
    Items = new[] { 3, 1, 2 }
});

结果:

html
Hello Penkar
<p>3</p>
<p>1</p>
<p>2</p>

调用 RunCompileFromCached 方法之后将会使用 MD5 加密模板并生成 MD5字符串的 .dll 存放在网站根目录下的 templates 目录中。只要模板内容不变,数据发生改变也不会重新编译模板。这样大大的提高了首次之后的性能。

如,传入新的数据:

cs
var result = _viewEngine.RunCompileFromCached(@"
Hello @Model.Name
@foreach(var item in Model.Items)
{
    <p>@item</p>
}
", new TestModel    // 支持匿名类型
{
    Name = "Penkar",
    Items = new[] { 5,6,7,8 }
});

结果:

html
Hello Penkar
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>

模板不再重新编译,只是重新替换数据。

高级用法

高级用法支持将特定程序集、特定命名空间、特定类型引入到模板中使用。

添加程序集

比如这里添加 System.IO 程序集:

cs
var result = _viewEngine.RunCompileFromCached(@"<div>@System.IO.Path.Combine(""Penkar"", ""ViewEngine"")</div>", builderAction: builder =>
            {
                builder.AddAssemblyReferenceByName("System.IO");
            });

结果:

html
<div>Penkar\\ViewEngine</div>

另外,Penkar 提供多种方式加载程序集:

cs
builder.AddAssemblyReferenceByName("System.Security"); // 通过名称
builder.AddAssemblyReference(typeof(System.IO.File)); // 通过类型
builder.AddAssemblyReference(Assembly.Load("source")); // 通过元数据引用

添加命名空间

cs
var result = _viewEngine.RunCompileFromCached(@"<div>@Path.Combine(""Penkar"", ""ViewEngine"")</div>", builderAction: builder =>
            {
                builder.AddUsing("System.IO");
                builder.AddAssemblyReferenceByName("System.IO");
            });

结果:

html
<div>Penkar\\ViewEngine</div>

也支持加入多个 using

builder.AddUsing("System.IO");
builder.AddUsing("Penkar");

定义模板方法

cs
var result = _viewEngine.RunCompileFromCached(@"
<area>
    @{ RecursionTest(3); }
</area>

@{
  void RecursionTest(int level)
  {
 if (level <= 0)
 {
  return;
 }

 <div>LEVEL: @level</div>
 @{ RecursionTest(level - 1); }
  }
}
");

结果:

html
<area>
<div>LEVEL: 3</div>
<div>LEVEL: 2</div>
<div>LEVEL: 1</div>
</area>

调用类方法

定义 CustomModel 类并继承 ViewEngineModel 基类

cs
public class CustomModel : ViewEngineModel
{
    public int A { get; set; }
    public string B { get; set; }

    public string Decorator(object value)
    {
        return "-=" + value + "=-";
    }
}

在模板中调用 Decorator(value) 方法:

cs
var content = @"Hello @A, @B, @Decorator(123)";

var template = _viewEngine.Compile<CustomModel>(content);

var result = template.Run(instance =>
{
    instance.A = 10;
    instance.B = "Alex";
});

结果:

html
Hello 10, Alex, -=123=-

IViewEngine 接口

IViewEngine 提供了简单方便的 RunCompile 方法,也提供了最原始化的 CompileRun 方法。

通过原始的 CompileRun 方法可以实现很多复杂的逻辑和自定义指令集。

cs
/// <summary>
/// 编译模板
/// </summary>
/// <param name="content"></param>
/// <param name="builderAction"></param>
/// <returns></returns>
IViewEngineTemplate Compile(string content, Action<IViewEngineOptionsBuilder> builderAction = null);
/// <summary>
/// 编译模板
/// </summary>
/// <param name="content"></param>
/// <param name="builderAction"></param>
/// <returns></returns>
Task<IViewEngineTemplate> CompileAsync(string content, Action<IViewEngineOptionsBuilder> builderAction = null);
/// <summary>
/// 编译模板
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="content"></param>
/// <param name="builderAction"></param>
/// <returns></returns>
IViewEngineTemplate<T> Compile<T>(string content, Action<IViewEngineOptionsBuilder> builderAction = null)
    where T : IViewEngineModel;
/// <summary>
/// 编译模板
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="content"></param>
/// <param name="builderAction"></param>
/// <returns></returns>
Task<IViewEngineTemplate<T>> CompileAsync<T>(string content, Action<IViewEngineOptionsBuilder> builderAction = null)
    where T : IViewEngineModel;

字符串模板替换引擎

Penkar 除了内置视图引擎之外,还支持以下几种模板替换,如:

cs
// 提供数据模板方式
var str = "我叫{name}".Render(new Dictionary{ {"name", "Penkar"} });
var str = "我叫{Name}".Render(new { Name = "Penkar" });
var str = "我叫{Detail.Name}".Render(new { Detail = new { Name = "Furoin" } });

// 从配置读取方式
var str = "我叫#(Penkar:Address)".Render();
json
{
  "Penkar": {
    "Address": "https://furion.baiqian.ltd"
  }
}

格式化规范模板

TP.Wrapper(...) 规范模板,使用如下:

cs
// 生成模板字符串
var template = TP.Wrapper("Penkar 框架", "让 .NET 开发更简单,更通用,更流行。",
    "##作者## 百小僧",
    "##当前版本## v3.5.3",
    "##文档地址## https://furion.baiqian.ltd",
    "##Copyright## Penkar");

Console.WriteLine(template);

日志打印模板如下:

bash
┏━━━━━━━━━━━  Penkar 框架 ━━━━━━━━━━━
  .NET 开发更简单,更通用,更流行。

 作者:        百小僧
 当前版本:     v3.5.3
 文档地址:     https://furion.baiqian.ltd
 Copyright:   Penkar
┗━━━━━━━━━━━  Penkar 框架 ━━━━━━━━━━━

关于属性生成

如果列表项以 ##属性名## 开头,自动生成 属性名: 作为行首且自动等宽对齐。

Penkar 3.9.1 之前版本使用 [属性名] 开头。