_ViewStart.cshtml 布局文件在哪里以及如何链接?

209

这是默认MVC 3模板中的About.cshtml:

@{
    ViewBag.Title = "About Us";
}

<h2>About</h2>
<p>
     Put content here.
</p>
我本以为在 About.cshtml 文件中会有一个对 _ViewStart 文件的引用,但显然没有。
我查看了 global.asaxweb.config,但找不到 About.cshtml 文件是如何与 _ViewStart 文件中的布局相关联的。
一切都按预期工作,我只是想知道底层发生了什么...
7个回答

248
ScottGu的博客
自ASP.NET MVC 3 Beta发布以来,您现在可以在项目的Views文件夹下添加名为_ViewStart.cshtml(或_ViewStart.vbhtml适用于VB)的文件:
_ViewStart文件可用于定义您希望在每个视图呈现开始时执行的常见视图代码。例如,我们可以在_ViewStart.cshtml文件中编写代码,以便将每个视图的Layout属性默认设置为SiteLayout.cshtml文件:
由于此代码在每个视图的开始处执行,因此我们不再需要在任何单独的视图文件中显式设置Layout(除非我们想要覆盖上述默认值)。
重要提示:由于_ViewStart.cshtml允许我们编写代码,因此我们可以选择使我们的布局选择逻辑更加丰富,而不仅仅是基本的属性设置。例如:我们可以根据访问站点的设备类型使用不同的布局模板 - 并为这些设备提供面向电话或平板电脑的优化布局,并为PC /笔记本电脑提供针对桌面的优化布局。或者,如果我们正在构建一个CMS系统或常用的共享应用程序,则可以根据客户(或其角色)在访问站点时选择不同的布局。
这使得UI有了很多灵活性。它还允许您更轻松地编写视图逻辑一次,并避免在多个位置重复使用它。
另请参见这里

17
所以这在MVC3中更或多少是一个“硬编码”的特性?我不需要将其更改为另一个“默认”页面,只是好奇它是如何设置的。谢谢您解决所有问题 :) - Kman
3
Kman- Hardcoded,按照惯例(在这里选择其他“handle”名称 :))-是的,确实如此。很高兴它解除了困惑。 - jim tollan
你可能不仅需要在“Views”文件夹中使用它。如果您添加了自定义RazorViewEngine以将视图组织到其他文件夹中,您还必须在这些备用视图文件夹的根目录中包含该文件。例如,我将所有Inspinia模板视图移动到一个文件夹中,并在视图引擎中运行了以下代码:ViewLocationFormats = ViewLocationFormats.Union(new string[] { "~/Inspinia/ExampleViews/{1}/{0}.cshtml" }).ToArray();。结果,我不得不将我的_ViewStart.cshtml文件的副本添加到“~/Inspinia/ExampleViews”中,否则它不会被拾取,也不会设置任何布局。 - Triynko
2
如果您的Views文件夹有子文件夹,您可以在每个子文件夹中放置一个_ViewStart文件,以链接到该子文件夹中的视图吗? - toddmo

37

从更广义的角度来看,MVC框架“知道”_Viewstart.cshtml的能力被称为“按约定编程”。

约定优于配置(也称为按约定编程)是一种软件设计范例,旨在减少开发人员需要做出的决策数量,从而获得简单性,但不一定失去灵活性。该短语基本上意味着开发人员只需要指定应用程序的非传统方面。例如,如果模型中有一个销售类,则相应的数据库表默认称为“sales”。只有当一个人偏离这个惯例,比如将表称为“products_sold”,才需要编写关于这些名称的代码。

维基百科

这并没有什么神奇的地方。它只是被写入了MVC框架的核心代码库中,因此是MVC“知道”的东西。这就是为什么你在.config文件或其他地方找不到它的原因;它实际上在MVC代码中。但是你可以通过重写来改变或清空这些约定。


