科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件Atlas 实现机制浅析(一)

Atlas 实现机制浅析(一)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

 与 .NET 和 Java 平台下其它 AJAX 框架相比,Altas 最大的亮点就在于与 ASP.NET 现有机制的无缝融合。通过 VS.NET 集成开发环境,使用者可以在对 js 和 AJAX 不甚了解的情况下,以非常自然的方式使用到最先进的技术。

作者:IT专家社区 来源:IT专家网 2008年5月20日

关键字: 机制 实现 Atlas net Windows

  • 评论
  • 分享微博
  • 分享邮件
与 .NET 和 Java 平台下其它 AJAX 框架相比,Altas 最大的亮点就在于与 ASP.NET 现有机制的无缝融合。通过 VS.NET 集成开发环境,使用者可以在对 js 和 AJAX 不甚了解的情况下,以非常自然的方式使用到最先进的技术。此外直接在 js 一级提供 WebService 的调用支持,也大大降低了对 ws 技术的使用门槛。而 ASP.NET 中一直引以为豪的数据绑定等技术,也可以在 Altas 中无缝得到支持,让现有投资能够最大限度得到保护。从这些意义上来说,虽然 Altas 在 AJAX 理念上没有太多突破,但不失为一个强大且实用的 AJAX 框架,非常符合 MS 在技术运用上的一贯原则。

  整体结构

  从整体结构上来看,Altas 的核心在于 <atlas:ScriptManager .../> 这个标签,所有支持 Altas 的页面都必须有且只有一个此标签,以引入 Altas 的基础架构支持。在此基础上,通过 <altas:UpdatePanel .../> 标签定义需要异步更新的范围,避免传统 Post Back 模式下的全页面刷新。而需要支持 AJAX 模式获取数据的控件,则可以通过 js 脚本和 xml 脚本两种方式定义,并由 Altas 框架进行动态 patch 以实现标准 web 控件的 AJAX 支持。此外就是 WebService 调用和数据绑定的支持机制,也是利用 Altas 框架的基础架构实现的。

  ScriptManager

  首先,ScriptManager 是一个容器,用户可以在 ScriptManager 标签下定义期望引用的其它 js 库,以及希望通过 js 直接调用的 WebService 服务。

  例如在如下的定义中,ScriptManager 控件将保存对两个客户端 js 库和 ComplexService 服务的引用,并在页面 Render 的时候写入适当的支持代码。我们可以通过 ScriptManager.Scripts 和 ScriptManager.Services 属性访问类似定义。

以下内容为程序代码:
<atlas:ScriptManager runat="server" ID="UpdatePanel2" 
  EnableScriptComponents="True" EnablePartialRendering="True">
  <Scripts>
    <atlas:ScriptReference ScriptName="AtlasUIMap" />
    <atlas:ScriptReference Path="~/MyScripts/MyScript.js" />
  </Scripts>
  <Services>
    <atlas:ServiceReference Path="ComplexService.asmx" />
  </Services>
</atlas:ScriptManager>


  其中 ScriptReference 非常简单,支持通过 ScriptName 或 Path 属性指定脚本。

  ScriptName 指定 Altas 内建的库名称,在 FrameworkScript 类型中有具体定义。这个属性在有的文档和例子中,也直接称为 Name 属性,但最新的 Altas M1 中已改为 ScriptName。这个脚本类型将被通过 ScriptManager.ConvertFrameworkScriptToFileName 函数转换为对应的 js 文件名。

以下内容为程序代码:
public enum FrameworkScript
{
      Custom,
      AtlasUIDragDrop, // "AtlasUIDragDrop.js";
      AtlasUIGlitz, // "AtlasUIGlitz.js";
      AtlasUIMap // "AtlasUIMap.js";
}

 
  如果直接使用 Path 则可以指定任意的用户自定义库。此外还可以通过 ScriptReference.Browser 属性指定脚本适用于的浏览器,Altas 将根据客户端浏览器类型,自动选择加载合适的脚本。

  而 ServiceReference 也非常类似,可以通过 Path 和 Type 属性指定 WebService 的 .asmx 路径和相关类型。如果 GenerateProxy 属性为 true 的话(缺省),则 ScriptManager 会为此服务自动生成 proxy 包装脚本;否则将依赖于后台的自动处理机制提供支持。具体的 WebService 实现原理,等后面进行分析时在详细解释。目前需要知道的是,如果打开 GenerateProxy 模式,则 Altas 会自动生成 proxy 包装脚本,并与 Scripts 中脚本一同在合适的时候写到页面。

  除了 Scripts 和 Services 两类显式的元素外,ScriptManager 还提供 IScriptService 和 IScriptControl 两类接口实现对象的管理。

