Hello Blazor:Source Generators生成导航菜单
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hello Blazor:Source Generators生成导航菜单相关的知识,希望对你有一定的参考价值。
前言
最近写了多篇关于Source Generators的文章,发现它确实可以简化我们的部分开发工作。
这不,我又盯上了Blazor。
问题
默认的NavMenu.razor
组件用于显示导航菜单,它的部分代码如下:
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
</ul>
</div>
这也就意味着,如果我们增加一个页面,就要修改一次NavMenu.razor
组件,这当然是不合适的。
实现原理
我们查看obj\\Debug\\net5.0\\Razor\\Pages\\Counter.razor.g.cs
(编译时生成的中间文件),可以看到如下代码:
[Microsoft.AspNetCore.Components.RouteAttribute("/counter")]
public partial class Counter : Microsoft.AspNetCore.Components.ComponentBase
它其实对应源代码里的:
@page "/counter"
也就是说,只要我们遍历所有的Microsoft.AspNetCore.Components.RouteAttribute
,获得路由信息放到List<Menu>
即可。
具体实现代码如下:
[Generator]
public class MenuGenerator : ISourceGenerator
{
private const string MenuClassText = @"
public class Menu
{
public string Route { get; set; }
public string Title { get; set; }
}";
public void Initialize(GeneratorInitializationContext context)
{
}
public void Execute(GeneratorExecutionContext context)
{
context.AddSource("Menu", SourceText.From(MenuClassText, Encoding.UTF8));
var options = (context.Compilation as CSharpCompilation).SyntaxTrees[0].Options as CSharpParseOptions;
var compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(MenuClassText, Encoding.UTF8), options));
var allClasses = compilation.SyntaxTrees.
SelectMany(x => x.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>());
var sourceBuilder = new StringBuilder(@"
using System.Collections.Generic;
namespace MenuGenerator
{
public static class NavHelper
{
public static IEnumerable<Menu> GetMenus(){
return new List<Menu> {");
foreach (var classDeclarationSyntax in allClasses)
{
var routeAttribute = classDeclarationSyntax.AttributeLists.SelectMany(x => x.Attributes).FirstOrDefault(attr => attr.Name.ToString() == "Microsoft.AspNetCore.Components.RouteAttribute");
if (routeAttribute != null)
{
var routeArg = routeAttribute.ArgumentList.Arguments.First();
var routeExpr = routeArg.Expression;
var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree);
var route = semanticModel.GetConstantValue(routeExpr).ToString();
if (route == @"/") continue;
var title = classDeclarationSyntax.Identifier.ToString();
sourceBuilder.Append($@"
new Menu{{ Route = ""{route}"", Title = ""{title}"" }},");
}
}
sourceBuilder.Append(@"
};
}
}
}");
context.AddSource("Mapper", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
}
}
使用示例
修改NavMenu.razor代码如下:
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
@foreach (var menu in MenuGenerator.NavHelper.GetMenus())
{
<li class="nav-item px-3">
<NavLink class="nav-link" href="@menu.Route">
<span class="oi " aria-hidden="true"></span> @menu.Title
</NavLink>
</li>
}
</ul>
</div>
编译后可以看到自动生成的代码:
运行后测试,工作正常,成功!
结论
菜单信息还有许多地方需要扩展,比如顺序、图标、菜单名称等,这些可以通过添加自定义Attribute实现。
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!
以上是关于Hello Blazor:Source Generators生成导航菜单的主要内容,如果未能解决你的问题,请参考以下文章
Hello Blazor:启用深色模式 #yyds干货盘点#
Hello Blazor:(13)查找HTML元素对应.razor文件
Hello Blazor:你必须踩过这5个坑,才算学会部署Blazor WebAssembly到静态网站