zoukankan      html  css  js  c++  java
  • 从Dojo1.6到Dojo1.8(二)—— 基本模块,事件,约定,DOM操作

    上一篇文章介绍了Dojo1.8带来的整体上的变化,以及AMD机制带来的Dojo模块化的改变。这一篇文章将把中心放在dojo core的改变上。将从dojo base的几个基本模块lang,array,load等入手,再详细介绍一下Event,Advice,Topics,Promises,Requests等几个常用功能的变化。

    相信从上一篇的介绍,大家应该能够感觉到dojo在模块化上的变化,baseless是一个趋势,就是说一个模块仅仅依赖于它所要使用的模块,而非去加载一个dojo.js,其实里面有很多的类和方法都是没有被使用到的。从1.8开始,要尽量避免使用dojo.*,取而代之的将是很多个新的模块的引入,如lang.*,kernal.*。。。

    dojo 基本模块

    首先我们来讨论一下Dojo的几个基本的模块,或者说一些基本的方法。如dojo.extend,dojo.hitch,dojo.isArray,看看这些基本函数在1.8中将如何被使用。

    类型判断函数

    Dojo2.0将移除dojo的几个类型判断函数,因为他们基本都可以通过原生的JavaScript函数去完成,如下所示:

    1.x2.0
    dojo.isString(v) typeof v == "string"
    dojo.isArray(v) v instanceof Array
    dojo.isFunction(v) typeof v == "function"
    dojo.isArrayLike(v) "length" in v, etc. (but see note below)

    dojo/_base/lang

    1.x 语法2.0 模块2.0 语法
    dojo.extend dojo/_base/lang lang.extend
    dojo._hitchArgs dojo/_base/lang lang._hitchArgs
    dojo.hitch dojo/_base/lang lang.hitch
    dojo.delegate dojo/_base/lang lang.delegate
    dojo._toArray dojo/_base/lang lang._toArray
    dojo.partial dojo/_base/lang lang.partial
    dojo.clone dojo/_base/lang lang.clone
    dojo.trim dojo/_base/lang lang.trim
    dojo.replace dojo/_base/lang lang.replace
    dojo.mixin dojo/_base/lang lang.mixin
    dojo._mixin dojo/_base/lang lang._mixin
    dojo.exists dojo/_base/lang lang.exists
    dojo.getObject dojo/_base/lang lang.getObject
    dojo.setObject dojo/_base/lang lang.setObject

    dojo/_base/array

    1.x 语法2.0 模块2.0 语法
    dojo.forEach dojo/_base/array array.forEach
    dojo.map dojo/_base/array array.map
    dojo.filter dojo/_base/array array.filter
    dojo.every dojo/_base/array array.every
    dojo.some dojo/_base/array array.some
    dojo.indexOf dojo/_base/array array.indexOf

    浏览器/设备 嗅探器

    在dojo1.6中我们可以通过dojo.isIE等变量来获取用户浏览器的一些信息。而在dojo1.8中将改成使用dojo/has API来获取这些信息。举例来说:

    if(dojo.isIE < 6){
      // ...
    }

    将改成:

    require(["dojo/has", "dojo/sniff"], function(has){
      if(has("ie") < 6){
        // ...
      }
    });

    Load 和 UnLoad 处理函数

    dojo.addOnLoad和dojo.addOnUnLoad函数用来在页面加载完之后或者离开页面的时候调用一些处理函数,在1.8中,这两个函数被分别移到了dojo/ready和dojo/_base/unload模块之中。用法也发生了如下的变化:

    1.x 语法2.0 模块2.0 语法
    dojo.addOnLoad(f) dojo/ready ready(f)
    dojo.ready(f) dojo/ready ready(f)
    dojo.addOnUnload dojo/_base/unload unload.addOnUnload
    dojo.addOnWindowUnload dojo/_base/unload unload.addOnWindowUnload

    我也没有搞懂为什么ready和unload所在的目录不一样,unload要被放在_base目录中。这里先放一个疑问,之后有空再去研究一下。也希望能有同学发现并告诉我其中的原因。

    更多的关于dojo基础函数的变化可以参考dojo2.0 migration guide的相关内容。

    事件处理(Events)

    DOM事件

    dojo提供了在HTML tag中定义控件/节点DOM事件的方法,下面是一段很常见的例子:

    <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>

    在1.8版本中,将只能使用dojo/on,同样可以使用编程式或者定义式的方式来定义事件。对DOM原生事件以及Widget的事件都同样适用。

    <script>
      require(["dojo/dom", "dojo/on", "dojo/parser", "dojo/ready", "dijit/registry", "dijit/form/Button"],
      function(dom, on, parser, ready, registry){
        var myClick = function(evt){
          console.log("I was clicked");
        };
     
        ready(function(){
          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>

    dojo/on 取代dojo.connect

    在dojo之前的版本中一直被津津乐道的dojo.connect和dojo.disconnect(这两个函数使用不当会造成严重的内存泄漏,后面会专门写篇文章来讲)在1.8之后将会被dojo/on模块中的on()函数所取代。

    dojo.connect用法:

    var handle = dojo.connect(node, "onclick", callback);
    // ...
    dojo.disconnect(handle);

    将被改成如下的新用法:

    require(["dojo/on"], function(on){
      var handle = on(node, "click", callback);
      // ...
      handle.remove();
    });

    注意到这里的on前缀被移除了,onclick被编程了click。 返回的handle变量提供了一个remove方法,而不再需用使用dojo.disconnect来去除事件关联。

    此外dojo/query返回的节点列表也可以直接使用on函数来绑定事件处理函数。如:

    //老的写法,使用connect
    dojo.query("li").connect("onclick", callback);
    
    //1.8之后,使用on
    require(["dojo/query"], function(query){
      query("li").on("click", callback);
    });

    鼠标事件(mouse)

    dojo支持onmouseenter和onmouseleave事件,尽管有些浏览器原生不支持这些事件。在dojo1.6及之前的版本中onmouseenter作为string传入dojo.connect函数中,如:

    dojo.connect(node, "onmouseenter", callback);

    在1.8中这两个事件被定义在dojo/mouse模块的事件对象中,使用的时候它需要和其他模块一样加载进来:

    require(["dojo/on", "dojo/mouse"], function(on, mouse){
      on(node, mouse.enter, callback);
    });

    对于鼠标按键的事件的写法也有所改变。

    1.6及之前版本写法:

    dojo.connect(node, "onmousedown", function(evt){
      if(dojo.mouseButtons.isLeft(evt){ ... }
    });

    新的写法如下:

    require(["dojo/on", "dojo/mouse"], function(on, mouse){
      on(node, "mousedown", function(evt){
        if(mouse.isLeft(evt)){ ... }
      });
    });

    事件委派(Event Delegation)

    对于JavaScript的事件委托一直想找事件好好讲一下,几乎每个框架都提供了对它的支持和封装,jQuery,dojo等。不过在这里讲貌似又不是很合适,先大概讲一下自己的理解:

    当某个元素的事件被触发的时候,该事件会被抛到它的父节点(parent)。所以当需要处理很多子节点的事件的时候,我们可以只捕获父节点的事件,然后通过判断事件目标(e.target)来判定是不是我们所想要处理的节点的事件被触发了。等不及的同学可以访问http://davidwalsh.name/event-delegate这篇文章,我觉得讲的还比较详细。

    在dojo1.6中使用dojo.behavior和dojox.NodeList.delegate模块来支持事件委派,在1.8中被编入了dojo/on模块中。

    1.6的写法:

    var myBehavior = {
      "#mylist li:click" : {
        onclick: onListItemClickHandler
      }
    };
    dojo.behavior.add(myBehavior);
    dojo.behavior.apply();

    1.8及写法:

    require(["dojo/on", "dojo/query", "dojo/_base/window"], function(on, query, win){
      on(win.doc(), "#mylist li:click", onListItemClickHandler);
    });

    对于事件处理的更多的变化可以参考下表:

    1.x 语法2.0 module2.0 语法
    dojo.connect(node,”onclick”,cb) dojo/on on(node,”click”,cb) (note that “on” prefix removed)
    dojo.connect(node,”onmouseenter”,cb) dojo/on,dojo/mouse on(node,mouse.enter,cb)
    dojo.connect(node,”onmouseleave”,cb) dojo/on,dojo/mouse on(node,mouse.leave,cb)
    dojo.connect(node,”onkeypress”,cb) dojo/on on(node,”keypress”,cb) for printable or on(node,”keydown”,cb) for arrows etc.
    dojo.disconnect(handle)   handle.remove()
    dojo.connectPublisher   see above
    dojo.fixEvent dojo/_base/event event.fix
    dojo.stopEvent dojo/_base/event event.stop
    dojo.mouseButtons.is***() dojo/mouse mouse.is***()

    通知(Advice)

    Dojo.connect可以用于类似AOP中的后通知(After Advice),可以用于在某些切入点之后调用指定的处理函数。在1.8中,dojo提供了一个较为全面的AOP支持dojo/aspect。

    aspect提供了三个方法aspect.before(),aspect.after(),aspect.arround()。分别用来在某个方法调用前、后或者前后插入处理函数的接口。

    1.6的写法:

    var handle = dojo.connect(myInstance, "execute", callback);
    // ...
    dojo.disconnect(handle);

    在1.8中将使用aspect的写法:

    require(["dojo/aspect"], function(aspect){
      var handle = aspect.after(myInstance, "execute", callback);
      /  ...
      handle.remove();
    });

    订阅与发布(Subscribe and Publish)/Topic

    dojo.publish()/dojo.subscribe()/dojo.unsubscribe是dojo提供的订阅与发布模式的接口。在1.8中被封装到了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);

    1.8使用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();
    });

    延迟和约定(deferred and promise)

    延迟(Deferred)一直是dojo很热门的一个处理异步调用的工具,它提供了延迟处理函数的调用,直到某个特定事件发生或某个动作完成后发生。Dojo中的Ajax调用的异步处理就是使用的Deffered的机制。

    契约(Promise)是一套API接口,定义了进行异步处理的一些方法和属性,Deferred是promise的实现。后面我会找时间专门写一篇文章来介绍Dojo中的Deffered和Promise,这里先重点讲一下Promise在1.8中的变化。

    在Dojo1.8中,对promise接口进行了重构,Dojo.Deferred和dojo.DeferredList的使用也发生了变化。1.8中,dojo.Deferred和dojo.when的功能被移到了dojo/promise,dojo/deferred以及dojo/when的模块当中。dojo.DeferredList被弃用,相应的功能被dojo/promise/all所取代,可以支持当多个契约事件都发生之后,再调用特定的处理函数。

    首先看一下1.6版本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);
    });

    再一下1.8的写法

    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);
      });
    });

    弃掉了callback函数的调用,取而代之的是resolve方法,这样更能突出契约的概念。使用then函数通过传参数的方式传入处理函数,也比直接调用addCallback和addErrback更加简洁。

    Getting Started with Deferreds 以及 Dojo Deferred and Promises两篇文档比较全面的介绍了dojo promise和deferred的概念和用法。大家可以仔细阅读,之后我也会找时间将这两篇翻译一下,并加入自己的理解。有兴趣的同学可以关注。

    请求(requests)

    把请求(request)放在这里讲,主要是因为在dojo的Ajax调用使用了promise的接口,所以在1.8中也会随着promise的改变发生一下使用上的变化。我们取1.6中的dojo.xhrGet方法为例:

    dojo.xhrGet({
      url: "something.json",
      handleAs: "json",
      load: function(response){
        console.log("response:", response);
      },
      error: function(err){
        console.log("error:", err);
      }
    });

    在1.8中,新的写法类似于promise:

    require(["dojo/request"], function(request){
      request.get("something.json", {
        handleAs: "json"
      }).then(function(response){
        console.log("response:", response);
      }, function(err){
        console.log("error:", err);
      });
    });

    同时在1.8中,对于script以及iframe交互的也被封装到了dojo/request/script以及dojo/request/iframe两个子模块中。使用了与dojo/request的接口。

    在官网的文档上看到这样一段话:

    dojo/request will load the most appropriate request handler for your platform, which for a browser is XHR. The code above could easily be code running on NodeJS and you wouldn't need to change anything.

     意思是说dojo/request会自动根据代码运行的平台和环境选择最合适的请求处理方式,对于浏览器来说当然是XHR。而对于NodeJS这样的后台运行环境,可能会使用别的方式来处理请求,如请求本地资源的本地IO(对NodeJS了解不是非常深,所以这里只能自己YY一下了。。)。 之前在developworks上看到过dojo部门的同事写的在Node.js上使用Dojo的文章,还没来得及实验,大家有兴趣的可以看一下:http://www.ibm.com/developerworks/cn/web/1203_wangpei_dojonodejs/

    DOM 操作

    接下来我们讲一下dojoDOM操作方法和接口的变化。我想大家从前面的介绍中已经看出现在Dojo的趋势是放弃dojo core作为所有dojo模块的依赖,所以之前在dojo.*下的一些DOM操作的方法也被相应的移到了一些新的模块当中。下面的表格总结了这些模块的以及相应的功能:

    ModuleDescriptionContains
    dojo/dom Core DOM functions byId()
    isDescendant()
    setSelectable()
    dojo/dom-attr DOM attribute functions has()
    get()
    set()
    remove()
    getNodeProp()
    dojo/dom-class DOM class functions contains()
    add()
    remove()
    replace()
    toggle()
    dojo/dom-construct DOM construction functions toDom()
    place()
    create()
    empty()
    destroy()
    dojo/dom-form Form handling functions fieldToObject()
    toObject()
    toQuery()
    toJson()
    dojo/io-query String processing functions objectToQuery()
    queryToObject()
    dojo/dom-geometry DOM geometry related functions position()
    getMarginBox()
    setMarginBox()
    getContentBox()
    setContentSize()
    getPadExtents()
    getBorderExtents()
    getPadBorderExtents()
    getMarginExtents()
    isBodyLtr()
    docScroll()
    fixIeBiDiScrollLeft()
    dojo/dom-prop DOM property functions get()
    set()
    dojo/dom-style DOM style functions getComputedStyle()
    get()
    set()

     举个简单的例子,在1.6中对一个DOM node的获取和改变属性的方法如下:

    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");

    在1.8中,都使用dojo/dom和dojo/dom-attr两个模块中的方法:

    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");
    });

    这样的写法引入模块的概念,可以让你很明确的明白自己在做的事情,减少错误的发生。

     好了,这一篇就写这么多。后面会继续这一系列的最后一篇,重点介绍一下dojo1.8中对Dijit,dojox以及parser等模块的变化。敬请关注~~

  • 相关阅读:
    第二章函数对象
    2013/10/24初学BOOST
    [转]delete 多表删除的使用
    vs2010配置boost编程环境(照抄并简化)
    游戏服务器修改状态标志位方法
    小思 引用和指针
    第二章:UNIX标准化及实现
    const指针
    第一章:UNIX基础知识
    基础算法——位运算
  • 原文地址:https://www.cnblogs.com/owenChen/p/2827076.html
Copyright © 2011-2022 走看看