从视图或部分视图将 CSS 或 JavaScript 文件添加到布局头
Posted
技术标签:
【中文标题】从视图或部分视图将 CSS 或 JavaScript 文件添加到布局头【英文标题】:Add CSS or JavaScript files to layout head from views or partial views 【发布时间】:2011-07-03 20:39:11 【问题描述】:布局页面头部:
<head>
<link href="@Url.Content("~/Content/themes/base/Site.css")"
rel="stylesheet" type="text/css" />
</head>
应用需要的一个视图(AnotherView):
<link href="@Url.Content("~/Content/themes/base/AnotherPage.css")"
rel="stylesheet" type="text/css" />
而另一个视图有一个局部视图(AnotherPartial),它需要:
<link href="@Url.Content("~/Content/themes/base/AnotherPartial.css")"
rel="stylesheet" type="text/css" />
问题:我们如何添加这些 CSS 文件链接 AnotherView 和 AnotherPartial 链接到 Layout head?
RenderSection 不是一个好主意,因为 AnotherPage 可以有多个 Partials。将所有 CSS 添加到 head 没有用,因为它会动态更改(它取决于 Anotherpages)。
【问题讨论】:
@NuriYILMAZ 根据您的标题,“来自视图”和“或部分视图”之间存在巨大差异。无论如何,关于这件事有什么新想法吗? 【参考方案1】:布局:
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.6.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-2.0.6-development-only.js")" type="text/javascript"></script>
@if (IsSectionDefined("AddToHead"))
@RenderSection("AddToHead", required: false)
@RenderSection("AddToHeadAnotherWay", required: false)
</head>
查看:
@model ProjectsExt.Models.DirectoryObject
@section AddToHead
<link href="@Url.Content("~/Content/Upload.css")" rel="stylesheet" type="text/css" />
【讨论】:
我认为这是最简单的解决方案。 开箱即用的好解决方案! 如果AddToHead
部分位于嵌入在View
中的局部视图中,这将不起作用。
这个问题特别提到了部分观点,而这个评分最高的答案并没有解决这个问题!对于另一个查询,这可能是一个很好的解决方案,但不是这个。
如果它确实适用于局部视图,那将是一个优雅的解决方案。【参考方案2】:
更新:https://github.com/speier/mvcassetshelper
上提供的基本示例我们正在使用以下实现将 JS 和 CSS 文件添加到布局页面中。
查看或部分查看:
@
Html.Assets().Styles.Add("/Dashboard/Content/Dashboard.css");
Html.Assets().Scripts.Add("/Dashboard/Scripts/Dashboard.js");
布局页面:
<head>
@Html.Assets().Styles.Render()
</head>
<body>
...
@Html.Assets().Scripts.Render()
</body>
HtmlHelper 扩展:
public static class HtmlHelperExtensions
public static AssetsHelper Assets(this HtmlHelper htmlHelper)
return AssetsHelper.GetInstance(htmlHelper);
public class AssetsHelper
public static AssetsHelper GetInstance(HtmlHelper htmlHelper)
var instanceKey = "AssetsHelperInstance";
var context = htmlHelper.ViewContext.HttpContext;
if (context == null) return null;
var assetsHelper = (AssetsHelper)context.Items[instanceKey];
if (assetsHelper == null)
context.Items.Add(instanceKey, assetsHelper = new AssetsHelper());
return assetsHelper;
public ItemRegistrar Styles get; private set;
public ItemRegistrar Scripts get; private set;
public AssetsHelper()
Styles = new ItemRegistrar(ItemRegistrarFormatters.StyleFormat);
Scripts = new ItemRegistrar(ItemRegistrarFormatters.ScriptFormat);
public class ItemRegistrar
private readonly string _format;
private readonly IList<string> _items;
public ItemRegistrar(string format)
_format = format;
_items = new List<string>();
public ItemRegistrar Add(string url)
if (!_items.Contains(url))
_items.Add(url);
return this;
public IHtmlString Render()
var sb = new StringBuilder();
foreach (var item in _items)
var fmt = string.Format(_format, item);
sb.AppendLine(fmt);
return new HtmlString(sb.ToString());
public class ItemRegistrarFormatters
public const string StyleFormat = "<link href=\"0\" rel=\"stylesheet\" type=\"text/css\" />";
public const string ScriptFormat = "<script src=\"0\" type=\"text/javascript\"></script>";
【讨论】:
@JBeckton:查看代码并将Insert
方法替换为Add
方法
@Kalman - 这个线程安全性(在我看来非常正确)受到质疑:***.com/questions/6609586/…
这是非常错误的;它应该是[ThreadStatic]
,或者最好存储在HttpContext.Items
。
我错过了什么吗?如果在 中调用 Styles.Render(),则在 之后添加的任何 css 文件(即在局部视图中添加的文件)都不会被渲染。 (MVC 从上到下渲染。)
@FernandoCorreia 我认为你完全错了。命名部分在作为整个线程基础的部分视图中不起作用。【参考方案3】:
你可以在布局中通过RenderSection方法定义section。
布局
<head>
<link href="@Url.Content("~/Content/themes/base/Site.css")"
rel="stylesheet" type="text/css" />
@RenderSection("heads", required: false)
</head>
然后,您可以在视图的部分区域中包含您的 css 文件部分视图除外。
该部分在视图中工作,但在设计上不能在部分视图中工作。
<!--your code -->
@section heads
<link href="@Url.Content("~/Content/themes/base/AnotherPage.css")"
rel="stylesheet" type="text/css" />
如果你真的想在局部视图中使用截面区域,可以按照文章重新定义RenderSection方法。
Razor, Nested Layouts and Redefined Sections – Marcin On ASP.NET
【讨论】:
【参考方案4】:遗憾的是,默认情况下无法像其他用户建议的那样使用section
,因为section
仅对View
的直接child
可用。
然而可行的是在每个视图中实现和重新定义section
,意思是:
section Head
@RenderSection("Head", false)
这样每个视图都可以实现一个 head 部分,而不仅仅是直接子级。但这仅部分有效,尤其是在多个部分开始时(正如您在问题中提到的那样)。
因此,解决您的问题的唯一真正方法是使用ViewBag
。最好的可能是 CSS 和脚本的单独集合(列表)。为此,您需要确保在执行任何视图之前初始化使用的List
。然后你可以在每个视图/部分的顶部做这样的事情(不关心 Scripts
或 Styles
值是否为空:
ViewBag.Scripts.Add("myscript.js");
ViewBag.Styles.Add("mystyle.css");
然后,您可以在布局中循环遍历集合并根据List
中的值添加样式。
@foreach (var script in ViewBag.Scripts)
<script type="text/javascript" src="@script"></script>
@foreach (var style in ViewBag.Styles)
<link href="@style" rel="stylesheet" type="text/css" />
我认为它很丑,但它是唯一有效的东西。
******更新****
由于它首先开始执行内部视图并逐步处理布局,并且 CSS 样式是级联的,因此通过 ViewBag.Styles.Reverse()
反转样式列表可能是有意义的。
这种方式首先添加最外层的样式,这与 CSS 样式表的工作方式是一致的。
【讨论】:
感谢 ntziolis。看起来不错,但是剃刀布局头在另一个视图之前首先工作,并且 .scripts 和 .styles 动态在另一个视图之前是空的。我找到了关于它的不错的博客,并分享了这个问题。 这将适用于任何派生视图,但不适用于部分视图。对于部分,确实执行顺序是错误的。基本上对于部分没有办法将它们包含在标题中。我建议,不要将其添加到标题中,只需将其添加到正文标记的开头即可。这不是我的第一选择,但这样你就有一种简洁的方式在一个地方管理所有样式/js,而不是分散它们。 我同意你的看法。因为我在答案中找到了一些解决方案,但它正是 js 解决方案。我真的很想知道为什么我们不能将布局页面用作经典的 asp.net。这意味着我可以从子页面到达头部。【参考方案5】:我试图解决这个问题。
我的答案就在这里。
“动态标题”-http://dynamicheader.codeplex.com/、https://nuget.org/packages/DynamicHeader
例如_Layout.cshtml是:
<head>
@Html.DynamicHeader()
</head>
...
而且,您可以将 .js 和 .css 文件注册到您想要的任何地方的“DynamicHeader”。
例如AnotherPartial.cshtml中的代码块是:
@
DynamicHeader.AddSyleSheet("~/Content/themes/base/AnotherPartial.css");
DynamicHeader.AddScript("~/some/myscript.js");
此示例的 HTML 输出结果为:
<html>
<link href="/myapp/Content/themes/base/AnotherPartial.css" .../>
<script src="/myapp/some/myscript.js" ...></script>
</html>
...
【讨论】:
【参考方案6】:我遇到了类似的问题,最后用下面的代码应用了 Kalman 的出色答案(不是那么整洁,但可以说更具可扩展性):
namespace MvcHtmlHelpers
//http://***.com/questions/5110028/add-css-or-js-files-to-layout-head-from-views-or-partial-views#5148224
public static partial class HtmlExtensions
public static AssetsHelper Assets(this HtmlHelper htmlHelper)
return AssetsHelper.GetInstance(htmlHelper);
public enum BrowserType Ie6=1,Ie7=2,Ie8=4,IeLegacy=7,W3cCompliant=8,All=15
public class AssetsHelper
public static AssetsHelper GetInstance(HtmlHelper htmlHelper)
var instanceKey = "AssetsHelperInstance";
var context = htmlHelper.ViewContext.HttpContext;
if (context == null) return null;
var assetsHelper = (AssetsHelper)context.Items[instanceKey];
if (assetsHelper == null)context.Items.Add(instanceKey, assetsHelper = new AssetsHelper(htmlHelper));
return assetsHelper;
private readonly List<string> _styleRefs = new List<string>();
public AssetsHelper AddStyle(string stylesheet)
_styleRefs.Add(stylesheet);
return this;
private readonly List<string> _scriptRefs = new List<string>();
public AssetsHelper AddScript(string scriptfile)
_scriptRefs.Add(scriptfile);
return this;
public IHtmlString RenderStyles()
ItemRegistrar styles = new ItemRegistrar(ItemRegistrarFormatters.StyleFormat,_urlHelper);
styles.Add(Libraries.UsedStyles());
styles.Add(_styleRefs);
return styles.Render();
public IHtmlString RenderScripts()
ItemRegistrar scripts = new ItemRegistrar(ItemRegistrarFormatters.ScriptFormat, _urlHelper);
scripts.Add(Libraries.UsedScripts());
scripts.Add(_scriptRefs);
return scripts.Render();
public LibraryRegistrar Libraries get; private set;
private UrlHelper _urlHelper;
public AssetsHelper(HtmlHelper htmlHelper)
_urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
Libraries = new LibraryRegistrar();
public class LibraryRegistrar
public class Component
internal class HtmlReference
internal string Url get; set;
internal BrowserType ServeTo get; set;
internal List<HtmlReference> Styles get; private set;
internal List<HtmlReference> Scripts get; private set;
internal List<string> RequiredLibraries get; private set;
public Component()
Styles = new List<HtmlReference>();
Scripts = new List<HtmlReference>();
RequiredLibraries = new List<string>();
public Component Requires(params string[] libraryNames)
foreach (var lib in libraryNames)
if (!RequiredLibraries.Contains(lib))
RequiredLibraries.Add(lib);
return this;
public Component AddStyle(string url, BrowserType serveTo = BrowserType.All)
Styles.Add(new HtmlReference Url = url, ServeTo=serveTo );
return this;
public Component AddScript(string url, BrowserType serveTo = BrowserType.All)
Scripts.Add(new HtmlReference Url = url, ServeTo = serveTo );
return this;
private readonly Dictionary<string, Component> _allLibraries = new Dictionary<string, Component>();
private List<string> _usedLibraries = new List<string>();
internal IEnumerable<string> UsedScripts()
SetOrder();
var returnVal = new List<string>();
foreach (var key in _usedLibraries)
returnVal.AddRange(from s in _allLibraries[key].Scripts
where IncludesCurrentBrowser(s.ServeTo)
select s.Url);
return returnVal;
internal IEnumerable<string> UsedStyles()
SetOrder();
var returnVal = new List<string>();
foreach (var key in _usedLibraries)
returnVal.AddRange(from s in _allLibraries[key].Styles
where IncludesCurrentBrowser(s.ServeTo)
select s.Url);
return returnVal;
public void Uses(params string[] libraryNames)
foreach (var name in libraryNames)
if (!_usedLibraries.Contains(name))_usedLibraries.Add(name);
public bool IsUsing(string libraryName)
SetOrder();
return _usedLibraries.Contains(libraryName);
private List<string> WalkLibraryTree(List<string> libraryNames)
var returnList = new List<string>(libraryNames);
int counter = 0;
foreach (string libraryName in libraryNames)
WalkLibraryTree(libraryName, ref returnList, ref counter);
return returnList;
private void WalkLibraryTree(string libraryName, ref List<string> libBuild, ref int counter)
if (counter++ > 1000) throw new System.Exception("Dependancy library appears to be in infinate loop - please check for circular reference");
Component library;
if (!_allLibraries.TryGetValue(libraryName, out library))
throw new KeyNotFoundException("Cannot find a definition for the required style/script library named: " + libraryName);
foreach (var childLibraryName in library.RequiredLibraries)
int childIndex = libBuild.IndexOf(childLibraryName);
if (childIndex!=-1)
//child already exists, so move parent to position before child if it isn't before already
int parentIndex = libBuild.LastIndexOf(libraryName);
if (parentIndex>childIndex)
libBuild.RemoveAt(parentIndex);
libBuild.Insert(childIndex, libraryName);
else
libBuild.Add(childLibraryName);
WalkLibraryTree(childLibraryName, ref libBuild, ref counter);
return;
private bool _dependenciesExpanded;
private void SetOrder()
if (_dependenciesExpanded)return;
_usedLibraries = WalkLibraryTree(_usedLibraries);
_usedLibraries.Reverse();
_dependenciesExpanded = true;
public Component this[string index]
get
if (_allLibraries.ContainsKey(index))
return _allLibraries[index];
var newComponent = new Component();
_allLibraries.Add(index, newComponent);
return newComponent;
private BrowserType _requestingBrowser;
private BrowserType RequestingBrowser
get
if (_requestingBrowser == 0)
var browser = HttpContext.Current.Request.Browser.Type;
if (browser.Length > 2 && browser.Substring(0, 2) == "IE")
switch (browser[2])
case '6':
_requestingBrowser = BrowserType.Ie6;
break;
case '7':
_requestingBrowser = BrowserType.Ie7;
break;
case '8':
_requestingBrowser = BrowserType.Ie8;
break;
default:
_requestingBrowser = BrowserType.W3cCompliant;
break;
else
_requestingBrowser = BrowserType.W3cCompliant;
return _requestingBrowser;
private bool IncludesCurrentBrowser(BrowserType browserType)
if (browserType == BrowserType.All) return true;
return (browserType & RequestingBrowser) != 0;
public class ItemRegistrar
private readonly string _format;
private readonly List<string> _items;
private readonly UrlHelper _urlHelper;
public ItemRegistrar(string format, UrlHelper urlHelper)
_format = format;
_items = new List<string>();
_urlHelper = urlHelper;
internal void Add(IEnumerable<string> urls)
foreach (string url in urls)
Add(url);
public ItemRegistrar Add(string url)
url = _urlHelper.Content(url);
if (!_items.Contains(url))
_items.Add( url);
return this;
public IHtmlString Render()
var sb = new StringBuilder();
foreach (var item in _items)
var fmt = string.Format(_format, item);
sb.AppendLine(fmt);
return new HtmlString(sb.ToString());
public class ItemRegistrarFormatters
public const string StyleFormat = "<link href=\"0\" rel=\"stylesheet\" type=\"text/css\" />";
public const string ScriptFormat = "<script src=\"0\" type=\"text/javascript\"></script>";
项目包含一个静态的AssignAllResources方法:
assets.Libraries["jQuery"]
.AddScript("~/Scripts/jquery-1.10.0.min.js", BrowserType.IeLegacy)
.AddScript("~/Scripts//jquery-2.0.1.min.js",BrowserType.W3cCompliant);
/* NOT HOSTED YET - CHECK SOON
.AddScript("//ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js",BrowserType.W3cCompliant);
*/
assets.Libraries["jQueryUI"].Requires("jQuery")
.AddScript("//ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js",BrowserType.Ie6)
.AddStyle("//ajax.aspnetcdn.com/ajax/jquery.ui/1.9.2/themes/eggplant/jquery-ui.css",BrowserType.Ie6)
.AddScript("//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js", ~BrowserType.Ie6)
.AddStyle("//ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/themes/eggplant/jquery-ui.css", ~BrowserType.Ie6);
assets.Libraries["TimePicker"].Requires("jQueryUI")
.AddScript("~/Scripts/jquery-ui-sliderAccess.min.js")
.AddScript("~/Scripts/jquery-ui-timepicker-addon-1.3.min.js")
.AddStyle("~/Content/jQueryUI/jquery-ui-timepicker-addon.css");
assets.Libraries["Validation"].Requires("jQuery")
.AddScript("//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js")
.AddScript("~/Scripts/jquery.validate.unobtrusive.min.js")
.AddScript("~/Scripts/mvcfoolproof.unobtrusive.min.js")
.AddScript("~/Scripts/CustomClientValidation-1.0.0.min.js");
assets.Libraries["MyUtilityScripts"].Requires("jQuery")
.AddScript("~/Scripts/GeneralOnLoad-1.0.0.min.js");
assets.Libraries["FormTools"].Requires("Validation", "MyUtilityScripts");
assets.Libraries["AjaxFormTools"].Requires("FormTools", "jQueryUI")
.AddScript("~/Scripts/jquery.unobtrusive-ajax.min.js");
assets.Libraries["DataTables"].Requires("MyUtilityScripts")
.AddScript("//ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js")
.AddStyle("//ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css")
.AddStyle("//ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables_themeroller.css");
assets.Libraries["MvcDataTables"].Requires("DataTables", "jQueryUI")
.AddScript("~/Scripts/jquery.dataTables.columnFilter.min.js");
assets.Libraries["DummyData"].Requires("MyUtilityScripts")
.AddScript("~/Scripts/DummyData.js")
.AddStyle("~/Content/DummyData.css");
在_layout页面中
@
var assets = Html.Assets();
CurrentResources.AssignAllResources(assets);
Html.Assets().RenderStyles()
</head>
...
@Html.Assets().RenderScripts()
</body>
在局部和视图中
Html.Assets().Libraries.Uses("DataTables");
Html.Assets().AddScript("~/Scripts/emailGridUtilities.js");
【讨论】:
有趣。似乎有点矫枉过正,但我确实看到这更多地用于那些别无选择的网站,而是与使用旧版本 ie 的用户打交道......就像在一些国家没有升级的公司环境中,你想自己开枪。哈哈。为此 +1【参考方案7】:这是一个名为Cassette 的NuGet 插件,除其他外,它还使您能够在部分中引用脚本和样式。
虽然这个插件有a number of configurations 可用,这使得它非常灵活。这是引用脚本或样式表文件的最简单方法:
Bundles.Reference("scripts/app");
根据the documentation:
对
Reference
的调用可以出现在页面、布局或局部视图中的任何位置。路径参数可以是以下之一:
捆绑路径 资产路径 - 引用包含此资产的整个捆绑包 网址
【讨论】:
【参考方案8】:尝试开箱即用的解决方案(ASP.NET MVC 4 或更高版本):
@
var bundle = BundleTable.Bundles.GetRegisteredBundles().First(b => b.Path == "~/js");
bundle.Include("~/Scripts/myFile.js");
【讨论】:
我收到一个错误:CS0103: The name 'BundleTable' does not exist in the current context
nvm:已解决。必须附加System.Web.Optimization
即System.Web.Optimization.BundleTable.Bundles.GetRegisteredBundles().First(b => b.Path == "~/bundles/css");
这不会全局修改捆绑包吗?如果您在页面 A 上执行此操作,然后打开页面 B,页面 B 也将包含 myFile.js,我认为 OP 不希望这样做【参考方案9】:
对于我们这些使用 ASP.NET MVC 4 的人 - 这可能会有所帮助。
首先,我在 App_Start 文件夹中添加了一个 BundleConfig 类。
这是我用来创建它的代码:
using System.Web.Optimization;
public class BundleConfig
public static void RegisterBundles(BundleCollection bundles)
bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/SiteMaster.css"));
其次,我在 Global.asax 文件中注册了 BundleConfig 类:
protected void Application_Start()
BundleConfig.RegisterBundles(BundleTable.Bundles);
第三,我在我的 CSS 文件中添加了样式助手:
/* Styles for validation helpers */
.field-validation-error
color: red;
font-weight: bold;
.field-validation-valid
display: none;
input.input-validation-error
border: 1px solid #e80c4d;
input[type="checkbox"].input-validation-error
border: 0 none;
.validation-summary-errors
color: #e80c4d;
font-weight: bold;
font-size: 1.1em;
.validation-summary-valid
display: none;
最后我在任何视图中都使用了这个语法:
@Styles.Render("~/Content/css")
【讨论】:
但这会导致<body>
标签中间的链接不正确。【参考方案10】:
我编写了一个简单的包装器,它允许您将每个局部视图中的样式和脚本动态注册到 head 标记中。
它基于 jsakamoto 提出的 DynamicHeader,但它有一些性能改进和调整。
它非常易于使用,用途广泛。
用法:
@
DynamicHeader.AddStyleSheet("/Content/Css/footer.css", ResourceType.Layout);
DynamicHeader.AddStyleSheet("/Content/Css/controls.css", ResourceType.Infrastructure);
DynamicHeader.AddScript("/Content/Js/Controls.js", ResourceType.Infrastructure);
DynamicHeader.AddStyleSheet("/Content/Css/homepage.css");
您可以在里面找到完整的代码、解释和示例: Add Styles & Scripts Dynamically to Head Tag
【讨论】:
以上是关于从视图或部分视图将 CSS 或 JavaScript 文件添加到布局头的主要内容,如果未能解决你的问题,请参考以下文章