使用dojo工具包的步聚很简单,只有三步:
1. 在文档的head节引入dojo.js:
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js" type="text/javascript"></script>这里dojo.xd.js中的xd是cross domain loading的意思。当你使用dojo.xd.js时,dojo对在dojo.js后面加载的那些javascript文件使用cross domain loading技术,以利用browser的多线程下载,最终达到快速加载的目的。
2. 在head节中引用样式表声明:
<link rel="stylesheet" type="text/css" href="http://…/dojolib/dijit/themes/tundra/tundra.css">
3. 在<body>标签上添加样式声明:
<body class="tundra">
tundra是dojo自带的样式属性之一。
然后就可以在页面中使用dojo提供的各种API了。我把上面的方式称作静态链接到dojo工具包。在一些特殊场合,比如user script技术或者bookmarklet中引入dojo工具包,此时可称为动态链接到dojo工具包。
以Greasemonkey的用户脚本为例,新建一个用户脚本,使它对任意网页生效(但注意避开那些已经使用dojo工具包的页面),代码示例如下:
// Create script tag in head var dojoUrl = "http://example.com/dojolib/dojo/dojo.js"; var dojoStyleUrl = "http://example.com/dojolib/dijit/themes/tundra/tundra.css"; var script = document.createElement("script"); script.src = dojoUrl; document.getElementsByTagName("head")[0].appendChild(script); // Create link style in head var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = dojoStyleUrl; document.getElementsByTagName("head")[0].appendChild(link); unsafeWindow.addEventListener("load",function(event) { document.body.className += (" tundra"); var dojo = unsafeWindow["dojo"]; dojo.fadeOut({node:document.body}).play(); // This line will complain! });略微解释一下代码。首先代码在文档的head部分引入dojo.js和相应的样式文件。并通过addEventListener语句,在文档完成加载时,修改body标签,使之具有tundra样式。最后,它调用dojo的功能,试图使整个页面淡出。这里的unsafeWindow是Greasemonkey引入的一个包装对象,提供了大部分的'window'对象的属性和功能,我们只要将其视作'window'对象就可以了。
在你的站点上布署dojo工具包的1.6版的SDK版(注意为重现该错误,一定要布署SDK版)。运行上面的代码,会得到dojo is not defined错误。当然,这里的dojo只是个局部变量,但它的实质是指出了window['dojo'] is no defined。
啊哈!dojo加载不成功,问题出在哪儿?
SDK版的dojo.js只是一个纯粹的加载器。如果要说它还做了点别的事情,就是定义了dojoConfig这个变量。所以在上述有问题的代码执行出错时,仍然可以看到window.dojoConfig对象有定义。而release版的dojo.js则包含了dojo core的全部功能,即它的名字空间和包管理体系,一些语法糖衣,动画效果等。
SDK版的dojo.js负责加载下面4个重要的文件:
dojo/_base/_loader/bootstrap.js,dojo/_base/_loader/loader.js,dojo/_base/_loader/hostenv_browser.js,dojo/_base.js
加载方式就是在文档的head部分创建外部链接的<script>标签。问题就出在这里。如果你使用动态链接的方式,当页面加载完成后,你并不会在<head>标签里看到对这些文件的引用。这个结果是由于dojo的加载方式决定的。
当dojo.js试图加载上述脚本时,在browser环境(dojo支持各种运行时环境,如rihno,Firefox扩展等)下,它使用这样的语句创建标签:
var tmps = [ http://.../dojolib/dojo/_base/_loader/bootstrap.js,http://.../dojolib/dojo/_base/_loader/loader.js,http://.../dojolib/dojo/_base/_loader/hostenv_browser.js,http://.../dojolib/dojo/_base.js ] for (var x = 0; x < tmps.length; x++){ document.write("<scr"+"ipt type='text/javascript' src='"+tmps[x]+"'></scr"+"ipt>"); }
此时document.write()并不会导致有效地输出。document.write()只能在页面解析时,其输出才会被当作页面内容的一部分。在页面解析完毕后,修改文档内容只能通过DOM API来完成。而Greasemonkey调用user script的时间,应该就是文档解析完毕之后。
要避免/改正这个问题,可以:
1. 使用release版的dojo.js
2. 修改用户脚本,通过DOM API来加载上述4个文件。修改后的用户脚本如下:
// Create script tag in head var dojoUrl = "http://example.com/dojolib/dojo/dojo.js"; var dojoStyleUrl = "http://example.com/dojolib/dijit/themes/tundra/tundra.css"; var script = document.createElement("script"); script.src = dojoUrl; document.getElementsByTagName("head")[0].appendChild(script); var tmps = [ "http://example.com/dojolib/dojo/_base/_loader/bootstrap.js","http://example.com/dojolib/dojo/_base/_loader/loader.js","http://example.com/dojolib/dojo/_base/_loader/hostenv_browser.js","http://example.com/dojolib/dojo/_base.js" ]; for (var x in tmps){ var _script = document.createElement('script'); _script.src = tmps[x]; document.getElementsByTagName('head')[0].appendChild(_script); } // Create link style in head var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = dojoStyleUrl; document.getElementsByTagName("head")[0].appendChild(link); unsafeWindow.addEventListener("load",function(event) { document.body.className += (" tundra"); console.log(unsafeWindow["dojo"]); //this line will complains unsafeWindow["dojo"] is not defined });结论:SDK版的dojo.js使用了document.write()来创建script标签,从而实现dojo的加载。在某些场合下,document.write()语句并不会像期望的那样工作,应该改用DOM API来操作。上述问题我已当作一个bug提交给dojo开发人员,track ID为13970.