【问题标题】:Making cached script files to refresh on client machines使缓存的脚本文件在客户端计算机上刷新
【发布时间】:2013-03-21 00:04:20
【问题描述】:

我们的一个生产基地遇到了问题。已为页面更新 JavaScript 文件并上传到 IIS。我们使用

直接包含文件
<script src="PATH_TO_SCRIPT" type="text/javascript"></script>

我们开始收到客户关于页面损坏的投诉。这是因为 JS 文件缓存在客户端计算机上并且没有从服务器刷新。

我们以后如何在不更改javascript文件名的情况下避免这种情况?

ASP.Net 捆绑和缩小可能会有所帮助。但是有很多页面和网站是相当遗留。几乎所有的页面都有一些繁重的逻辑写在相关的js文件中。

该站点正在运行 .Net 4.0 和 IIS 7

【问题讨论】:

    标签: asp.net caching iis-7 .net-4.0


    【解决方案1】:

    捆绑和缩小确实是处理这个问题的正确方法,因为它会在渲染脚本时负责正确地将正确的版本号附加到 url。

    但是,如果这是一个旧站点并且由于某些原因您不能使用捆绑,一种可能性是编写一个服务器端帮助程序,它将生成脚本标记并计算文件的校验和并附加正确的查询字符串参数:

    public static class ScriptExtensions
    {
        public static string Script(this Page page, string relativeUrl)
        {
            var path = page.Server.MapPath(relativeUrl);
            if (File.Exists(path))
            {
                return string.Format(
                    "<script type=\"type/javascript\" src=\"{0}?v={1}\"></script>",
                    page.ResolveUrl(relativeUrl),
                    Hash(path)
                );
            }
            return string.Empty;
        }
    
        private static string Hash(string file)
        {
            using (var stream = File.OpenRead(file))
            using (var bs = new BufferedStream(stream))
            {
                using (var sha1 = new SHA1Managed())
                {
                    byte[] hash = sha1.ComputeHash(bs);
                    var result = new StringBuilder(2 * hash.Length);
                    foreach (byte b in hash)
                    {
                        result.AppendFormat("{0:X2}", b);
                    }
                    return result.ToString();
                }
            }
        }
    }
    

    然后在您的 WebForm 中使用帮助程序来包含您的脚本:

    <%= this.Script("~/scripts/example.js") %>
    

    这将发出以下标记:

    <script type="type/javascript" src="/scripts/example.js?v=3C222D8DFA2A02A02E9A585EA6FE0D95673E8B4A"></script>
    

    现在,当您更改脚本文件的内容时,其 SHA1 校验和将有所不同,并且将生成不同版本的查询字符串参数并附加到所有客户端缓存。

    【讨论】:

    • 我想到了这种方案,例如使用自定义服务器端控件在页面中包含脚本,但唯一停止的是这将如何影响页面的整体呈现时间?好吧,我知道在问这个问题之前我应该​​已经实现并尝试了这个。是否可以缓存它,以免每次都重新计算?
    • 当然,您可以查看 javascript 文件的最后修改日期并决定是否要重新计算哈希值。您可能已经缓存了最后修改日期并根据该信息做出决定。基本上,这就是捆绑在 ASP.NET 中所做的事情。真的,如果你不能重新发明轮子,那就用它吧。
    • 感谢您的启发。我在捆绑基础设施上缝合了一些东西。
    • 在这个解决方案中,是否会在每次渲染视图时计算哈希?这不会严重影响性能吗?
    • @Bertvan,是的,在这个例子中,哈希将在每个请求上计算,是的,它可能会对性能产生负面影响。但我从来没有说过这是一个生产就绪的代码,你可以复制粘贴到你的应用程序中。我只是说明了这个概念。很明显,如果您想将其集成到生产系统中,您将不得不考虑缓存哈希值。
    【解决方案2】:

    受到 Darin 解决方案的启发,我决定使用 Bundling and Minification 以获得它所提供的所有好处,我想出了以下解决方案。为 Page 类型添加一个带有 Extension 方法的静态类:

    public static class ScriptExtensions
    {
        public static string Script(this Page page, string relativeUrl)
        {
            var path = page.Server.MapPath(relativeUrl);
            if (File.Exists(path))
            {
                return BundlesConfig.AddPageScript(relativeUrl);
            }
            return string.Empty;
        }
    }
    

    BundlesConfig 类 contians 方法为 js 文件生成 bundle 并添加到 Bundles 中:

    public class BundlesConfig
    {
        private static readonly ICollection<string> addedScripts 
                                                         = new HashSet<string>();
        private static readonly string bundleTemplate = "~/bundles/scripts/{0}";
    
        internal static string AddPageScript(string relativeUrl)
        {            
                var fileName = CleanFileName(relativeUrl);
                var bundleName = string.Format(bundleTemplate, fileName);
    
                if(!addedScripts.Contains(fileName))
                {
                    var bundle = new ScriptBundle(bundleName);
                    bundle.Include(relativeUrl);
                    addedScripts.Add(fileName);
                    BundleTable.Bundles.Add(bundle);
                }
          return System.Web.Optimization.Scripts.Render(bundleName).ToHtmlString();
        }
    
        private static string CleanFileName(string url)
        {
            if (url.Contains("/"))
            {
                return url.Substring(url.LastIndexOf("/") + 1).Replace('.', '_')
                              .Replace("-", "__");
            }
    
            return url.Replace('.', '_').Replace("-", "__");
        }
    }
    

    现在在页面上,而不是标准的script 标签:

    <scrip type="text/javascript" src="/scripts/jquery-min.js"></script>
    

    我们使用:

    <%= this.Script("~/Scripts/jquery-min.js") %>
    

    方法吐出如下:

    <script type="text/javascript" src="/bundles/scripts/jquery__min_js?v=...."></script>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-03
      • 2020-06-05
      • 2019-06-15
      • 1970-01-01
      • 2013-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多