2011年11月30日,星期三

Avoiding bugs from cached JavaScript和CSS文件 in SharePoint

这篇文章很详细,所以在这里’执行摘要– 如果您将自定义JavaScript / CSS文件等存储在SharePoint下的某个位置’s ‘layouts’文件夹(或将其手动上传到站点内的库中),请确保每次更新时URL都会更改。通常使用网址中的querystring参数来完成此操作,例如/_layouts/MyProject/JS/MyScript.js修订版= 2011.11.20.1

2011年12月5日更新: 更正-如果您仅使用Microsoft的ScriptLink控件向页面添加JavaScript引用,则如果将.js文件存储在页面中,则该问题不会对您造成影响“layouts”。但是,如果将它们存储在库中,并且每次文件更改时都没有回收IIS,这将会自动发生-对于WSP部署,它应该自动发生。“layouts”当然。您可能会发现,并非总是可以单独使用ScriptLink-例如,在我当前的项目中,我们在JavaScript文件之间有很多依赖关系,并且ScriptLink不允许您指定自定义.js文件应以的顺序(顺序)已添加到页面(AFAIK)。因此,尽管您的行程可能会有所不同,但我认为本文的主旨仍然适用于许多人。无论如何,非常感谢Mahmoud Hamed在下面的评论中发现了测试结果中的缺陷-我已经更新了下表。

对于我从事的大多数项目,我更喜欢将JavaScript / CSS等存储在SharePoint根文件夹下某处的文件系统上(例如“14”),通常在“layouts”文件夹-对我来说,这些文件通常是“site infrastructure”因此对于整个平台的品牌/功能至关重要。也就是说,在某些情况下,我’d走另一条路线并将其存储在网站集中(因此存储在内容数据库中)– these would be:

  • 对于仅适用于一个网站(例如团队网站)的CSS / JS文件
  • 沙盒解决方案

当我概述这些原因时 这个问题的答案,投票显示出很多同意,总体上,我看到很多人都在使用这种方法。但是,我’ve最近注意到,许多人没有意识到关键的陷阱– 和it’如此重要,我’我不断感到惊讶’s doesn’不再讨论。问题是 end users 不 seeing updated JavaScript和CSS文件 following a recent code deployment。好吧,好吧,我对文章标题有些作弊–实际上,问题不是SharePoint特有的’s “layouts”文件夹(甚至是IIS),当我说“JavaScript和CSS文件”,我的意思是说JavaScript / CSS /图像/音频/视频,实际上是大多数静态文件。但是,嘿,有时以SharePoint的方式来组织主题是提醒人们网络的一般行为也适用于我们的最佳方法‘special’SharePoint类型。似乎许多人可能会错过这些内容,因为他们专注于某些超酷的SharePoint功能区开发,并且在技术阅读方面,我认为’可以公平地说,许多人几乎只关注SharePoint内容。

问题

和许多生活一样’最简单的陷阱,这一陷阱是如此容易陷入。请记住,它仅适用于进行一定程度的自定义(例如品牌/自定义代码)并将文件存储在文件系统上的SharePoint项目–如果这两件事不’不适用于您,问题完全出在Microsoft上,他们会处理文件的问题,因此’没什么好担心的。否则,请想象以下内容:

  1. 开发人员对一些自定义代码进行了更新–先前部署的文件将更新。让’s say we’重新部署一些更新的JavaScript和CSS。
  2. 关联的WSP部署到SharePoint,并且文件在场中的所有Web服务器上更新。
  3. 用户只有进行硬刷新(CTRL + F5)才能看到Javascript / CSS的更改。 Worse still, they could experience 脚本 bugs or broken formatting which could make the application completely unusable.

