我想知道在部分视图中包含 javascript 文件的最佳做法是什么。一旦呈现,这将最终成为我页面 html 中间的一个 js 包含标记。从我的角度来看,这不是一个很好的方法。它们属于 head 标签,因此不应阻止浏览器一次性渲染 html。
一个例子: 我在一个'PictureGallery'部分视图中使用一个jquery图片库插件,因为这个部分视图将在几个页面上使用。仅在使用此视图时才需要加载此插件,并且我不想知道每个部分视图正在使用哪些插件...
感谢您的回答。
我想知道在部分视图中包含 javascript 文件的最佳做法是什么。一旦呈现,这将最终成为我页面 html 中间的一个 js 包含标记。从我的角度来看,这不是一个很好的方法。它们属于 head 标签,因此不应阻止浏览器一次性渲染 html。
一个例子: 我在一个'PictureGallery'部分视图中使用一个jquery图片库插件,因为这个部分视图将在几个页面上使用。仅在使用此视图时才需要加载此插件,并且我不想知道每个部分视图正在使用哪些插件...
感谢您的回答。
似乎与这个问题非常相似:Linking JavaScript Libraries in User Controls
我将在此处重新发布该问题的答案。
出于您提到的原因,我绝对建议不要将它们放在局部中。一个视图很有可能会拉入两个都引用同一个 js 文件的部分。在加载 html 的其余部分之前,您还获得了加载 js 的性能影响。
我不知道最佳实践,但我选择在母版页中包含任何常见的 js 文件,然后为特定于特定或少量视图的一些附加 js 文件定义一个单独的 ContentPlaceHolder。
这是一个示例母版页 - 它非常不言自明。
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<head runat="server">
... BLAH ...
<asp:ContentPlaceHolder ID="AdditionalHead" runat="server" />
... BLAH ...
<%= Html.CSSBlock("/styles/site.css") %>
<%= Html.CSSBlock("/styles/ie6.css", 6) %>
<%= Html.CSSBlock("/styles/ie7.css", 7) %>
<asp:ContentPlaceHolder ID="AdditionalCSS" runat="server" />
</head>
<body>
... BLAH ...
<%= Html.JSBlock("/scripts/jquery-1.3.2.js", "/scripts/jquery-1.3.2.min.js") %>
<%= Html.JSBlock("/scripts/global.js", "/scripts/global.min.js") %>
<asp:ContentPlaceHolder ID="AdditionalJS" runat="server" />
</body>
Html.CSSBlock 和 Html.JSBlock 显然是我自己的扩展,但同样,它们的作用是不言自明的。
然后在说我会有一个 SignUp.aspx 视图
<asp:Content ID="signUpContent" ContentPlaceHolderID="AdditionalJS" runat="server">
<%= Html.JSBlock("/scripts/pages/account.signup.js", "/scripts/pages/account.signup.min.js") %>
</asp:Content>
HTH,查尔斯
附言。这是我询问的关于缩小和连接 js 文件的后续问题: Concatenate & Minify JS on the fly OR at build time - ASP.NET MVC
编辑:根据我的其他答案的要求,我按要求实现 .JSBlock(a, b)
public static MvcHtmlString JSBlock(this HtmlHelper html, string fileName)
{
return html.JSBlock(fileName, string.Empty);
}
public static MvcHtmlString JSBlock(this HtmlHelper html, string fileName, string releaseFileName)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentNullException("fileName");
string jsTag = string.Format("<script type=\"text/javascript\" src=\"{0}\"></script>",
html.MEDebugReleaseString(fileName, releaseFileName));
return MvcHtmlString.Create(jsTag);
}
然后魔法发生的地方......
public static MvcHtmlString MEDebugReleaseString(this HtmlHelper html, string debugString, string releaseString)
{
string toReturn = debugString;
#if DEBUG
#else
if (!string.IsNullOrEmpty(releaseString))
toReturn = releaseString;
#endif
return MvcHtmlString.Create(toReturn);
}
您将脚本放在页面底部的原因是确保在尝试任何操作之前已加载 dom。这也可以通过类似 $(document).ready(callback); 的方式实现。jQuery 方法。
我同意不将嵌入式 JavaScript 放入 html 的观点。相反,我使用 Html 助手创建一个空 div 以用作服务器指令。助手 sig 是 Html.ServerData(String name, Object data);
我使用这个 ServerData 方法来获取从服务器到客户端的指令。div 标签保持干净,“data-”属性是有效的 html5。
对于 loadScript 指令,我可以在我的 ascx 或 aspx 中执行类似的操作:
<%= Html.ServerData("loadScript", new { url: "pathTo.js" }) %>
或者添加另一个看起来更干净的助手来执行此操作:
<%= Html.LoadScript("~/path/to.js") %>
html 输出将是:
<div name="loadScript" data-server="encoded json string">
然后我有一个可以找到任何服务器数据标签的 jQuery 方法: $(containingElement).serverData("loadScript"); // 返回解码后的 json 对象的类似 jQuery 的数组。
客户端可能看起来像这样:
var script = $(containingelement").serverData("loadScript"); $.getScript(script.url, function () { // 脚本已经加载 - 现在可以用它做事了 });
这种技术非常适合需要在 ajax 加载的内容中加载的用户控件或脚本。我写的版本更多地涉及处理缓存脚本,因此它们在每次完整页面加载时仅加载一次,并包含回调和 jQuery 触发器,因此您可以在它准备好时挂钩它。
如果有人对此的完整版本感兴趣(从 MVC 到 jQuery 扩展),我很乐意更详细地展示这种技术。否则 - 希望它给某人一种解决这个棘手问题的新方法。
今天,我创建了自己的解决方案,完全符合要求。不管它是不是好的设计,都由你们来决定,但我认为我应该分享任何一种方式!
下面是我的 HtmlExtensions 类,它允许您在母版页中执行此操作:
<%=Html.RenderJScripts() %>
我的 HtmlExtensions 类:
public static class HtmlExtensions
{
private const string JSCRIPT_VIEWDATA = "__js";
#region Javascript Inclusions
public static void JScript(this HtmlHelper html, string scriptLocation)
{
html.JScript(scriptLocation, string.Empty);
}
public static void JScript(this HtmlHelper html, string scriptLocationDebug, string scriptLocationRelease)
{
if (string.IsNullOrEmpty(scriptLocationDebug))
throw new ArgumentNullException("fileName");
string jsTag = "<script type=\"text/javascript\" src=\"{0}\"></script>";
#if DEBUG
jsTag = string.Format(jsTag, scriptLocationDebug);
#else
jsTag = string.Format(jsTag, !string.IsNullOrEmpty(scriptLocationRelease) ? scriptLocationRelease : scriptLocationDebug);
#endif
registerJScript(html, jsTag);
}
public static MvcHtmlString RenderJScripts(this HtmlHelper html)
{
List<string> jscripts = html.ViewContext.TempData[JSCRIPT_VIEWDATA] as List<string>;
string result = string.Empty; ;
if(jscripts != null)
{
result = string.Join("\r\n", jscripts);
}
return MvcHtmlString.Create(result);
}
private static void registerJScript(HtmlHelper html, string jsTag)
{
List<string> jscripts = html.ViewContext.TempData[JSCRIPT_VIEWDATA] as List<string>;
if(jscripts == null) jscripts = new List<string>();
if(!jscripts.Contains(jsTag))
jscripts.Add(jsTag);
html.ViewContext.TempData[JSCRIPT_VIEWDATA] = jscripts;
}
#endregion
}
到底是怎么回事?
上面的类将使用方法扩展 HtmlHelper,以将 javascript 链接添加到由集合存储的HtmlHelper.ViewContext.TempData
集合。在母版页的末尾,我<%=Html.RenderJScripts() %>
将循环 jscripts 集合放入其中HtmlHelper.ViewContext.TempData
并将这些呈现到输出。
然而,有一个缺点,.. 您必须确保在添加脚本之前不渲染脚本。例如,如果您想对 css 链接执行此操作,则它不会起作用,因为您必须将它们放在<head>
页面的标记中,并且 htmlhelper 会在您添加链接之前呈现输出。
这似乎是一个类似(尽管不完全)的问题。 返回包含 javascript 的部分视图是不好的做法吗?
我的偏好是创建一个从您的主 Site.master 继承的 plugin.master 页面。这个想法是你把这些插件塞进plugin.master 并制作8 个左右的页面,这些页面将使用这个部分视图从plugin.master 继承。
首选方法是将脚本放在底部,但是,如果您无法避免,那么将它们放在中间是合理的。
浏览器通常并行加载各种页面元素,但是,当浏览器下载 Javascript 文件时,它不会并行下载任何其他页面元素,直到 Javascript 完成下载。这意味着您的图像和其他图像将不得不等待,因此通常认为将脚本移动到底部会更好,但如果您的脚本很小,那么我不会担心。
海迪格。
使用 Aspnetmvc3 我决定,现在,只在部分中包含我的 javascript 文件
<script src="@Url.Content("~/Scripts/User/List.js")" type="text/javascript"></script>
然后,此 js 文件特定于我的 /user/List.schtml 文件。
/的
所以,这是一个老问题,但我在尝试解决最近的问题时发现了它。我问了一个问题并在这里回答了。这是该答案的副本。它类似于 OP 的答案,但避免使用 TempData。
一种解决方案是实现一个 Html 助手扩展功能,它只会加载一次脚本。见下文。(这也适用于 CSS 和其他包含的文件)。
实现 HtmlHelper 类的扩展和私有支持类作为每个 HttpContext 的单例。
public static class YourHtmlHelperExtensionClass
{
private class TagSrcAttrTracker
{
private TagSrcAttrTracker() { }
public Dictionary<string, string> sources { get; } = new Dictionary<string, string>();
public static TagSrcAttrTrackerInstance {
get {
IDictionary items = HttpContext.Current.Items;
const string instanceName = "YourClassInstanceNameMakeSureItIsUniqueInThisDictionary";
if(!items.Contains(instanceName))
items[instanceName] = new TagSrcAttrTracker();
return items[instanceName] as TagSrcAttrTracker;
}
}
}
public static MvcHtmlString IncludeScriptOnlyOnce(this HtmlHelper helper, string urlOfScript)
{
if(TagSrcAttrTracker.Instance.sources.ContainsKey(urlOfScript))
return null;
TagSrcAttrTracker.Instance.sources[urlOfScript] = urlOfScript;
TagBuilder script = new TagBuilder("script");
scriptTag.MergeAttribute("src", urlOfScript);
return MvcHtmlString.Create(script.ToString());
}
}
然后,将 JavaScript 和其他代码分离到单独的文件中。
示例 .js 文件内容
class MyClass{
myFunction() {
constructor(divId){
this._divId = divId
}
doSomething() {
// do something with a div with id == divId
}
}
}
局部视图的示例 .cshtml 文件内容
<link rel="stylesheet" type="text/css" href="~/Content/CustomCSS/MyPartialView.css"/>
<div id="@ViewBag.id">
Some content! Yay!
</div>
@Html.IncludeScriptOnlyOnce("/Scripts/CustomScripts/MyPartialView.js")
使用局部视图的示例 .cshtml 文件
...
<body>
<h1>Here is some content!</h1>
@Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary() { { "id", "id_1"} })
@Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary() { { "id", "id_2"} })
@Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary() { { "id", "id_3"} })
</body>
<script>
$().ready(
const id1Functionality = new MyClass("id_1") // forgive the poor naming :-)
const id2Functionality = new MyClass("id_3")
const id3Functionality = new MyClass("id_2")
id1Functionality.doSomething();
id2Functionality.doSomething();
id3Functionality.doSomething();
)
</script>
部分视图可能被多次包含,并且 JavaScript 与部分视图一起打包,但 .js 文件只包含在页面中一次,因此浏览器不会抱怨 MyClass 被多次声明。
MVC 的设计者费了很大的劲阻止我们使用代码隐藏,但是通过创建一个名为 <viewname>.aspx.cs 的文件并相应地修改 aspx 页面的继承属性,仍然可以得到它们.
我想说,放置包含的最佳位置是代码隐藏中的 Page_Load 处理程序(使用 Page.ClientScript.RegisterClientScriptInclude)。