如何防止部分视图中的脚本多次加载并在同一页面中多次使用部分时导致错误

Posted

技术标签:

【中文标题】如何防止部分视图中的脚本多次加载并在同一页面中多次使用部分时导致错误【英文标题】:How to keep script in partial view from loading more than once and causing errors when partial is used more than once in the same page 【发布时间】:2020-09-13 18:29:33 【问题描述】:

在 ASP.NET MVC 中工作时,我创建了一个在同一页面上呈现 2 次的局部视图。我的问题是 JavaScript 包含的次数与部分视图一样多,并且 javascript 不喜欢重新定义类。

我的问题是:如何在局部视图中包含 JavaScript,使其保持模块化,但只在页面中包含一次?我对需要将 .js 文件与部分视图的内容分开包含的解决方案不感兴趣,例如

<script src="some.js"></script>
@html.Partial("some/partial.cshtml")
@Html.Partial("some/partial.cshtml")

但我可以接受包含在局部视图中的单独 .js 文件。局部视图应包含使其工作所需的一切。

我的代码结构如下:

<div id=@ViewBag.id></div>
<script>
    class MyClass 
        constructor(divName)
            this._divId = divId;
        

        doSomething()
            /* do stuff with element that has id of divId */ 
        
    
</script>

然后,在调用文件中,我有

<body>
    @Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary()   "id", "id_1" )
    @Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary()   "id", "id_2" )
</body>
<script>
    $().ready(function()
        id1Functionality = new MyClass("id_1"); // please forgive the poor naming :-)
        id2Functionality = new MyClass("id_2");

        id1Functionality.doSomething();
        id2Functionality.doSomething();
    )
</script>

【问题讨论】:

我回答了自己的问题,但很乐意接受更好(更简单、更强大、更优雅)的答案,因此等待接受我自己的问题。 【参考方案1】:

一种解决方案是实现一个 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 被多次声明。

【讨论】:

以上是关于如何防止部分视图中的脚本多次加载并在同一页面中多次使用部分时导致错误的主要内容,如果未能解决你的问题,请参考以下文章

Javascript在ajax提交过程中页面显示加载中,请等待效果,并在提交过程中限制确定按钮防止多次提交,提交完成后,解除提交限制

在Blogger / Blogspot中多次重新加载同一页面会降低跳出率?

我如何调整这个选择语句以防止它多次从同一个表中选择?

多次使用 UIView(使用 xib 创建)作为同一视图的子视图

防止多次执行 Lua 脚本

Jquery多次加载到div中