如何在ASP.NET Core 2.0中使用视图组件?
答案
新建一个空项目,修改Startup类并添加MVC服务和中间件:
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
}
 
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
 
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}添加一个模型类:
public class UserInfoViewModel
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string LastLogin { get; set; }
}添加继承自ViewComponent的自定义视图组件:
public class UserInfoViewComponent : ViewComponent
{
    public IViewComponentResult Invoke(int userId)
    {
        var model = new UserInfoViewModel
        {
            UserId = userId,
            UserName = "james@bond.com",
            LastLogin = DateTime.Now.ToString()
        };
        return View(model);
    }
}添加视图组件对应的视图文件(Views/Shared/Components/UserInfo/Default.cshtml):
@using ViewComponents.Models @model UserInfoViewModel <div style="border: 1px dotted blue; margin: 5px"> <h3>User Info</h3> <p>UserId: @Model.UserId</p> <p>UserName: @Model.UserName</p> <p>Last Login: @Model.LastLogin</p> </div>
添加一个控制器和相应的视图文件:
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}@using ViewComponents.Models
 
<div style="border: 1px solid black; margin: 5px">
    <h2>Home/Index</h2>
    @await Component.InvokeAsync("UserInfo", new { UserId = 5 })
</div>现在,我们来看下解决方案的目录层次:
运行,此时页面显示:
讨论
视图组件是渲染到其他视图内部的一种特殊类型。对于重用视图中的公共部分或者将大的视图分隔成小组件都非常有用。
与部分视图不同,视图组件不依赖于控制器。它们有自己的类来控制组件用到的模型以及视图模板来生成最终呈现的HTML/CSS。
我喜欢把它们看作是小型控制器,尽管这并不完全正确,但是有助于我们了解它们的用法。和控制器不同,视图组件不处理HTTP请求,也没有控制器的生命周期,这就意味着在视图组件中不能使用过滤器和模型绑定。
视图组件可以使用依赖注入,这使得它们更加强大和易于测试。
创建
有很多方法创建视图组件,我们会讨论最常用到的选项:
- 创建一个类(放到项目中的任意位置),使其继承自抽象基类ViewComponent。 
- 按照约定,请将类名以ViewComponent结尾。 
- 创建一个返回IViewComponentResult的方法Invoke。 
- 这个方法可以接受任意数量的参数,这些参数会在调用视图组件时传入。 
- 将基类ViewComponent的方法View()作为Invoke的返回值,你也可以向其中传入自定义模型。 
- 可选的,你也可以在参数中指定视图页面的名称。 
基类ViewComponent通过属性公开了很多有用的细节,比如HttpContext,RouteData,IUrlHelper,IPrincipal和ViewData。
调用
可以通过两种方式调用视图组件:
- 在视图页面中调用@await Component.InvokeAsync(component, parameters) 
- 在控制器方法中,返回ViewComponent(component, parameters) 
上面调用中的component是一个指向组件类的字符串。
当调用视图组件时,InvokeAsync()方法可以接受任意数量的参数,这些参数可以通过匿名对象传入。
发现
ASP.NET Core会按照如下顺序查找视图组件的Razor页面:
- Views/[controller]/Components/[component]/[view].cshtml 
- Views/Shared/Components/[component]/[view].cshtml 
其中[component]匹配如下规则:
- 除去ViewComponent后缀的组件名。 
- 视图组件类上面[ViewComponent]特性所指定的值 
其中[view]缺省值是Default,当然我们也可以在视图组件的Invoke方法中返回特定的值。比如下面的组件指定视图名称为MyInfo.cshtml:
public IViewComponentResult Invoke(int userId)
{
    var model = new UserInfoViewModel
    {
        UserId = userId,
        UserName = "james@bond.com",
        LastLogin = DateTime.Now.ToString()
    };
    return View("MyInfo", model);
}本例中,如果我们删除视图组件对应的Razor页面,此时会有异常,从而我们能够清楚看到系统的查找顺序: