dojo is not defined!

来源:互联网 发布:北京软星 知乎 编辑:程序博客网 时间:2024/04/20 07:14

使用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 headvar 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 headvar 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 headvar 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 headvar 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.