这种情况通常会导致经常听到的问题“嗯,如果您执行CTRL + F5会怎样?”。当然,对于与开发人员联系的测试用户来说,这一切都很好。在生产中’对于组织的其余部分肯定不是很好–奇怪的是,要求30,000名信息工作者进行CTRL + F5在企业中进展不佳!在Internet / WCM场景中情况变得更好– would you 听到站点用户的JavaScript错误或CSS错误?该消息实际上是否会传达给实施团队(’记得,可能是 看到事件日志中的错误(来自此内容)?还是用户会去别的地方?

原因

发生这种情况的原因是因为第一次请求文件时,Web服务器(当然,这里是IIS,但对大多数服务器也是如此)使用HTTP标头为文件提供服务,该标头指定可以长时间缓存该文件。为了“layouts”文件夹,默认为31536000秒(1年):

CacheControlHeader
浏览器和代理通常会遵循此要求,因此在后续页面请求中,此文件将从‘临时网络文件’ on the user’的计算机或用户与服务器之间的代理。当然,如果没有此功能,网页加载速度将大大降低。我的测试表明,通常,内容数据库提供的文件没有使用相同的标头提供,因此更新会立即显示。然而,事实证明 SharePoint 2010(可能更早)将使用功能部署的文件与手动上传到同一库的文件的处理方式不同. My table below 在 tempts to explain most of my testing 和results:

位置 启用Blob缓存? 结果 笔记
样式库(发布站点) 没有
样式库(发布站点)
样式库(发布站点) 是的(现在已达到最大年龄) 按功能添加
样式库(发布站点) 是的(现在已达到最大年龄) 问题 手动添加到库
版面 脚本链接 DID添加查询字符串?rev = q%2Fb304kubfwrNJVD%2BdYdxg%3D%3D 但不清楚何时更新–文件更改时不!
版面 没有 问题 无论如何,BLOB缓存仅与内容数据库中的文件相关
定制图书馆 问题 手动添加到库
定制图书馆 按功能添加

Conclusions 和notes:

  • 该问题适用于‘layouts’或库中的文件 没有使用功能中的模块标签部署的(或至少是通过浏览器上传的)
    • N.B.我确实确实在Reflector中进行了挖掘,以找到此行为的根源,但是没有’t find it.
  • 对于JavaScript文件,它不会’不管文件如何添加到页面(例如 脚本链接 控制或普通<script> tag)
  • BLOB缓存不做任何更改
  • 沙盒解决方案或服务器场解决方案未做任何更改
  • 注意–已在SharePoint 2010 RTM(旧)和SharePoint 2010 SP1 + 八月 CU(在撰写本文时为全新)上进行了测试

对于受影响的情况,为避免浏览器/代理尽管文件被更新而仍缓存文件,无论文件链接到何处,只需在每个部署中添加/更改查询字符串值。因此,而不是像这样:

  • /_layouts/MyProject/JS/MyScript.js

..我们需要类似以下内容之一:

  • /_layouts/MyProject/JS/MyScript.js修订版= 1.0.0.0
  • /_layouts/MyProject/JS/MyScript.js修订版= 2011.11.20.1

参数可以有任何名称(尽管‘rev’ or ‘revision’是很常见的),但重要的是在每次修改时都以某种方式对其进行更改/增加。如果你’重新增加程序集版本(使用 AssemblyFileVersion 而不是主要版本号 记住),那么使用相同的值可能是合适的–在我当前的项目中,我们在某些地方执行此操作*(稍后会详细介绍),以便我们不要’不必记住手动增加上述链接。实际上,我们使用代码来读取当前的程序集文件版本,并将其附加为‘Rev’querystring值(此值将是TFS自动生成的当前内部版本的内部版本号)。当然,要权衡一下,您可能会强迫用户在更新后的第一时间重新下载文件。’t actually changed – but it’比有错误更安全。顺便说一句,您可能已经注意到Microsoft通过指向CSS / JS文件的链接来执行此操作–它们的值将在Service Pack /累积更新等中更新:

SharePoint2010_JavaScriptLinks

试图以更好的方式解决问题

因此,我们可以通过将querystring添加到链接到CSS / JS文件的任何URL上来解决问题,太好了!但是,在许多情况下,这意味着在每次部署之前,请记住在Visual Studio中的所有具有指向这些文件链接的位置上进行查找/替换。这不是’t ideal –在我们当前的项目中,这可以是母版页,用户控件,使用 ClientScriptManager 和许多其他位置。理想的情况是,如果所有这些问题都能在每次发行时得到照顾!

我没有’t spent too much time on this, but in our project we do use a solution for CSS files 在 least. All 我们的CSS files are emitted to the page 使用 SharePoint’s CSS注册链接 控制项–这意味着所有链接都是由一个控件生成的,这给我们带来了方便‘funnel’我们可以修改。因此,我们只是从 链接 控件,并且在我们的自定义类中,我们重写链接以使‘Rev’参数。使用的值与 AssemblyFileVersion 我们的‘core’程序集(也承载此控件),即CI流程会针对每个发行版进行更新。当然,您可以使用任何喜欢的方案:

   1: public class CacheSafeCssLinkControl : 链接
   2: {
   3:     protected override void Render(HtmlTextWriter output)
   4:     {  
   5:         使用 (HtmlTextWriter tempWriter = new HtmlTextWriter(new StringWriter()))
   6:         {
   7:             base.Render(tempWriter);
   8:             string html = tempWriter.InnerWriter.ToString();
   9:             if (!string.IsNullOrEmpty(html))
  10:             {
  11:                 html = html.ToLower().Replace(".css\"", string.Format(".css?Rev={0}\"", Core.Common.Utilities.GetAssemblyFileVersion(Assembly.GetExecutingAssembly())));   
  12:             }
  13:             output.Write(html);
  14:         }
  15:     }
  16: }

因此,这非常适合CSS文件。但是其他文件,特别是JavaScript呢?好吧,这里’在我们遇到障碍的地方– unfortunately Microsoft.SharePoint.WebControls.ScriptLink (JavaScript链接的等效控件)是密封的,因此我们可以’不要使用相同的方法。有趣的是,如果您很乐意使用反射(也许有一些缓存),则可以添加‘Rev’参数使用与我使用的相同技术 消除大型JS文件以优化SharePoint 2010 Internet网站 –因为链接的集合存储在 HttpContext.Current.Items, 那里’无需将Microsoft换出,就可以在将链接写入页面之前对其进行修改’s control.

I’d可能对此感到满意,但至少在我们项目中的真正问题是,JavaScript也倾向于通过其他途径添加到页面上,例如 ClientScriptManager 和<script> tags. We’d需要更新(大型)代码库以专用 脚本链接 以便我们有一个更有效的渠道。尽管如此,它仍使我对将来的工作方式有一个想法-但从长远来看,如果Microsoft会很好’自己的控件提供了一种添加消除缓存的查询字符串的方法。

概要

开发人员在开发过程中愉快地使用CTRL + F5,’经常会被忽略,最终用户将面临同样的问题,即默认情况下看不到更新的CSS和JavaScript文件。显然,这可能导致各种问题,包括如果页面HTML结构已更改但未使用相应的JavaScript的JavaScript错误。该问题主要在此类文件存储在‘layouts’文件夹,但有趣的是,文件也被手动上载到库中(至少在SharePoint 2010中)。关键是确保每次文件更新时URL都会更改,并且因为在那里查询字符串就足够了’无需实际重命名文件。最终它没有’无论您如何更改查询字符串值,是否’与CI关联的简单手动更新或自动化程度更高的过程。

10条评论:

马克·威尔逊说过...

It'还值得一提的是,IIS可能具有超出您在代码中执行的操作的缓存头。

必须对Fiddler进行良好的检查,并确定响应管道的哪个阶段正在设置标头。为了增加可伸缩性,我看到在几天之内设置了过期期限的站点,这些站点严重影响了升级应用程序的能力。

有时在升级过程中更改IIS缓存指令可以缓解这种情况。

马哈茂德·哈默德(Mahmoud Hamed)说过...

克里斯,你好
在我阅读了您的帖子并阅读了本节之后“ScriptLink DID添加查询字符串?rev = q%2Fb304kubfwrNJVD%2BdYdxg%3D%3D,但不清楚何时更新–文件更改时不”。我反映了ScriptLink和CssLink控件以查看它们如何呈现url,并且发现它们使用此方法。“SPUtility.MakeBrowserCacheSafeLayoutsUrl”which add the “rev” based on the file content so when you update the file the 转速 is changed. So just to make this work you need to add your files under the _layout folder do update reset IIS 和your url will be updated. I hope that this fix your problem.

李·戴尔说过...

克里斯,好帖子,不过一个问题。使用Blob缓存并显式设置max-age超时是否可以解决此问题?

我的意思是说您只在晚上部署,最大使用期限设置为一天。然后,当您的用户第二天到来时,他们的浏览器缓存将过期吗?

只是考虑一种解决问题的OOTB方法。

梅尔·洛塔(Mel Lota)说过...

克里斯,你好

很棒的帖子,我最近在我的一个项目中解决了同样的问题'm当前正在使用-不可否认不是SharePoint,但显然适用相同的原则。我最终通过使用中的FileUpdate任务将其集成到构建过程中'MSBuild社区任务'在部署时在每个母版页上的静态资源上更新查询字符串版本。 FileUpdate任务非常酷,因为它需要使用正则表达式来匹配路径:

http://geekswithblogs.net/mnf/archive/2009/07/03/msbuild-task-to-replace-content-in-text-files.aspx

干杯

克里斯·奥'Brien说过...

@标记,

同意-SharePoint'自己的缓存标题'layouts'目录是在IIS中添加的(不是通过任何内部SharePoint代码添加的),这是在SharePoint在IIS中创建网站时全部配置的。

不确定在部署前更改指令的想法是否有效。考虑到六个月前(并且此后从未访问过)的用户即使您使用了此技术,仍会收到陈旧文件,因为六个月前收到的标头说"cache for 1 year"。也许在其他情况下它很有用。

谢谢,

克里斯。

克里斯·奥'Brien说过...

@Mahmoud,

出色的侦探工作!我重新测试并看到了相同的行为。我认为这对于某些人来说可能会大大改变这个问题,很快我'将相应地更新文章。

然而, 有趣的是,'不能帮助我们的项目,因为我们不't仅将ScriptLink用于所有我们的JavaScript引用(我们使用ClientScriptManager + direct<script>也可以通过DelegateControl标记)。这是因为我们的JS文件中有一些依赖项, '只能使用ScriptLink控件来表达这些内容。也许我们错过了一些东西,但如果不是这样,我认为混合参考模型可能很常见-这意味着我的论点仍然存在。

谢谢,有很大的贡献!

克里斯。

克里斯·奥'Brien说过...

@李

我看到了您的想法,但不幸的是没有(我也对此进行了测试,请参见上面的表格)。实际上这是有道理的,因为重要的是文件时的标头 原来 服务(但是很久以前)。

干杯,

克里斯。

克里斯·奥'Brien说过...

@梅尔

真好知道这是否可以处理文件内容,因此只在绝对需要时才更改查询字符串(以便用户获得全部的缓存好处)吗?

谢谢,

克里斯。

安德斯·拉斯克(Anders Rask)说过...

显然,如果将CSS文件放置在1033 /样式或类似语言的特定文件夹下,而不是在_Layouts /样式下,则?rev =仅由CssRessources附加

月泪说过...

旧帖子,但应在此处添加,因为这在搜索排名中非常高。

Make use of SPUtility.MakeBrowserCacheSafeLayoutsUrl in SharePoint 2013: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.utilities.sputility.makebrowsercachesafelayoutsurl.aspx