ScriptManager的ResolveScriptReference事件的重要作用

  还是对那个个人门户进行优化时遇到的问题。
  那是个较为大型的网站(假设其域名为 [url]http://www.sample.com[/url]),其中有多个频道。它的个人门户为( [url]http://spaces.sample.com[/url]),里面的每个模块都都由其它一个频道提供(例如音乐频道: [url]http://music.sample.com[/url])。每个频道维护自己的逻辑,然后只要根据个人门户制定的协议就能够在门户上建造一个Web Part。目前使用这种方式将门户的压力分担给其它频道,门户用于维护内容分离的信息与逻辑。
   具体的实现方式,还有其细节我不便多说。这里遇到的问题是,由于某些频道还会提供独立的页面内嵌在个人门户的管理页面的IFrame中(例如,用户通过 个人门户的页面来访问音乐频道提供的页面,用以管理自己的音乐信息)。这样,外部页面和内部页面两个不同站点均使用了ASP.NET AJAX,其造成的结果就是当打开一个页面时,很可能会需要将一些庞大的AJAX库下载两次。例如MicrosoftAjax.js,会通过 [url]http://spaces.sample.com/ScriptResouce.axd[/url]和 [url]http://music.sample.com/ScriptResource.axd[/url]下载。这直接导致了缓存的失效,用户需要下载大量的代码。
  哎,还好,查看了ScriptManager的代码之后,发现了一个非常简单的解决方案。我们先来看一下ScriptManager的相关代码:
ScriptManager.RegisterScripts
private void RegisterScripts()
{
List<ScriptReference> list1 = this.CollectScripts();

ScriptReference reference1 =
new ScriptReference("MicrosoftAjax.js", this, this);
ScriptManager.AddFrameworkScript(reference1, list1, 0);

if (this.PageRequestManager.IncludingWebFormsScript)
{
ScriptReference reference2 =
new ScriptReference("MicrosoftAjaxWebForms.js", this, this);
ScriptManager.AddFrameworkScript(reference2, list1, 1);
}

foreach (ScriptReference reference3 in list1)
{
this.OnResolveScriptReference(new ScriptReferenceEventArgs(reference3));
}

List<ScriptReference> list2 = ScriptManager.RemoveDuplicates(list1);
bool flag1 = false;
foreach (ScriptReference reference4 in list2)
{
string text1 = reference4.GetUrl(this, this.Control, this.Zip);
this.RegisterClientScriptIncludeInternal(
reference4.ContainingControl, typeof(ScriptManager), text1, text1);
if (!flag1 && reference4.IsFrameworkAssembly())
{
this.ConfigureApplicationServices();
flag1 = true;
}
}
}
 
  这就是ScriptManager用来注册 脚本的方法。幸运的是,它没多对MicrosoftAjax.js和MicrosoftAjaxWebForms.js(如果需要的话)两个程序集的资源 文件进行特殊处理,而是与其它用户指定的脚本文件“一视同仁”地进行注册。换句话说,它一样会通过ResolveScriptReference事件进行 处理,我们有机会在这个时候改变它!当然,具体有很多细节方面的内容就只能请大家自己去阅读代码和分析了。
  我编写了一个ScriptManager类 似的控件StaticScriptManager,会响应ScriptManager的ResolveScriptReference事件,在这里我们可 以改变它所引用的脚本文件路径。StaticScriptManager的代码非常简单,在这里就全部贴出来了。
StaticScriptManager
[PersistChildren(false)]
[ParseChildren(true)]
[NonVisualControl]
public class StaticScriptManager : Control
{
public static StaticScriptManager GetCurrent(Page page)
{
return (page.Items[typeof(StaticScriptManager)] as StaticScriptManager);
}

private bool _StaticScriptEnabled = true;

public bool StaticScriptEnabled
{
get { return _StaticScriptEnabled; }
set { _StaticScriptEnabled = value; }
}

protected override void OnInit(EventArgs e)
{
base.OnInit(e);

if (!this.DesignMode)
{
if (StaticScriptManager.GetCurrent(this.Page) != null)
{
throw new InvalidOperationException("One ContentPageManager per Page!");
}

this.Page.Items[typeof(StaticScriptManager)] = this;

ScriptManager.GetCurrent(this.Page).ResolveScriptReference +=
new EventHandler<ScriptReferenceEventArgs>(OnResolveScriptReference);
}
}

private void OnResolveScriptReference(
object sender, ScriptReferenceEventArgs e)
{
if (!this.StaticScriptEnabled)
{
return;
}

ScriptReference script = e.Script;

if (script.Name != "MicrosoftAjax.js"
&& script.Name != "MicrosoftAjaxWebForms.js"
&& script.Name != "MicrosoftAjaxTimer.js"
&& !String.IsNullOrEmpty(script.Assembly))
{
return;
}

string scriptPath = ConfigurationManager.AppSettings["Atlas_StaticScriptPath"];
if (String.IsNullOrEmpty(scriptPath))
{
return;
}

script.Path = scriptPath.EndsWith("/") ?
scriptPath + script.Name : scriptPath + "/" + script.Name;
}
}
 
  关键在于OnResolveScriptReference方法,将会判断当前脚本是否是程序集的内嵌脚本。如果是的话,则读取配置信息,并将脚本文件地址修改为指定的路径。例如,我们如果在配制文件里面设置Atlas_StaticScriptPath为 [url]http://static.sample.com/scripts/atlas/[/url],则在浏览器里查看HTML则会发现页面引入下面这个路径的脚本文件:
[url]http://static.sample.com/scripts/atlas/MicrosoftAjax.js[/url]
  如果在ScriptManager或者ScriptReference里设置ScriptMode为Debug,那么则会引入下面这个路径的脚本文件:
[url]http://static.sample.com/scripts/atlas/MicrosoftAjax.debug.js[/url]
  现在,只要在页面放入了StaticScriptManager这个控件,就可以让大量的应用访问同一个地址,用户就不会下载多次了。事实上,如果有一系列的大型应用会使用相同脚本的话,都应该让他们指向同一个脚本地址,尽可能减少用户的下载数据量。
  其实这只是ScriptManager类ResolveScriptReference事件的一个简单实用实例。试想一下,如果再结合一些详细的配置,成为一个专业的详细的脚本库也不是件困难的事情了。

本文出自 “赵��” 博客,转载请与作者联系!

你可能感兴趣的:(事件,职场,休闲,ScriptManager)