现代Dojo
你可能已经从Dojo走了一会儿,或者你一直在努力,让您的旧的Dojo1.6应用程序1.10下工作,你会发现自己不知道是怎么回事。你经常听到“AMD”和“毫无根据”的言论,但你不知道该怎么做或者从哪里开始。本教程将帮你。
入门
Dojo1.7是Dojo Toolkit的明显的转变。并走向更现代化的架构.而且Dojo 1.10继续沿着这条道路。虽然这是广义地向后兼容,以便充分利用Dojo1.10的优势,一些基本概念已经改变了。该概念在形成Dojo 2.0的基础上,现在采取这些概念,你可以确保在正确的长期道路上。此外,为了充分利用新的功能直接(像dojo/on
),则需要采取一些的这些“现代”的概念。
在本教程中我将试图解释一些已经在Dojo中引入的概念。我将把这些作为“过去”和“现代”Dojo。我会尽我所能来解释一下,并发生了变化,触摸的原因。虽然有些变化是根本性的,乍一看可能会造成混乱,他们都是有很好的理由让你的代码更高效,运行速度更快,更好地利用JavaScript和使你的代码更易于维护。我觉得这是值得的投资时间去了解“现代”Dojo。
本教程是不是专门的迁移指南,但更多的是底漆为您可能会需要考虑,如果你已经熟悉道场的概念。具体技术细节都在建的参考指南文档工作提供道场1.X到2.0迁移指南。
你好新世界
其中一个“现代”道场的核心概念是,事情在全局命名空间是坏的。这有很多原因,但在复杂的Web应用程序,全局命名空间很容易成为污染带的所有代码的方式,特别是当许多机构使用多个JavaScript框架。我甚至不会提及邪恶的东西,可以从安全角度来看发生了人故意修改全局命名空间。这意味着,在“现代”的Dojo,如果你将要访问的东西在全局命名空间停止,因为你正在做的事情是错误的。虽然向后兼容绝大多数的工具包暂时的作用域全球本身,它不应该被用于新的开发。
如果你发现自己使用dojo.*
或dijit.*
或者dojox.*
,是不对的。
这意味着,对于习惯于“传统”Dojo的开发人员来说,只是包括dojo.js和获取核心功能,然后需要在一些其他模块,只是键入dojo.something到其他的内容已经不在了(或者真的应该是,因为虽然它将广泛工作,直到2.0,这是一个坏东西)。
再次,重复后我“的全局命名空间是坏的,全局命名空间是不好的,我也不会用全局命名空间,我不会用全局命名空间”。
另一个核心概念就是同步操作是缓慢和异步的是通常较快。“过去”Dojo已经有了与概念异步JavaScript代码相当强劲的血统dojo.Deferred
,但在“现代”的道场,最好是觉得一切异步运行的。
为加强道场的模块化和利用上面的概念,在1.7道场采用称为异步模块定义(AMD)的CommonJS的模块定义。这意味着道场模块加载通常是通过暴露的基本重写require()
和define()
功能。你可以找到的完整文档的参考指南中加载程序。这从根本上改变代码的结构方式。
让我们举个例子,我们的东西在将“过去”这样做:
dojo.ready(function(){
dojo.byId("helloworld").innerHTML = "Hello World!";
});
现在,让我们来看看现代版的这一点:
require(["dojo/dom", "dojo/domReady!"], function(dom){
dom.byId("helloworld").innerHTML = "Hello New World!";
});
欢迎来到新世界。对“Dojo”的基础是require()
功能。它创建JavaScript代码封闭,并为它提供了它所需要做的工作是作为参数传递给函数返回变量的模块。通常情况下,第一个参数是模块ID(MID)的阵列,第二是功能。在require()
封闭区间内,我们参考的基础上,我们在参数中声明的变量名的模块。不但我们可以调用任何的模块,而且也有一些使用和参考指南中往往又是一些常见的约定。
加载器,就像过去的一样,它把查找模块,它加载和管理加载模块。
以你的敏锐的眼睛会发现有一个“模块”的里,没有一个返回变量,叫dojo/domReady!
。这实际上是用来控制加载器的行为的加载“插件”。这确保了加载器的等待,直到该网页的DOM结构已准备就绪。在一个异步的世界里,这不是一个聪明的主意,认为你的DOM结构是存在的,如果你想操纵它,所以如果你打算做与DOM的东西在你的代码,请确保您有这个插件。因为我们没有在代码中使用该插件,它是把它在数组的结尾,并没有为它提供一个返回变量的标准惯例。
目前只存在dojo/domReady!
插件,但可能无法满足你的代码才能正常工作(因为有其他事情,像一些A11Y特征检测的,可能未运行)。因此,使用dojo/ready
使用的东西,如当dojo/parser
和dijit有些是明智的。
您也可以获得一个模块的引用与require()
后该模块已经加载,通过只供应MID作为一个字符串参数。这不会加载模块,如果尚未加载它会抛出一个错误。你不会看到在Dojo Toolkit的这种编码风格,因为我们喜欢在代码中集中管理所有的依赖。但是,如果你选择使用此替代语法会是这个样子:
require(["dojo/dom"], function(){
// some code 一些代码
var dom = require("dojo/dom");
// some more code 更多代码
});
在AMD的其他核心功能是define()
它通常用于定义模块。见定义模块教程如何使用的详细信息define()
。
Dojo的基础和核心
与“现代”Dojo打交道时,你可能会听到“毫无根据”。这是说保证模块不依赖于任何更多的“基础”Dojo的功能比它需要。在“过去”的世界里,有基础相当多的功能在dojo.js
,并且实施上它仍然在那里,直到至少Dojo 2.0,但如果你想确保你的代码更容易迁移,以及确保您的代码没有你预期什么,你应该停止使用dojo.*
。这意味着你可能不知道存在确定的命名空间。
其中一个dojoConfig
选项是async
。它默认为false
,这意味着所有的Dojo基本模块自动加载。如果你把它设置为true
并利用加载器的异步特性,这些模块不会自动加载。所有这一切结合在一起,使反应更加灵敏和快速加载的应用程序。
此外,Dojo正在拥抱的ECMAScript 5规格,并尽可能地贬低了仿真ES5功能Dojo的部分和“垫补”在适当情况下,为用户带来ES5功能非现代浏览器。这意味着“Dojo-way”在某些情况下是根本不使用Dojo的。
虽然参考指南已经更新,告诉你在哪里功能位于现在,有一个指示,其中一个特定的文件基本功能现在所处的位置。
一旦你获得了Dojo的基础和核心之外,几乎一切将工作像下面这样。在那里你会做一个dojo.require()
:
dojo.require("dojo.string");
dojo.byId("someNode").innerHTML = dojo.string.trim(" I Like Trim Strings ");
你现在会做require()
:
require(["dojo/dom", "dojo/string", "dojo/domReady!"], function(dom, string){
dom.byId("someNode").innerHTML = string.trim(" I Like Trim Strings ");
});
事件与建议
虽然dojo.connect()
并dojo.disconnect()
已移动到dojo/_base/connect
模块中,“现代”Dojo应遵循使用的模式dojo/on
进行事件处理和dojo/aspect
对方法的建议。还有一个更深入的教程活动可用,但我们将介绍一些差别在这里。
在“遗产”道场,有事件和修改方法行为之间没有明显的区别,并且dojo.connect()
被用来处理这两种。事件是发生在关系到一个对象,例如像一个“click”事件的事情。 dojo/on
无缝地与原生DOM事件和由道场对象或部件发出的交易。虽然建议是一个概念,是面向切面编程(AOP),为一个连接点(或方法)添加附加行为的一部分。同时Dojo的许多地方大多沿用了AOPdojo/aspect
模块提供了一个集中的机制来体现这一点。
在“过去”Dojo,我们可以有一个按钮的成就处理onclick
事件几种不同的方法:
<script>
dojo.require("dijit.form.Button");
myOnClick = function(evt){
console.log("I was clicked");
};
dojo.connect(dojo.byId("button3"), "onclick", myOnClick);
</script>
<body>
<div>
<button id="button1" type="button" onclick="myOnClick">Button1</button>
<button id="button2" data-dojo-type="dijit.form.Button" type="button" data-dojo-props="onClick: myOnClick">Button2</button>
<button id="button3" type="button">Button3</button>
<button id="button4" data-dojo-type="dijit.form.Button" type="button">
<span>Button4</span>
<script type="dojo/connect" data-dojo-event="onClick">
console.log("I was clicked");
</script>
</div>
</body>
在“现代”的道场,只使用dojo/on
,你可以指定你的代码,通过编程和声明,以及不用担心,如果它是DOM事件,或者你正在处理的Dijit中/小部件事件:
33 <script>
require([
"dojo/dom",
"dojo/on",
"dojo/parser",
"dijit/registry",
"dijit/form/Button",
"dojo/domReady!"
], function(dom, on, parser, registry){
var myClick = function(evt){
console.log("I was clicked");
};
parser.parse();
on(dom.byId("button1"), "click", myClick);
on(registry.byId("button2"), "click", myClick);
});
</script>
<body>
<div>
<button id="button1" type="button">Button1</button>
<button id="button2" data-dojo-type="dijit/form/Button" type="button">Button2</button>
<button id="button3" data-dojo-type="dijit/form/Button" type="button">
<div>Button4</div>
<script type="dojo/on" data-dojo-event="click">
console.log("I was clicked");
</script>
</button>
</div>
</body>
注意如何dijit.byId
不被使用。在“现代”Dojo中,dijit/registry
用于小部件和registry.byId()
检索到窗口小部件的引用。另外,注意如何dojo/on
同时处理的DOM节点并以相同的方式插件事件。
你可能会做一些像“传统”的方式增加功能的方法:
var callback = function(){
// ...
};
var handle = dojo.connect(myInstance, "execute", callback);
// ...
dojo.disconnect(handle);
在“现代”的Dojo,该dojo/aspect
模块可让您从一个方法得到的建议和“之后”或“绕”另一种方法“之前”添加行为。通常情况下,如果你正在变换dojo.connect()
,你会用替换spect.after()
这将是这个样子:
require(["dojo/aspect"], function(aspect){
var callback = function(){
// ...
};
var handle = aspect.after(myInstance, "execute", callback);
// ...
handle.remove();
});
查询参考指南,关于dojo/aspect
了解更多详情,以及大卫·沃尔什的博客dojo/aspect
和SitePen的博客比较上dojo/on 和 do/aspect.
主题
已经经历了一个有点修订的另一个领域是在道场的“发布/订阅”功能。这已被下模块化dojo/topic
模块和改进。
例如,这样的“传统”的方法是这样的:
// To publish a topic
dojo.publish("some/topic", [1, 2, 3]);
// To subscribe to a topic
var handle = dojo.subscribe("some/topic", context, callback);
// And to unsubscribe from a topic
dojo.unsubscribe(handle);
在"现在”的Dojo,你会利用dojo/topic
模块和这样做:
require(["dojo/topic"], function(topic){
// To publish a topic
topic.publish("some/topic", 1, 2, 3);
// To subscribe to a topic
var handle = topic.subscribe("some/topic", function(arg1, arg2, arg3){
// ...
});
// To unsubscribe from a topic
handle.remove();
});
查询参考指南手册,dojo/topic
了解更多详情。
注意发布论点并不怎么一个数组了,并且通过简单传递发布。
承诺
一个Dojo的核心概念一直是Deferred
,虽然换到“承诺”架构Dojo1.5发生,这是值得这里讨论。此外,在Dojo1.8和更新者,允许API被改写。虽然它主要是语义上和以前一样,它不再支持“遗产”的API,所以如果你想使用它,你将不得不采用“现代”的API。在“过去”Dojo,你会发现一个Deferred
像这样的工作:
function createMyDeferred(){
var myDeferred = new dojo.Deferred();
setTimeout(function(){
myDeferred.callback({ success: true });
}, 1000);
return myDeferred;
}
var deferred = createMyDeferred();
deferred.addCallback(function(data){
console.log("Success: " + data);
});
deferred.addErrback(function(err){
console.log("Error: " + err);
});
现在,“现代”Dojo将工作是这样的:
require(["dojo/Deferred"], function(Deferred){
function createMyDeferred(){
var myDeferred = new Deferred();
setTimeout(function(){
myDeferred.resolve({ success: true });
}, 1000);
return myDeferred;
}
var deferred = createMyDeferred();
deferred.then(function(data){
console.log("Success: " + data);
}, function(err){
console.log("Error: " + err);
});
});
还有更多的相当多的Deferred
S,所以去查询入门Deferreds教程。
dojo/DeferredList
虽然仍然存在,被弃用。你会发现在一个更强大,但类似的功能dojo/promise/all
和dojo/promise/first
。
要求
任何一个JavaScript库的核心原则之一是Ajax的概念。对于Dojo1.8和更高版本,这个基本的构建模块API舒畅,作出跨平台运行,易于扩展和促进再使用的代码。以前,您将有XHR,脚本和IFrame的IO通信之间变戏法,以及经常处理任何数据异国返回自己。 dojo/request
被引入来帮助使整个过程更加容易。
就像dojo/promise
老的实现仍然存在,但你可以轻松地重新组合代码采取新的代码优势。例如,在“过去”Dojo你可能会写这样的事情:
dojo.xhrGet({
url: "something.json",
handleAs: "json",
load: function(response){
console.log("response:", response);
},
error: function(err){
console.log("error:", err);
}
});
在“现代”的Dojo,你会写上这样的:
require(["dojo/request"], function(request){
request.get("something.json", {
handleAs: "json"
}).then(function(response){
console.log("response:", response);
}, function(err){
console.log("error:", err);
});
});
dojo/request
会加载最合适的请求处理您的平台,这对于一款浏览器是XHR。上面的代码可以很容易地在运行的NodeJS代码,你不需要改变什么。
这也是一个很广泛的话题,签出Ajax with dojo/request教程。
DOM操作
你可能会在这里看到一个趋势,如果你已经得到了这个远在本教程中,不仅有道场放弃了其依赖的全局命名空间,并采取了一些新的模式,这也打破了一些“核心”功能集成到模块更有甚者核心,以JavaScript工具包比DOM操作。
嗯,这也已经被分解成更小的块和模块化。这里是模块和它们包含的内容摘要:
模块 | 描述 | 包含 |
---|---|---|
dojo/dom | 核心DOM功能 | byId() isDescendant() setSelectable() |
dojo/dom-attr | DOM属性功能 | has() get() set() remove() getNodeProp() |
dojo/dom-class | DOM类函数 | contains() add() remove() replace() toggle() |
dojo/dom-construct | DOM建设中的作用 | toDom() place() create() empty() destroy() |
dojo/dom-form | 表单处理功能 | fieldToObject()toObject()toQuery()的toJSON() |
dojo/io-query | 字符串处理函数 | objectToQuery()queryToObject() |
dojo/dom-geometry | DOM几何相关功能 | position() getMarginBox() setMarginBox() getContentBox() setContentSize() getPadExtents() getBorderExtents() getPadBorderExtents() getMarginExtents() isBodyLtr() docScroll() fixIeBiDiScrollLeft() |
dojo/dom-prop | DOM属性功能 | get() set() |
dojo/dom-style | DOM风格的函数 | getComputedStyle() get() set() |
其中之一就是在整个“现代”Dojo工具包一贯的事情是逻辑访问左右分离代替:
var node = dojo.byId("someNode");
// Retrieves the value of the "value" DOM attribute
var value = dojo.attr(node, "value");
// Sets the value of the "value" DOM attribute
dojo.attr(node, "value", "something");
当同样的功能确实取决于参数两家全不同的事情,对这样的事情:
require(["dojo/dom", "dojo/dom-attr"], function(dom, domAttr){
var node = dom.byId("someNode");
// Retrieves the value of the "value" DOM attribute
var value = domAttr.get(node, "value");
// Sets the value of the "value" DOM attribute
domAttr.set(node, "value", "something");
});
以“现代”为例,它是很清楚你的代码在做什么,让你的编码做一些你没有打算的事变的比较困难的,因为额外的或缺失的说法。这种存取器的分离在“现代”Dojo中是一致的。。
多个DataStore与商店
在Dojo1.6,新的dojo/store
API被引入和dojo/data
API被废弃。而dojo/data
数据存储和dojox/data
被保持的数据存储在至少直到Dojo2.0,最好是尽可能迁移到新的API。本教程不能在课题中更多的详细了解,但在道场对象存储提供进一步的信息教程。
也有dojo/store
参考指南以及关于API的详细信息。
Dijit的和组件
Dijit中也改变自己在“现代”的世界里,但很多的变化已经在该工具包的基础,随着功能被分解成离散的积木和被合并在一起,使更复杂的功能。如果你正在创建一个自定义部件,你应该阅读创建自定义窗口小部件的教程。
如果你只是使用的dijits或其他部件开发,则有与引入的几个核心概念dojo/Stateful
和dojo/Evented
类。
dojo/Stateful
提供离散访问了Widget属性以及对“观察”更改这些属性的能力。例如,您可以执行以下操作:
require(["dijit/form/Button", "dojo/domReady!"], function(Button){
var button = new Button({
label: "A label"
}, "someNode");
// Sets up a watch on button.label
var handle = button.watch("label", function(attr, oldValue, newValue){
console.log("button." + attr + " changed from '" + oldValue + "' to '" + newValue + "'");
});
// Gets the current label
var label = button.get("label");
console.log("button's current label: " + label);
// This changes the value and should call the watch
button.set("label", "A different label");
// This will stop watching button.label
handle.unwatch();
button.set("label", "Even more different");
});
dojo/Evented
提供emit()
和on()
类的功能,这是纳入的dijits和窗口小部件。特别是,它是“现代”使用widget.on()
设置你的事件处理。例如,您可以执行以下操作:
require(["dijit/form/Button", "dojo/domReady!"], function(Button){
var button = new Button({
label: "Click Me!"
}, "someNode");
// Sets the event handling for the button
button.on("click", function(e){
console.log("I was clicked!", e);
});
});
分析器
最后,还有的dojo/parser
。道场已经有实力既是编程和声明的标记方式,用dojo/parser
处理声明标记的解释,并转换成那个对象实例化和窗口小部件。所有的“现代”思想上面提到已对影响dojo/parser
以及出现了一些自己的“现代”的转变。
虽然parseOnLoad: true
仍支持Dojo的配置,它通常更有意义显式调用解析器。例如:
require(["dojo/parser", "dojo/domReady!"], function(parser){
parser.parse();
});
其中与分析器等“大”的变化是,它支持兼容HTML5的属性来标记的节点。这使您的标记HTML是有效的HTML5。特别是dojoType
改变data-dojo-type
和代替指定对象参数作为非有效的HTML / XHTML属性,要传递给对象构造的所有参数在被指定data-dojo-props
的属性。例如:
<button data-dojo-type="dijit/form/Button" tabIndex=2 data-dojo-props="iconClass: 'checkmark'">OK</button>
Dojo支持使用在模块的ID(MID) data-dojo-type
,例如dojoType="dijit.form.Button"
变data-dojo-type="dijit/form/Button"
。
随着关于上面提到的与引入的概念的变化dojo/Evented
和dojo/Stateful
解析器跟上声明脚本,并添加相应的脚本类型复制“监视”和“关于”功能。例如,您现在就可以这样做:
<button data-dojo-type="dijit/form/Button" type="button">
<span>Click</span>
<script type="dojo/on" data-dojo-event="click" data-dojo-args="e">
console.log("I was clicked!", e);
this.set("label", "Clicked!");
</script>
<script type="dojo/watch" data-dojo-prop="label" data-dojo-args="prop, oldValue, newValue">
console.log("button: " + prop + " changed from '" + oldValue + "' to '" + newValue + "'");
</script>
</button>
此外,分析器也支持引入的概念dojo/aspect
,你可以在“后”和“绕”忠告“之前”,提供代码。见道场/分析器参考指南,有关更多信息。
该dojo/parser
还支持自动要求模块。这意味着你没有necessairly有模块中需要调用需要之前。如果设置isDebug
为true
,虽然,它会警告你,如果你都要求模块这种方式。
生成器
在本教程中简单谈谈最后一个区域是道场建设者。在道场1.7它被完全重写。这部分地是处理与AMD的显著的变化,但它也被设计现代化,并使它非常丰富的功能。实在是太广阔了本教程的话题。你应该阅读的创建构建信息的教程,但要准备忘记你,以拥抱“现代”的建设者知道老建设者的一切。
结论
希望您的旅途入道场的“现代”世界很有趣。虽然这需要一段时间的人熟悉的“遗产”的世界,开始在新的世界思维的“道场路”,一旦你采取了行动,这将是很难回去,你会发现你有更多的结构化的方法为您的应用程序。
当一切都失败了,还记得“现代”道场的方法是:
- 颗粒依赖性和模块化的 -只需要你需要什么,当你需要它。挥别“厨房水槽”。它使更快/更智能/安全应用程序。
- 异步 -东西不一定发生在顺序规划代码异步操作。
- 全局范围是坏的 - “我不会用全球范围内”一次,跟着我说,
- 离散访问器 -一个函数只做一件事,尤其是当它涉及到访问。有一个
get()
和set()
你想要做什么。 - 道场补充ES5 -如果ECMAScript的5做了(它是“shimmable”),然后道场并不想这样做。
- 事件与建议,而不是连接 -道场被迁移从“通用”连接客场关注的事件和面向方面的编程。
- 该Builder是一个不同的野兽 -这是更强大,更强大,更丰富的功能,但它只会去突出“遗产”应用程序设计不好的假设,不解决这些问题。
祝你好运!