在大型RIA中,动态加载技术十分重要,毕竟几百K甚至M级别的脚本让用户等待加载完了再浏览是不可能。大多数成熟的framework都有动态加载技术的实现,就是说先加载一个最基础功能,然后利用动态加载技术慢慢把所有的脚本加进来,或者说需要某个功能的时候再加载某个模块所 需要的javascript的文件。
好了不说那么多P话了,尽快进入正题来看看GI是怎么实现的动态加载技术。其实GI的load做的很复杂,因为他涉及到了进度条阿等等很多乱七八糟的东西,我们就不分析那么多东西了,来看看他最基础的东西是如何实现的吧。GI中的动态加载有两种,一种是用的是XMLHttpRequest来请求脚本内容,另一种则是加入一个 <script>标签的方式来动态加入脚本以达到动态加载的功能。网上也许还有其它得方法,但其它得方法也都是根据这两种演变来的,还是以这两种为基础得。我们来看看它是如何实现的。
首先是第一种
/*
* @private @jsxobf-clobber
*/
ClassLoader_prototype.loadJSFileSync
=
function
(strURI) {
var
req
=
new
jsx3.net.Request();
req.open(
"
GET
"
, strURI,
false
);
req.send();
if
(req.getStatus()
==
jsx3.net.Request.STATUS_OK) {
var
script
=
req.getResponseText();
jsx3.eval(script);
return
true
;
}
return
false
;
};
呵呵,XMLHttpRequest的GET请求就可以吧js文件拿下来,然后用eval执行所有的脚本,这样就达到了加载脚本的功能了。方法也很简单,也就不用多介绍什么了吧。不过我记着有人说过js必须要UTG-8格式存的,但测试了一下,不用好像也可以。
我们看在package。Js文件里面有个有意思的方法
/*
*
* Ensures that one or more classes is available before this method returns. Any class that fails to load will
* throw an error. Only classes that can be found by the system class loader may be loaded in this manner.
*
* @param strClass {String} the fully-qualified names of the classes to load.
* @since 3.2
*/
jsx3.require
=
function
(strClass) {
for
(
var
i
=
0
; i
<
arguments.length; i
++
) {
var
className
=
arguments[i];
className
=
jsx3._REQUIRE_MAP[className]
||
className;
if
(jsx3.Class.forName(className)
==
null
)
jsx3.CLASS_LOADER.loadClass(className);
}
};
需要说明的是jsx3.ClASS_LOADER.loadClass()方法中其实就是调用了loadJSFileSync()方法,这个方法就是在调用类之前如果该类没有被加载将会在被load一次做到需要什么才用什么,不会把所有Javascipt都加载进来。
然后我们再来看第二种方法,添加标签的方式来load脚本
/*
* @private @jsxobf-clobber
*/
ClassLoader_prototype.loadScript
=
function
(strSrc, strId) {
//
instance a new DOM element
var
element
=
document.createElement(
"
script
"
);
element.src
=
strSrc;
element.type
=
'text
/
javascript';
element.id
=
strId;
element.language
=
'javascript';
//
set up onload handler
var
me
=
this
;
if
(
this
.IE) {
element.onreadystatechange
=
function
() {
var
state
=
this
.readyState;
if
(state
==
"
loaded
"
||
state
==
"
interactive
"
||
state
==
"
complete
"
) {
this
.onreadystatechange
=
null
;
me._loaded[strSrc]
=
true
;
me._publish({subject:
"
load
"
, type:
"
script
"
, src:strSrc});
}
};
}
else
if
(
this
.MOZ
||
this
.KON) {
element.onload
=
function
() {
element.onload
=
null
;
me._loaded[strSrc]
=
true
;
me._publish({subject:
"
load
"
, type:
"
script
"
, src:strSrc});
};
}
element.jsxid
=
strSrc;
//
bind the element to the browser DOM to begin loading the resource
document.getElementsByTagName(
"
head
"
)[
0
].appendChild(element);
};
呵呵,相信大家都能看懂吧,就是在head标签中加入了script标签,src当然就是重点了,而如何判断脚本加载结束,也明确的告诉我们,在IE下用onreadystatechange,在Firefox下用onload。呵呵,好像很简单吧。
到这里,我突然想到一个问题,script标签好像功能很强大啊,竟然可以用来读服务器上的Javascript文件,跟XMLHttpRequest有同样的功能啊,看来这个东西可以被我们来利用一下了,做点手脚也许会有不同的效果哦,来看。
首先我们写一个Handlers用来模拟一个Javascript文件,然后用这个handler来生成返回的内容,
<
httpHandlers
>
<
add
verb
="*"
type
="JsTest.JsHandler"
path
="demo2.js"
/>
</
httpHandlers
>
public
class
JsHandler:IHttpHandler,IRequiresSessionState
{
public
void
ProcessRequest(HttpContext context)
{
string
action
=
context.Request[
"
action
"
].ToString();
context.Response.Write(
"
alert(\
"
hello world
"
+ action +
"
\
"
);
"
);
}
}
这个Handler中我只是返回了一段alert,呵呵,下面用script来加载他吧,刚才看到的一样,只是script中src我们做了点手脚,嘿嘿指向的是demo2.js?action=city22,哈哈看到了吧,就是个GET访问方式啊!
window.onload
=
function
()
{
var
script
=
document.createElement(
"
script
"
);
script.type
=
'text
/
javascript';
script.language
=
'javascript';
script.src
=
"
demo2.js?action=city22
"
;
document.getElementsByTagName(
"
head
"
)[
0
].appendChild(script);
}
测试成功,哈,看来还真是能模拟一个XMLHttpRequest了。
不过有人肯定该问了,这个有什么用XMLHttpRequest已经很好用了啊,干嘛在来个这东西。我感觉这东西在一定的领域还是能发挥作用的。XMLHttpRequest不能垮站访问,这是个问题,也就是说你在www.a.net/index.html上写了一段XMLHttpRequest不能调用www.b.net上的任何东西,即使改动document.domian 也不可以,唉多可惜。当然了Iframe给我们解决了这个问题,但是script标签又给了我们新的选择,我们完全可以模拟open(),send(),等这些方法,返回的脚本中使用变量作为返回内容来模拟responseText。看来跨站问题又有了新的解决思路了。以后script标签值得我们去深入研究一下咯。