zoukankan      html  css  js  c++  java
  • js组件开发

    首先附上jquery组件开发的网站:http://www.poluoluo.com/jzxy/201406/277886.html 

    jquery组件开发保证chainability,通过返回this.each();

    jQuery.fn.test2= function(){ 
    this.css("background","#ff0");//这里面的this为jquery对象,而不是dom对象
    return this.each(function(){ //遍历匹配的元素,此处的this表示为jquery对象,而不是dom对象
    alert("this"+this+this.innerHTML); //提示当前对象的dom节点名称,这里的this关键字都指向一个不同的DOM元素(每次都是一个不同的匹配元素)。
    });
    };

    this.css(),this.each()里面的this为jquery对象,但是alert里面this为dom对象.

    为什么要return this.each()对象,所以这样就可以继续链式操作了。

    js组件写法

    参考链接:http://blog.csdn.net/bingqingsuimeng/article/details/44451481

    组件写的不多,使劲回忆以前用到的组件,完全用js开发的组件,主要有两部分,tag名称定义和组件api,具体的实现过程不记得了。

    先给出一个计算输入字符数组件的原始写法:

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>test</title>
      <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
      <script>
        $(function() {
    
          var input = $('#J_input');
    
          //用来获取字数
          function getNum(){
            return input.val().length;
          }
    
          //渲染元素
          function render(){
            var num = getNum();
    
            //没有字数的容器就新建一个
            if ($('#J_input_count').length == 0) {
              input.after('<span id="J_input_count"></span>');
            };
    
            $('#J_input_count').html(num+'个字');
          }
    
          //监听事件
          input.on('keyup',function(){
            render();
          });
    
          //初始化,第一次渲染
          render();
    
    
        })
      </script>
    </head>
    <body>
    <input type="text" id="J_input"/>
    </body>
    </html>

    网上查了js写组件的过程,在结合自己的实践工,主要使用了三种写组件的方式:

    1.作用域隔离

    使用对象字面量定义一个对象,调用对象的函数方法;

    var textCount = {
      input:null,
      init:function(config){
        this.input = $(config.id);
        this.bind();
        //这边范围对应的对象,可以实现链式调用
        return this;
      },
      bind:function(){
        var self = this;
        this.input.on('keyup',function(){
          self.render();
        });
      },
      getNum:function(){
        return this.input.val().length;
      },
      //渲染元素
      render:function(){
        var num = this.getNum();
    
        if ($('#J_input_count').length == 0) {
          this.input.after('<span id="J_input_count"></span>');
        };
    
        $('#J_input_count').html(num+'个字');
      }
    }
    
    $(function() {
      //在domready后调用
      textCount.init({id:'#J_input'}).render();
    }) 

    这样写的优点是:所有的功能都在一个变量下面。代码更清晰,并且有统一的入口调用方法
    缺点:没有私有的概念,所有的方法都是公开的,例如getNum和bind方法应该是私有的方法,但是其他代码可以访问和更改他们,当代码量特别特别多的时候,很容易出现变量重复,或被修改的问题。
    为了私有化方法,出现的闭包的写法。
    2.闭包写法
    var TextCount = (function(){
      //私有方法,外面将访问不到
      var _bind = function(that){
        that.input.on('keyup',function(){
          that.render();
        });
      }
    
      var _getNum = function(that){
        return that.input.val().length;
      }
    
      var TextCountFun = function(config){
    
      }
    
      TextCountFun.prototype.init = function(config) {
        this.input = $(config.id);
        _bind(this);
    
        return this;
      };
    
      TextCountFun.prototype.render = function() {
        var num = _getNum(this);
    
        if ($('#J_input_count').length == 0) {
          this.input.after('<span id="J_input_count"></span>');
        };
    
        $('#J_input_count').html(num+'个字');
      };
      //返回构造函数
      return TextCountFun;
    
    })();
    
    $(function() {
      new TextCount().init({id:'#J_input'}).render();
    })
    这种写法把所有的东西都放在一个自动执行的闭包模块中,所以不受外界的影响,并且只对外返回了
    TextCountFun的构造函数,生成的对象只能访问到init,render方法。这种写法已经满足绝大多数的需求了。事实上大部分的jQuery插件都是这种写法。

    3.面向对象方式
    上面的写法已经可以满足大部分需求,但是当一个页面特别复杂,我们需要做一套组件。仅仅用这个就不行了。首先的问题就是,这种写法太灵活了,写单个组件还可以。如果我们需要做一套风格相近的组件,而且是多个人同时在写。那真的是噩梦。

    在编程的圈子里,面向对象一直是被认为最佳的编写代码方式。比如Java,就是因为把面向对象发挥到了极致,所以多个人写出来的代码都很接近,维护也很方便。但是很不幸的是,javascript不支持class类的定义。但是我们可以模拟。
    先实现一个简单的js类,作为base类。
    var Class = (function() {
      var _mix = function(r, s) {
        for (var p in s) {
          if (s.hasOwnProperty(p)) {
            r[p] = s[p]
          }
        }
      }
    
      var _extend = function() {
    
        //开关 用来使生成原型时,不调用真正的构成流程init
        this.initPrototype = true
        var prototype = new this()
        this.initPrototype = false
    
        var items = arguments.slice() || []
        var item
    
        //支持混入多个属性,并且支持{}也支持 Function
        while (item = items.shift()) {
          _mix(prototype, item.prototype || item)
        }
    
    
        // 这边是返回的类,其实就是我们返回的子类
        function SubClass() {
          if (!SubClass.initPrototype && this.init)
            this.init.apply(this, arguments)//调用init真正的构造函数
        }
    
        // 赋值原型链,完成继承
        SubClass.prototype = prototype
    
        // 改变constructor引用
        SubClass.prototype.constructor = SubClass
    
        // 为子类也添加extend方法
        SubClass.extend = _extend
    
        return SubClass
      }
      //超级父类
      var Class = function() {}
      //为超级父类添加extend方法
      Class.extend = _extend
    
    })()
    基类的使用方式为:
    /继承超级父类,生成个子类Animal,并且混入一些方法。这些方法会到Animal的原型上。
    //另外这边不仅支持混入{},还支持混入Function
    var Animal = Class.extend({
      init:function(opts){
        this.msg = opts.msg
        this.type = "animal"
      },
      say:function(){
        alert(this.msg+":i am a "+this.type)
      }
    })
    
    
    //继承Animal,并且混入一些方法
    var Dog = Animal.extend({
      init:function(opts){
        //并未实现super方法,直接简单使用父类原型调用即可
        Animal.prototype.init.call(this,opts)
        //修改了type类型
        this.type = "dog"
      }
    })
    
    //new Animal({msg:'hello'}).say()
    
    new Dog({msg:'hi'}).say()
    其实就是重新覆盖或者定义了基类中的抽象出的方法。

    本文实例请参考原文链接,

    抽象出base

    可以看到,我们的组件有些方法,是大部分组件都会有的。

    • 比如init用来初始化属性。
    • 比如render用来处理渲染的逻辑。
    • 比如bind用来处理事件的绑定。
    抽象出这三个方法,都按照这个约定写,开发大规模组件库就变得更加规范,相互之间配合也更容易。
    事实上,这边的init,bind,render就已经有了点生命周期的影子,但凡是组件都会具有这几个阶段,初始化,绑定事件,以及渲染。当然这边还可以加一个destroy销毁的方法,用来清理现场。
    在组件开发中,引入事件机制 ,以便向外部报出组件当前状态。
    观察者模式
    想象一下base是个机器人会说话,他会一直监听输入的字数并且汇报出去(通知)。而你可以把耳朵凑上去,听着他的汇报(监听)。发现字数超过5个字了,你就做些操作。
    //辅组函数,获取数组里某个元素的索引 index
    var _indexOf = function(array,key){
      if (array === null) return -1
      var i = 0, length = array.length
      for (; i < length; i++) if (array[i] === item) return i
      return -1
    }
    
    var Event = Class.extend({
      //添加监听
      on:function(key,listener){
        //this.__events存储所有的处理函数
        if (!this.__events) {
          this.__events = {}
        }
        if (!this.__events[key]) {
          this.__events[key] = []
        }
        if (_indexOf(this.__events,listener) === -1 && typeof listener === 'function') {
          this.__events[key].push(listener)
        }
    
        return this
      },
      //触发一个事件,也就是通知
      fire:function(key){
    
        if (!this.__events || !this.__events[key]) return
    
        var args = Array.prototype.slice.call(arguments, 1) || []
    
        var listeners = this.__events[key]
        var i = 0
        var l = listeners.length
    
        for (i; i < l; i++) {
          listeners[i].apply(this,args)
        }
    
        return this
      },
      //取消监听
      off:function(key,listener){
    
        if (!key && !listener) {
          this.__events = {}
        }
        //不传监听函数,就去掉当前key下面的所有的监听函数
        if (key && !listener) {
          delete this.__events[key]
        }
    
        if (key && listener) {
          var listeners = this.__events[key]
          var index = _indexOf(listeners, listener)
    
          (index > -1) && listeners.splice(index, 1)
        }
    
        return this;
      }
    })
    
    
    var a = new Event()
    
    //添加监听 test事件
    a.on('test',function(msg){
      alert(msg)
    })
    
    //触发 test事件
    a.fire('test','我是第一次触发')
    a.fire('test','我又触发了')
    
    a.off('test')
    
    a.fire('test','你应该看不到我了')

    实现起来并不复杂,只要使用this.__events存下所有的监听函数(on时候存下监听)。在fire的时候去找到并且执行相应的监听函数就行了。
    这个时候面向对象的好处就来了,如果我们希望base拥有事件机制。只需要这么写:
    var Base = Class.extend(Event,{
      ...
      destroy:function(){
        //去掉所有的事件监听
        this.off()
      }
    })

    是的只要extend的时候多混入一个Event,这样Base或者它的子类生成的对象都会自动具有事件机制。

    有了事件机制我们可以把组件内部很多状态暴露出来,比如我们可以在set方法中抛出一个事件,这样每次属性变更的时候我们都可以监听到。

    到这里为止,我们的base类已经像模像样了,具有了init,bind,render,destroy方法来表示组件的各个关键过程,并且具有了事件机制。基本上已经可以很好的来开发组件了。

    对基类的更进一步的丰富目前就不在讨论了,主要考虑三个方面:

    1.事件代理:不在是dom元素的事件绑定监听,也不需要用户去关心什么时候销毁。

    2。模板渲染:用户不需要覆盖render方法,而是覆盖实现setUp方法。可以通过在setUp里面调用render来达到渲染对应html的目的。直接使用模板解析渲染。

    3.数据单项绑定:无需操作dom,后面要改动内容,不需要操作dom,只需要调用setChuckdata(key,新的值),选择性的更新某个数据,相应的html会自动重新渲染。



     

  • 相关阅读:
    BoundsChecker下载
    大型系统内部资源定位的途径
    架构的焦点
    为什么日志只应该有三个级别
    回收站引发ORACLE查询表空间使用缓慢
    题目记录
    广搜入门 待改进的广搜
    归并排序的使用
    大数数组中滚动数组的应用
    多重背包问题
  • 原文地址:https://www.cnblogs.com/liangxin/p/6554755.html
Copyright © 2011-2022 走看看