前者提供 Altas 自身的服务支持,例如用于提供诊断 API 的 ProfileScriptService 组件。
后者提供 Altas 服务端控件支持,例如用于服务端定时器的 TimerControl 控件。

  所有这些涉及脚本的引用,都会在 ScriptManager.OnPagePreRenderComplete 事件中,调用 RenderXmlScript 方法写入到一个 xml 脚本中。

以下内容为程序代码:
<script src="ScriptLibrary/Atlas/Debug/Atlas.js" type="text/javascript">
...
<script type="text/xml-script">
<page xmlns:script="http://schemas.microsoft.com/xml-script/2005">
  <references>
    <add src="ScriptLibrary/Atlas/Debug/AtlasUIMap.js" />
    <add src="MyScripts/MyScript.js" />
    <add src="ComplexService.asmx/js" />
  </references>
  ...
</page>

  值得注意的是,Altas 会自动根据 web.config 中 system.web/compilation 的配置,选择 Debug 或 Release 模式的脚本。Release 模式脚本删去了多余的空格等修饰负荷,少了一些调试方面的支持。如果希望对 Altas 的脚本直接进行修改,别忘了两个版本的代码进行同步。

  此外,ScriptManager 是一个协调者,它自身维护了一些常用的状态,并会根据状态来切换 Altas 引擎的工作机制。

  最常使用的是 EnablePartialRendering 和 EnableScriptComponents 属性。

  EnablePartialRendering 属性决定是否启用局部重绘的模式。

  传统的 Post Back 模式页面,在用户 submit 时会重绘整个页面,并导致浏览器显式的闪烁。而在基于 AJAX 技术的 Altas 框架中,可以通过 UpdatePanel 标签指定需要重绘的局部。这样一来页面在处理请求时,会首先根据 ScriptManager.IsInPartialRenderingMode 属性判断是否在重绘模式中。如果在重绘模式,则仅仅将需要重绘的 UpdatePanel 内容,返回给客户端浏览器,并由 Altas 自动进行内容的更新。通过这种模式,使用者可以在对代码几乎无需修改的情况下,直接享受到 AJAX 带来的客户端用户体验的提升。 EnableScriptComponents 属性决定是否启用 XML 脚本模式。

  XML 脚本模式是 Altas 引入的基于 XML 的描述性组件定义模型,可以通过一组 XML 标签,定义页面中已有 Web 组件的 AJAX 行为,而无需对现有组件进行修改和调整。而且因为所有的行为都是由 Altas 引擎在客户端动态绑定,所以组件的目标也可不仅仅限于现有的 Web 组件。具体的介绍可以参考 Atlas XML Script。而对于某些特殊情况,例如 ASP.NET 2.0 中的 master 页面,可以通过此属性关闭 XML 脚本支持,以大幅度简化页面的功能,此时 Altas 会自动使用 AtlasRuntime.js (57K) 替换完整的 Atlas.js (174K) 脚本。

  最后,在了解了 ScriptManager 的基本职责后,我们来看看它的实现。

以下内容为程序代码:
public interface IScriptControlContainer
{
  IScriptControl RegisterControl(Control control);

public class ScriptManager : System.Web.UI.Control, IScriptControlContainer, IPostBackDataHandler {...}

  ScriptManager 是一个 Web 界面控件,可以直接在 VS.NET 的设计界面中进行调整;它实现了 IScriptControlContainer 接口以作为 IScriptControl 的容器,对注册到容器的不支持 IScriptControl 接口的类型,将自动建立 GenericScriptControl 进行包装;实现 IPostBackDataHandler 接口则是用于在 Post Back 时处理局部重绘的支持。