14
如果MVC知道这个问题,为什么Visual Studio不知道并指出给我呢?如果按照约定编码意味着只要你没有违反惯例就没问题,那感觉有点糟糕啊... - Arne Evertsson
不打破惯例就是重点。据我所知,Ruby on Rails 也遵循这一范例。 - Umar Farooq Khawaja
1
+1 Raif。没有必要为文档不全的“按约定编码”辩护。我可以对我的任何落后代码说同样的话。“什么?你没想到它到33时会崩溃吗?每个人都知道你跳过33。”不幸的是,ASP.NET MVC的文档缺口很大。唯一的MS文档是自动生成的,没有内部源摘要。 - shannon
6
惯例优于配置并不意味着您不能更改它。应该有可用的配置来指定文件的名称和位置。很可能已经有了,但谁知道是什么。人们使用“惯例优于配置”的口号来掩盖代码库中众多糟糕的决策,而这让我感到有些恼火,因为我是那个在事后来维护他们文档不良的混乱代码的人,它“只是工作”(但上帝保佑您更改任何内容 - 您将花费数小时弄清楚如何打破一切)。 - Robert C. Barth
3
@AidenStrydom 我不同意。被接受的答案实际上告诉了我如何使用_ViewStart。而这个答案只是谈论了一个设计概念。我来这里是为了获取关于_ViewStart的信息,而不是关于为什么Visual Studio不会告诉我有关_ViewStart的任何信息的信息。 - Millie Smith
显示剩余2条评论

24

另一个想法。

如果你想要拥有自己的cshtml文件作为通用模板,可以这样做

在你的_viewstart.cshtml中提及你的通用cshtml文件即可。

@{Layout = "~/Views/Shared/_Layout.cshtml";}

13

查找这个信息比查看文档更好的地方是源代码。

从Github引用MVC 6 代码,我们有一些感兴趣的文件。

----更新----

由于源结构更改,有关如何收集viewstart页面的信息现在可以在RazorViewEngine.cs中找到“GetViewStartPages”函数。

----/更新----

要回答它们如何发挥作用,请查看RazorView,我认为(因为IView)它与MVC管道相关联。该文件具有RenderAsync方法,该方法从MVC管道调用以呈现请求的视图。

RenderAsync会先调用RenderPage,然后再调用RenderLayout(请注意顺序)。RenderPage首先调用处理viewstart文件(注意复数,可能会有多个_viewstart文件)的函数。

因此,您要查找的信息可以在Microsoft.AspNet.Mvc.Razor命名空间下的RazorView.cs文件中的RenderViewStartAsync函数中获得。


12

现在可能会为这个问题(2016年的MVC4、MVC5)添加一些附加信息。

Razor引擎在运行与_ViewStart.cshtml相同目录或子目录中的其他代码之前,找到并运行_ViewStart.cshtml中的代码。

任何视图都可以覆盖Layout属性或其任何值。

我只是想增加一些更多的信息来展示为什么它是_ViewStart。

如果您获取ILSpy并检查RazorViewEngine(System.Web.Mvc.dll)中的代码,您将看到代码本身引用了该名称。

_ViewStart in  System.Web.Mvc.dll

您可以看到RazorViewEngine正在寻找具有该名称的文件:

razorviewengine code

RazorViewEngine.ViewStartFileName = "_ViewStart";

4
这就是我一直在寻找的,我讨厌在我的项目中“不知道”发生了什么,因为我也正在为VS制作自己的模板,而这个突然出现的文件非常难以理解。 - Sebastian 506563

2
如果您想为页面设置通用布局,您需要定义通用布局,并将视图与布局关联起来。我们必须在每个视图上设置布局属性,这违反了DRY(不要重复自己)原则。为此,.Net Framework提供了"_ViewStart.cshtml"文件,放置在视图文件夹中。我们将布局信息放置在"_ViewStart.cshtml"文件中,默认情况下每个视图都使用此布局信息。如果您想为某些视图提供不同的布局信息,比如Home视图,您可以创建一个新的"_ViewStart.cshtml"文件,并引用该布局,并将其放置在"Home View"文件夹中。

2
简短的回答是:在任何视图被渲染时,ViewStarts 首先开始工作。以下是详细说明:
创建单个视图文件的过程如下: 1. ViewStart 与 ViewImports 合并成一个文件并执行。请注意,ViewImports 总是与任何 cshtml 文件(包括 ViewStart 文件)合并。它的目的是抽象 @using 语句和其他常见指令。 2. ViewStart 的输出(如 Layout 和 ViewData)变为特定视图文件可用。 3. 在视图文件内,如果 Layout 变量为空或成为 null,则渲染视图主体,并将最终输出交付给用户。 4. 如果 Layout 变量不为空,则执行移动到布局文件,然后将其与 ViewImports 合并为一个文件。在布局文件内的 @RenderBody() 语句处,执行会返回到视图文件,再次与 ViewImports 合并,并在 @RenderBody() 处将输出与布局文件合并,最终输出结果交付给用户。
希望这让您了解程序生命周期中未知奥秘的真相。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接