  在重载的 Control.OnInit 方法中,将根据页面请求头中 delta 属性是否为 true 来判断,当前控件是否处于局部重绘中。如果是局部重绘模式,则关闭页面的 trace 模式,并接管页面 LoadComplete 事件,并最终根据每个 UpdatePanel 的重绘状态,返回实际的重绘结果。此外无论是否局部重绘,都会接管页面的 PreRenderComplete 事件,以便完成前面提到的 Altas.js 和 XML 脚本的输出。伪代码如下:

以下内容为程序代码:
protected override void OnInit(EventArgs e)
{
// 当不处于设计模式,且控件属于某个页面时
if (!DesignMode && (_page != null))
{
// 判断页面中是否只有一个 ScriptManager 实例,否则抛出异常
// 如果页面请求中 delta 属性为 true 则处于重绘模式
if (_page.Request.Headers["delta"] == "true"[img]/images/wink.gif[/img]
{
_inPartialRenderingMode = true; // 处于重绘模式
      _page.TraceEnabled = false; // 关闭 trace 支持
      
      // 根据每个 UpdatePanel 的重绘状态,返回实际的重绘结果
      _page.LoadComplete += new EventHandler(this.OnPageLoadComplete); 
}
// 完成前面提到的 Altas.js 和 XML 脚本的输出
_page.PreRenderComplete += new EventHandler(this.OnPagePreRenderComplete);
}
}在重载的 Control.OnPreRender 方法中,将针对一系列约束条件进行检查。
  •   首先,页面必须有 <form runat="server"> 标签,否则 ASP.NET 无法建立顶级 form 供 Altas 接管相应 submit 事件。
  •   其次,如果在局部重绘模式中,则不对页面提供 Post Back 后滚动位置的维护支持,打开此模式则抛出异常。
  •   然后,如果在局部重绘模式中,则页面必须有 <head runat="server"> 标签,以便将名为 .atlas__delta 的 CSS style 挂靠在上面。不过这个限制似乎牵强了一点,目前还不知道为什么必须如此,待进一步分析。

  实现的伪代码如下:

以下内容为程序代码:
protected override void OnPreRender(EventArgs e)
{
// 页面必须有 <form runat="server"> 标签,否则 ASP.NET 无法建立顶级 form 供 Altas 接管相应 submit 事件
if (Page.Form == null)
throw new InvalidOperationException("Must have a <form runat=\"server\">"[img]/images/wink.gif[/img];
// 是否在局部重绘模式中
  if (EnablePartialRendering)
  {
   // 不对页面提供 Post Back 后滚动位置的维护支持,打开此模式则抛出异常
if (Page.MaintainScrollPositionOnPostBack)
throw new InvalidOperationException("MaintainScrollPostition is not supported on pages with partial rendering turned on."[img]/images/wink.gif[/img];
// 页面必须有 <head runat="server"> 标签,以便将名为 .atlas__delta 的 CSS style 挂靠在上面
if (Page.Header == null)
throw new InvalidOperationException("Must have a <head runat=\"server\">"[img]/images/wink.gif[/img];
// 名为 .atlas__delta 的 CSS style
    Style style = new Style();
style.Font.Name = "Lucida Console";
    Page.Header.StyleSheet.CreateStyleRule(style, null, ".atlas__delta"[img]/images/wink.gif[/img];
}
}

  在页面重绘的准备工作完成后,OnPagePreRenderComplete 方法会被调用。

  如果是在局部重绘模式中,则直接接管 Page 的 Render 方法。

  否则将根据浏览器类型,以及是否启用 XML 脚本模式来选择加载合适的 Altas 核心脚本。

  最后,如果存在任意一种脚本服务、控件或引用,则调用 RenderXmlScript 函数输出 XML 脚本。然后会输出客户端代理脚本或局部重绘模式的初始化脚本。 实现的伪代码如下:

以下内容为程序代码:
private void OnPagePreRenderComplete(object sender, EventArgs e)
{
// 是否在局部重绘模式中
if (_inPartialRenderingMode)
{  
// 接管 Page 的 Render 方法
    Page.SetRenderMethodDelegate(new RenderMethod(RenderPageCallback));
    return;
  }
// 获取客户端浏览器类型
string browser = _page.Request.Browser.Browser;
if (browser == "IE"[img]/images/wink.gif[/img]
RegisterFrameworkScript("AtlasCompat.js"[img]/images/wink.gif[/img];
else if(browser == "AppleMAC-Safari"[img]/images/wink.gif[/img]
RegisterFrameworkScript("AtlasCompat2.js"[img]/images/wink.gif[/img];
// 是否启用 XML 脚本模式
if (_effectiveEnableScriptComponents)
RegisterFrameworkScript("Atlas.js"[img]/images/wink.gif[/img];
else
RegisterFrameworkScript("AtlasRuntime.js"[img]/images/wink.gif[/img];
if (存在任意一种脚本服务、控件或引用)
{
StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
// 输出 XML 脚本
    writer.Write("<script type=\"text/xml-script\">"[img]/images/wink.gif[/img];
    RenderXmlScript(writer);
    writer.Write(""[img]/images/wink.gif[/img];
// 是否需要输出初始化脚本
string proxyScript = GetClientProxyScript();
if (proxyScript != null || _enablePartialRendering)
{
writer.Write("<script type=\"text/javascript\">"[img]/images/wink.gif[/img];
// 输出客户端代理脚本
if (proxyScript != null)
writer.Write(proxyScript);
// 输出局部重绘模式初始化脚本
writer.WriteLine("Web.WebForms._PageRequest._setupAsyncPostBacks(document.getElementById('" + _page.Form.ClientID + "'), '" + UniqueID + "');"[img]/images/wink.gif[/img];
writer.Write(""[img]/images/wink.gif[/img];
}
// 将上述脚本注册到页面的客户端脚本管理器
_page.ClientScript.RegisterStartupScript(typeof(ScriptManager), "ScriptManager", writer.ToString(), false);   
}
}

  至此,我们对 Altas 的核心组件 ScriptManager 的大致结构已经有了初步的了解,接下来会针对基于 UpdatePanel 的局部重绘模式、基于 XML 脚本的声明式定义、基于 XMLHTTP 的后台数据通讯机制、以及 WebService 和 DataBinding 支持的实现机制进行分析。

    • 评论
    • 分享微博
    • 分享邮件
    闂傚倸鍊搁崐鎼佸磹妞嬪孩顐介柨鐔哄Т閻骞栧ǎ顒€濡肩紒鎰殜閺岋繝宕堕埡浣锋睏闂佸搫顑呴柊锝夊蓟閺囷紕鐤€閻庯綆浜炴禒鐐節濞堝灝鐏犻柕鍫熸倐瀵寮撮敍鍕澑闁诲函缍嗘禍鏍磻閹捐鍐€妞ゆ挶鍔庣粙蹇涙⒑鐠恒劌娅愰柟鍑ゆ嫹

    婵犵數濮烽弫鍛婃叏閻戝鈧倹绂掔€n亞鍔﹀銈嗗坊閸嬫捇鏌涢悢閿嬪仴闁糕斁鍋撳銈嗗坊閸嬫挾绱撳鍜冭含妤犵偛鍟灒閻犲洩灏欑粣鐐烘⒑瑜版帒浜伴柛鎾寸洴閹儳煤椤忓應鎷洪梻鍌氱墛閸楁洟宕奸妷銉ф煣濠电姴锕ょ€氼參宕h箛鏃傜瘈濠电姴鍊绘晶娑㈡煕鐎c劌濡介柕鍥у瀵粙濡歌閳ь剚甯¢弻鐔兼寠婢跺﹥娈婚梺鍝勭灱閸犳牠骞冨⿰鍫濈厸闁稿本绋撹ぐ瀣煟鎼淬値娼愭繛鍙壝悾婵堢矙鐠恒劍娈鹃梺鍓插亝濞叉牠鎮″☉銏$厱閻忕偛澧介惌瀣箾閸喐鍊愭慨濠勭帛閹峰懐绮电€n亝鐣伴梻浣规偠閸斿宕¢崘鑼殾闁靛繈鍊曢崘鈧銈嗗姂閸庡崬鐨梻鍌欑劍鐎笛呯矙閹寸姭鍋撳鐓庡籍鐎规洑鍗冲畷鍗炍熼梹鎰泿闂備線娼ч悧鍡涘箠鎼淬垺鍙忔い鎺嗗亾闁宠鍨块崺銉╁幢濡炲墽鍑规繝鐢靛О閸ㄦ椽鏁嬮柧鑽ゅ仦娣囧﹪濡堕崨顔兼闂佺ǹ顑呴崐鍦崲濞戙垹骞㈡俊顖濐嚙绾板秹鏌f惔銏e妞わ妇鏁诲璇差吋閸偅顎囬梻浣告啞閹搁箖宕版惔顭戞晪闁挎繂顦介弫鍡椼€掑顒婂姛闁活厽顨嗙换娑㈠箻閺夋垹鍔伴梺绋款儐閹瑰洭寮婚敐鍛婵炲棙鍔曠壕鎶芥⒑閸濆嫭婀扮紒瀣灴閸╃偤骞嬮敃鈧婵囥亜閺囩偞鍣洪柍璇诧功缁辨捇宕掑▎鎴濆濡炪們鍔岄幊姗€骞嗗畝鍕<闁绘劙娼х粊锕傛煙閸忚偐鏆橀柛鏂跨焸閹偤宕归鐘辩盎闂佸湱鍎ら崹鐢割敂閳哄懏鍊垫慨姗嗗墻濡插綊鏌曢崶褍顏€殿喕绮欐俊姝岊槼闁革絻鍎崇槐鎾存媴缁涘娈┑鈽嗗亝缁诲牆顕f繝姘亜缁炬媽椴搁弲锝夋偡濠婂啰效闁诡喗锕㈤幊鐘活敆閸屾粣绱查梺鍝勵槸閻楀嫰宕濇惔锝囦笉闁绘劗鍎ら悡娑㈡倶閻愯泛袚闁哥姵锕㈤弻鈩冩媴閻熸澘顫掗悗瑙勬礈閸犳牠銆佸鈧幃鈺呮惞椤愩倝鎷婚梻鍌氬€峰ù鍥х暦閸偅鍙忛柟鎯板Г閳锋梻鈧箍鍎遍ˇ顖炲垂閸岀偞鐓㈡俊顖滃皑缁辨岸鏌ㄥ┑鍡╂Ц缂佲偓鐎n偁浜滈柡宥冨妿閳藉绻涢崼鐔虹煉婵﹨娅e☉鐢稿川椤斾勘鈧劕顪冮妶搴′簼婵炶尙鍠栧畷娲焵椤掍降浜滈柟鍝勬娴滈箖姊洪幐搴㈢┛濠碘€虫搐鍗遍柟鐗堟緲缁秹鏌涢锝囩畼妞ゆ挻妞藉铏圭磼濡搫顫岄悗娈垮櫘閸撴瑨鐏冮梺鍛婁緱閸犳岸宕㈤幖浣光拺闁告挻褰冩禍浠嬫煕鐎n亜顏柟顔斤耿閺佸啴宕掑☉姘箞闂佽鍑界紞鍡涘磻閸℃ɑ娅犳い鎺戝€荤壕濂告煕鐏炲墽鈽夌紒妞﹀洦鐓欓柣鐔告緲椤忣參鏌熼悡搴㈣础闁瑰弶鎸冲畷鐔兼濞戞瑦鐝¢梻鍌氬€搁崐椋庣矆娓氣偓楠炴牠顢曢妶鍌氫壕婵ê宕崢瀵糕偓瑙勬礀缂嶅﹪寮婚崱妤婂悑闁告侗鍨界槐閬嶆煟鎼达紕鐣柛搴ㄤ憾钘濆ù鍏兼綑绾捐法鈧箍鍎遍ˇ浼存偂閺囥垺鐓涢柛銉e劚婵$厧顭胯閸ㄤ即婀侀梺缁樓圭粔顕€顢旈崼鐔虹暢闂傚倷鐒︾€笛呮崲閸屾娑樜旈崨顓犲幒闂佸搫娲㈤崹娲偂閸愵亝鍠愭繝濠傜墕缁€鍫熸叏濡寧纭鹃柦鍐枛閺屾洘绻涜鐎氱兘宕戦妸鈺傗拺缂備焦锚婵洦銇勯弴銊ュ籍闁糕斂鍨藉鎾閳ユ枼鍋撻悽鍛婄叆婵犻潧妫楅埀顒傛嚀閳诲秹宕堕妸锝勭盎闂婎偄娲︾粙鎰板箟妤e啯鐓涢悘鐐靛亾缁€瀣偓瑙勬礋娴滃爼銆佸鈧幃銏$附婢跺澶�

    重磅专题
    往期文章
    最新文章