pbootcms网站模板|日韩1区2区|织梦模板||网站源码|日韩1区2区|jquery建站特效-html5模板网

基于javascript的組件開(kāi)發(fā)方式

作為一名前端工程師,寫(xiě)組件的能力至關(guān)重要。雖然javascript經(jīng)常被人嘲笑是個(gè)小玩具,但是在一代代大牛的前仆后繼的努力下,漸漸的也摸索了一套組件的編寫(xiě)方式。下面我們來(lái)談?wù)劊?

  作為一名前端工程師,寫(xiě)組件的能力至關(guān)重要。雖然javascript經(jīng)常被人嘲笑是個(gè)小玩具,但是在一代代大牛的前仆后繼的努力下,漸漸的也摸索了一套組件的編寫(xiě)方式。

  下面我們來(lái)談?wù)劊诂F(xiàn)有的知識(shí)體系下,如何很好的寫(xiě)組件。

  比如我們要實(shí)現(xiàn)這樣一個(gè)組件,就是一個(gè)輸入框里面字?jǐn)?shù)的計(jì)數(shù)。這個(gè)應(yīng)該是個(gè)很簡(jiǎn)單的需求。


  

  我們來(lái)看看,下面的各種寫(xiě)法。

  為了更清楚的演示,下面全部使用jQuery作為基礎(chǔ)語(yǔ)言庫(kù)。



  最簡(jiǎn)陋的寫(xiě)法

  嗯 所謂的入門(mén)級(jí)寫(xiě)法呢,就是完完全全的全局函數(shù)全局變量的寫(xiě)法。(就我所知,現(xiàn)在好多外包還是這種寫(xiě)法)

  代碼如下:




  
  test
  
  




  

  這段代碼跑也是可以跑的,但是呢,各種變量混亂,沒(méi)有很好的隔離作用域,當(dāng)頁(yè)面變的復(fù)雜的時(shí)候,會(huì)很難去維護(hù)。目前這種代碼基本是用不了的。當(dāng)然少數(shù)的活動(dòng)頁(yè)面可以簡(jiǎn)單用用。


  作用域隔離

  讓我們對(duì)上面的代碼作些改動(dòng),使用單個(gè)變量模擬命名空間。


var textCount = {
  input:null,
  init:function(config){
    this.input = $(config.id);
    this.bind();
    //這邊范圍對(duì)應(yīng)的對(duì)象,可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用
    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('');
    };

    $('#J_input_count').html(num+'個(gè)字');
  }
}

$(function() {
  //在domready后調(diào)用
  textCount.init({id:'#J_input'}).render();
}) 

  

  這樣一改造,立馬變的清晰了很多,所有的功能都在一個(gè)變量下面。代碼更清晰,并且有統(tǒng)一的入口調(diào)用方法。

  但是還是有些瑕疵,這種寫(xiě)法沒(méi)有私有的概念,比如上面的getNum,bind應(yīng)該都是私有的方法。但是其他代碼可以很隨意的改動(dòng)這些。當(dāng)代碼量特別特別多的時(shí)候,很容易出現(xiàn)變量重復(fù),或被修改的問(wèn)題。

  于是又出現(xiàn)了一種函數(shù)閉包的寫(xiě)法:


var TextCount = (function(){
  //私有方法,外面將訪問(wèn)不到
  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('');
    };

    $('#J_input_count').html(num+'個(gè)字');
  };
  //返回構(gòu)造函數(shù)
  return TextCountFun;

})();

$(function() {
  new TextCount().init({id:'#J_input'}).render();
})

  

  這種寫(xiě)法,把所有的東西都包在了一個(gè)自動(dòng)執(zhí)行的閉包里面,所以不會(huì)受到外面的影響,并且只對(duì)外公開(kāi)了TextCountFun構(gòu)造函數(shù),生成的對(duì)象只能訪問(wèn)到init,render方法。這種寫(xiě)法已經(jīng)滿足絕大多數(shù)的需求了。事實(shí)上大部分的jQuery插件都是這種寫(xiě)法。


  面向?qū)ο?/b>

  上面的寫(xiě)法已經(jīng)可以滿足絕大多數(shù)需求了。

  但是呢,當(dāng)一個(gè)頁(yè)面特別復(fù)雜,當(dāng)我們需要的組件越來(lái)越多,當(dāng)我們需要做一套組件。僅僅用這個(gè)就不行了。首先的問(wèn)題就是,這種寫(xiě)法太靈活了,寫(xiě)單個(gè)組件還可以。如果我們需要做一套風(fēng)格相近的組件,而且是多個(gè)人同時(shí)在寫(xiě)。那真的是噩夢(mèng)。

  在編程的圈子里,面向?qū)ο笠恢笔潜徽J(rèn)為最佳的編寫(xiě)代碼方式。比如java,就是因?yàn)榘衙嫦驅(qū)ο蟀l(fā)揮到了極致,所以多個(gè)人寫(xiě)出來(lái)的代碼都很接近,維護(hù)也很方便。但是很不幸的是,javascript不支持class類(lèi)的定義。但是我們可以模擬。

  下面我們先實(shí)現(xiàn)個(gè)簡(jiǎn)單的javascript類(lèi):


var Class = (function() {
  var _mix = function(r, s) {
    for (var p in s) {
      if (s.hasOwnProperty(p)) {
        r[p] = s[p]
      }
    }
  }

  var _extend = function() {

    //開(kāi)關(guān) 用來(lái)使生成原型時(shí),不調(diào)用真正的構(gòu)成流程init
    this.initPrototype = true
    var prototype = new this()
    this.initPrototype = false

    var items = Array.prototype.slice.call(arguments) || []
    var item

    //支持混入多個(gè)屬性,并且支持{}也支持 Function
    while (item = items.shift()) {
      _mix(prototype, item.prototype || item)
    }


    // 這邊是返回的類(lèi),其實(shí)就是我們返回的子類(lèi)
    function SubClass() {
      if (!SubClass.initPrototype && this.init)
        this.init.apply(this, arguments)//調(diào)用init真正的構(gòu)造函數(shù)
    }

    // 賦值原型鏈,完成繼承
    SubClass.prototype = prototype

    // 改變constructor引用
    SubClass.prototype.constructor = SubClass

    // 為子類(lèi)也添加extend方法
    SubClass.extend = _extend

    return SubClass
  }
  //超級(jí)父類(lèi)
  var Class = function() {}
  //為超級(jí)父類(lèi)添加extend方法
  Class.extend = _extend

  return Class
})()

  

  這是拿John Resig的class簡(jiǎn)單修改了下。

這邊只是很簡(jiǎn)陋的實(shí)現(xiàn)了類(lèi)的繼承機(jī)制。如果對(duì)類(lèi)的實(shí)現(xiàn)有興趣可以參考我另一篇文章javascript oo實(shí)現(xiàn)

  我們看下使用方法:


//繼承超級(jí)父類(lèi),生成個(gè)子類(lèi)Animal,并且混入一些方法。這些方法會(huì)到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){
    //并未實(shí)現(xiàn)super方法,直接簡(jiǎn)單使用父類(lèi)原型調(diào)用即可
    Animal.prototype.init.call(this,opts)
    //修改了type類(lèi)型
    this.type = "dog"
  }
})

//new Animal({msg:'hello'}).say()

new Dog({msg:'hi'}).say()

  

  使用很簡(jiǎn)單,超級(jí)父類(lèi)具有extend方法,可以繼承出一個(gè)子類(lèi)。子類(lèi)也具有extend方法。

  這邊要強(qiáng)調(diào)的是,繼承的父類(lèi)都是一個(gè)也就是單繼承。但是可以通過(guò)extend實(shí)現(xiàn)多重混入。詳見(jiàn)下面用法。

  有了這個(gè)類(lèi)的擴(kuò)展,我們可以這么編寫(xiě)代碼了:


var TextCount = Class.extend({
  init:function(config){
    this.input = $(config.id);
    this._bind();
    this.render();
  },
  render:function() {
    var num = this._getNum();

    if ($('#J_input_count').length == 0) {
      this.input.after('');
    };

    $('#J_input_count').html(num+'個(gè)字');

  },
  _getNum:function(){
    return this.input.val().length;
  },
  _bind:function(){
    var self = this;
    self.input.on('keyup',function(){
      self.render();
    });
  }
})

$(function() {
  new TextCount({
    id:"#J_input"
  });
})

  這邊可能還沒(méi)看見(jiàn)class的真正好處,不急我們繼續(xù)往下。


  抽象出base

  可以看到,我們的組件有些方法,是大部分組件都會(huì)有的。

  • 比如init用來(lái)初始化屬性。
  • 比如render用來(lái)處理渲染的邏輯。
  • 比如bind用來(lái)處理事件的綁定。

  當(dāng)然這也是一種約定俗成的規(guī)范了。如果大家全部按照這種風(fēng)格來(lái)寫(xiě)代碼,開(kāi)發(fā)大規(guī)模組件庫(kù)就變得更加規(guī)范,相互之間配合也更容易。

  這個(gè)時(shí)候面向?qū)ο蟮暮锰幘蛠?lái)了,我們抽象出一個(gè)Base類(lèi)。其他組件編寫(xiě)時(shí)都繼承它。


var Base = Class.extend({
  init:function(config){
    //自動(dòng)保存配置項(xiàng)
    this.__config = config
    this.bind()
    this.render()
  },
  //可以使用get來(lái)獲取配置項(xiàng)
  get:function(key){
    return this.__config[key]
  },
  //可以使用set來(lái)設(shè)置配置項(xiàng)
  set:function(key,value){
    this.__config[key] = value
  },
  bind:function(){
  },
  render:function() {

  },
  //定義銷(xiāo)毀的方法,一些收尾工作都應(yīng)該在這里
  destroy:function(){

  }
})

  

  base類(lèi)主要把組件的一般性?xún)?nèi)容都提取了出來(lái),這樣我們編寫(xiě)組件時(shí)可以直接繼承base類(lèi),覆蓋里面的bind和render方法。

  于是我們可以這么寫(xiě)代碼:


var TextCount = Base.extend({
  _getNum:function(){
    return this.get('input').val().length;
  },
  bind:function(){
    var self = this;
    self.get('input').on('keyup',function(){
      self.render();
    });
  },
  render:function() {
    var num = this._getNum();

    if ($('#J_input_count').length == 0) {
      this.get('input').after('');
    };

    $('#J_input_count').html(num+'個(gè)字');

  }
})

$(function() {
  new TextCount({
  //這邊直接傳input的節(jié)點(diǎn)了,因?yàn)閷傩缘馁x值都是自動(dòng)的。
    input:$("#J_input")
  });
})

  

  可以看到我們直接實(shí)現(xiàn)一些固定的方法,bind,render就行了。其他的base會(huì)自動(dòng)處理(這里只是簡(jiǎn)單處理了配置屬性的賦值)。

  事實(shí)上,這邊的init,bind,render就已經(jīng)有了點(diǎn)生命周期的影子,但凡是組件都會(huì)具有這幾個(gè)階段,初始化,綁定事件,以及渲染。當(dāng)然這邊還可以加一個(gè)destroy銷(xiāo)毀的方法,用來(lái)清理現(xiàn)場(chǎng)。

  此外為了方便,這邊直接變成了傳遞input的節(jié)點(diǎn)。因?yàn)閷傩再x值自動(dòng)化了,一般來(lái)說(shuō)這種情況下都是使用getter,setter來(lái)處理。這邊就不詳細(xì)展開(kāi)了。


  引入事件機(jī)制(觀察者模式)

  有了base應(yīng)該說(shuō)我們編寫(xiě)組件更加的規(guī)范化,體系化了。下面我們繼續(xù)深挖。

  還是上面的那個(gè)例子,如果我們希望輸入字的時(shí)候超過(guò)5個(gè)字就彈出警告。該怎么辦呢。

  小白可能會(huì)說(shuō),那簡(jiǎn)單啊直接改下bind方法:


var TextCount = Base.extend({
  ...
  bind:function(){
    var self = this;
    self.get('input').on('keyup',function(){
      if(self._getNum() > 5){
        alert('超過(guò)了5個(gè)字了。。。')
      }
      self.render();
    });
  },
  ...
})

  

  的確也是一種方法,但是太low了,代碼嚴(yán)重耦合。當(dāng)這種需求特別特別多,代碼會(huì)越來(lái)越亂。

  這個(gè)時(shí)候就要引入事件機(jī)制,也就是經(jīng)常說(shuō)的觀察者模式。

注意這邊的事件機(jī)制跟平時(shí)的瀏覽器那些事件不是一回事,要分開(kāi)來(lái)看。

  什么是觀察者模式呢,官方的解釋就不說(shuō)了,直接拿這個(gè)例子來(lái)說(shuō)。

  想象一下base是個(gè)機(jī)器人會(huì)說(shuō)話,他會(huì)一直監(jiān)聽(tīng)輸入的字?jǐn)?shù)并且匯報(bào)出去(通知)。而你可以把耳朵湊上去,聽(tīng)著他的匯報(bào)(監(jiān)聽(tīng))。發(fā)現(xiàn)字?jǐn)?shù)超過(guò)5個(gè)字了,你就做些操作。

  所以這分為兩個(gè)部分,一個(gè)是通知,一個(gè)是監(jiān)聽(tīng)。

  假設(shè)通知是 fire方法,監(jiān)聽(tīng)是on。于是我們可以這么寫(xiě)代碼:


var TextCount = Base.extend({
  ...
  bind:function(){
    var self = this;
    self.get('input').on('keyup',function(){
      //通知,每當(dāng)有輸入的時(shí)候,就報(bào)告出去。
      self.fire('Text.input',self._getNum())
      self.render();
    });
  },
  ...
})

$(function() {
  var t = new TextCount({
    input:$("#J_input")
  });
  //監(jiān)聽(tīng)這個(gè)輸入事件
  t.on('Text.input',function(num){
    //可以獲取到傳遞過(guò)來(lái)的值
    if(num>5){
       alert('超過(guò)了5個(gè)字了。。。')
    }
  })
})

  

  fire用來(lái)觸發(fā)一個(gè)事件,可以傳遞數(shù)據(jù)。而on用來(lái)添加一個(gè)監(jiān)聽(tīng)。這樣組件里面只負(fù)責(zé)把一些關(guān)鍵的事件拋出來(lái),至于具體的業(yè)務(wù)邏輯都可以添加監(jiān)聽(tīng)來(lái)實(shí)現(xiàn)。沒(méi)有事件的組件是不完整的。

  下面我們看看怎么實(shí)現(xiàn)這套事件機(jī)制。

  我們首先拋開(kāi)base,想想怎么實(shí)現(xiàn)一個(gè)具有這套機(jī)制的類(lèi)。


//輔組函數(shù),獲取數(shù)組里某個(gè)元素的索引 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({
  //添加監(jiān)聽(tīng)
  on:function(key,listener){
    //this.__events存儲(chǔ)所有的處理函數(shù)
    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
  },
  //觸發(fā)一個(gè)事件,也就是通知
  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
  },
  //取消監(jiān)聽(tīng)
  off:function(key,listener){

    if (!key && !listener) {
      this.__events = {}
    }
    //不傳監(jiān)聽(tīng)函數(shù),就去掉當(dāng)前key下面的所有的監(jiān)聽(tīng)函數(shù)
    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()

//添加監(jiān)聽(tīng) test事件
a.on('test',function(msg){
  alert(msg)
})

//觸發(fā) test事件
a.fire('test','我是第一次觸發(fā)')
a.fire('test','我又觸發(fā)了')

a.off('test')

a.fire('test','你應(yīng)該看不到我了')

  

  實(shí)現(xiàn)起來(lái)并不復(fù)雜,只要使用this.__events存下所有的監(jiān)聽(tīng)函數(shù)。在fire的時(shí)候去找到并且執(zhí)行就行了。

  這個(gè)時(shí)候面向?qū)ο蟮暮锰幘蛠?lái)了,如果我們希望base擁有事件機(jī)制。只需要這么寫(xiě):


var Base = Class.extend(Event,{
  ...
  destroy:function(){
    //去掉所有的事件監(jiān)聽(tīng)
    this.off()
  }
})
//于是可以
//var a  = new Base()
// a.on(xxx,fn)
//
// a.fire()

  

  是的只要extend的時(shí)候多混入一個(gè)Event,這樣Base或者它的子類(lèi)生成的對(duì)象都會(huì)自動(dòng)具有事件機(jī)制。

  有了事件機(jī)制我們可以把組件內(nèi)部很多狀態(tài)暴露出來(lái),比如我們可以在set方法中拋出一個(gè)事件,這樣每次屬性變更的時(shí)候我們都可以監(jiān)聽(tīng)到。

  到這里為止,我們的base類(lèi)已經(jīng)像模像樣了,具有了init,bind,render,destroy方法來(lái)表示組件的各個(gè)關(guān)鍵過(guò)程,并且具有了事件機(jī)制。基本上已經(jīng)可以很好的來(lái)開(kāi)發(fā)組件了。


  更進(jìn)一步,richbase

  我們還可以繼續(xù)深挖。看看我們的base,還差些什么。首先瀏覽器的事件監(jiān)聽(tīng)還很落后,需要用戶(hù)自己在bind里面綁定,再然后現(xiàn)在的TextCount里面還存在dom操作,也沒(méi)有自己的模板機(jī)制。這都是需要擴(kuò)展的,于是我們?cè)赽ase的基礎(chǔ)上再繼承出一個(gè)richbase用來(lái)實(shí)現(xiàn)更完備的組件基類(lèi)。

  主要實(shí)現(xiàn)這些功能:

  • 事件代理:不需要用戶(hù)自己去找dom元素綁定監(jiān)聽(tīng),也不需要用戶(hù)去關(guān)心什么時(shí)候銷(xiāo)毀。
  • 模板渲染:用戶(hù)不需要覆蓋render方法,而是覆蓋實(shí)現(xiàn)setUp方法。可以通過(guò)在setUp里面調(diào)用render來(lái)達(dá)到渲染對(duì)應(yīng)html的目的。
  • 單向綁定:通過(guò)setChuckdata方法,更新數(shù)據(jù),同時(shí)會(huì)更新html內(nèi)容,不再需要dom操作。

  我們看下我們實(shí)現(xiàn)richbase后怎么寫(xiě)組件:


var TextCount = RichBase.extend({
  //事件直接在這里注冊(cè),會(huì)代理到parentNode節(jié)點(diǎn),parentNode節(jié)點(diǎn)在下面指定
  EVENTS:{
    //選擇器字符串,支持所有jQuery風(fēng)格的選擇器
    'input':{
      //注冊(cè)keyup事件
      keyup:function(self,e){
        //單向綁定,修改數(shù)據(jù)直接更新對(duì)應(yīng)模板
        self.setChuckdata('count',self._getNum())

      }
    }
  },
  //指定當(dāng)前組件的模板
  template:'<%= count %>個(gè)字',
  //私有方法
  _getNum:function(){
    return this.get('input').val().length || 0
  },
  //覆蓋實(shí)現(xiàn)setUp方法,所有邏輯寫(xiě)在這里。最后可以使用render來(lái)決定需不需要渲染模板
  //模板渲染后會(huì)append到parentNode節(jié)點(diǎn)下面,如果未指定,會(huì)append到document.body
  setUp:function(){
    var self = this;

    var input = this.get('parentNode').find('#J_input')
    self.set('input',input)

    var num = this._getNum()
    //賦值數(shù)據(jù),渲染模板,選用。有的組件沒(méi)有對(duì)應(yīng)的模板就可以不調(diào)用這步。
    self.render({
      count:num
    })

  }
})

$(function() {
  //傳入parentNode節(jié)點(diǎn),組件會(huì)掛載到這個(gè)節(jié)點(diǎn)上。所有事件都會(huì)代理到這個(gè)上面。
  new TextCount({
    parentNode:$("#J_test_container")
  });
})

/**對(duì)應(yīng)的html,做了些修改,主要為了加上parentNode,這邊就是J_test_container

*/

  

  看下上面的用法,可以看到變得更簡(jiǎn)單清晰了:

  • 事件不需要自己綁定,直接注冊(cè)在EVENTS屬性上。程序會(huì)自動(dòng)將事件代理到parentNode上。
  • 引入了模板機(jī)制,使用template規(guī)定組件的模板,然后在setUp里面使用render(data)的方式渲染模板,程序會(huì)自動(dòng)幫你append到parentNode下面。
  • 單向綁定,無(wú)需操作dom,后面要改動(dòng)內(nèi)容,不需要操作dom,只需要調(diào)用setChuckdata(key,新的值),選擇性的更新某個(gè)數(shù)據(jù),相應(yīng)的html會(huì)自動(dòng)重新渲染。

  下面我們看下richebase的實(shí)現(xiàn):


var RichBase = Base.extend({
  EVENTS:{},
  template:'',
  init:function(config){
    //存儲(chǔ)配置項(xiàng)
    this.__config = config
    //解析代理事件
    this._delegateEvent()
    this.setUp()
  },
  //循環(huán)遍歷EVENTS,使用jQuery的delegate代理到parentNode
  _delegateEvent:function(){
    var self = this
    var events = this.EVENTS || {}
    var eventObjs,fn,select,type
    var parentNode = this.get('parentNode') || $(document.body)

    for (select in events) {
      eventObjs = events[select]

      for (type in eventObjs) {
        fn = eventObjs[type]

        parentNode.delegate(select,type,function(e){
          fn.call(null,self,e)
        })
      }

    }

  },
  //支持underscore的極簡(jiǎn)模板語(yǔ)法
  //用來(lái)渲染模板,這邊是抄的underscore的。非常簡(jiǎn)單的模板引擎,支持原生的js語(yǔ)法
  _parseTemplate:function(str,data){
    /**
     * http://ejohn.org/blog/javascript-micro-templating/
     * https://github.com/jashkenas/underscore/blob/0.1.0/underscore.js#L399
     */
    var fn = new Function('obj',
        'var p=[],print=function(){p.push.apply(p,arguments);};' +
        'with(obj){p.push(\'' + str
            .replace(/[\r\t\n]/g, " ")
            .split("<%").join("\t")
            .replace(/((^|%>)[^\t]*)'/g, "$1\r")
            .replace(/\t=(.*?)%>/g, "',$1,'")
            .split("\t").join("');")
            .split("%>").join("p.push('")
            .split("\r").join("\\'") +
        "');}return p.join('');")
    return data ? fn(data) : fn
  },
  //提供給子類(lèi)覆蓋實(shí)現(xiàn)
  setUp:function(){
    this.render()
  },
  //用來(lái)實(shí)現(xiàn)刷新,只需要傳入之前render時(shí)的數(shù)據(jù)里的key還有更新值,就可以自動(dòng)刷新模板
  setChuckdata:function(key,value){
    var self = this
    var data = self.get('__renderData')

    //更新對(duì)應(yīng)的值
    data[key] = value

    if (!this.template) return;
    //重新渲染
    var newHtmlNode = $(self._parseTemplate(this.template,data))
    //拿到存儲(chǔ)的渲染后的節(jié)點(diǎn)
    var currentNode = self.get('__currentNode')
    if (!currentNode) return;
    //替換內(nèi)容
    currentNode.replaceWith(newHtmlNode)

    self.set('__currentNode',newHtmlNode)

  },
  //使用data來(lái)渲染模板并且append到parentNode下面
  render:function(data){
    var self = this
    //先存儲(chǔ)起來(lái)渲染的data,方便后面setChuckdata獲取使用
    self.set('__renderData',data)

    if (!this.template) return;

    //使用_parseTemplate解析渲染模板生成html
    //子類(lèi)可以覆蓋這個(gè)方法使用其他的模板引擎解析
    var html = self._parseTemplate(this.template,data)

    var parentNode = this.get('parentNode') || $(document.body)

    var currentNode = $(html)
    //保存下來(lái)留待后面的區(qū)域刷新
    //存儲(chǔ)起來(lái),方便后面setChuckdata獲取使用
    self.set('__currentNode',currentNode)
    parentNode.append(currentNode)
  },
  destroy:function(){

    var self = this
    //去掉自身的事件監(jiān)聽(tīng)
    self.off()
    //刪除渲染好的dom節(jié)點(diǎn)
    self.get('__currentNode').remove()
    //去掉綁定的代理事件
    var events = self.EVENTS || {}
    var eventObjs,fn,select,type
    var parentNode = self.get('parentNode')

    for (select in events) {
      eventObjs = events[select]

      for (type in eventObjs) {
        fn = eventObjs[type]

        parentNode.undelegate(select,type,fn)
      }

    }

  }
})

  

  主要做了兩件事,一個(gè)就是事件的解析跟代理,全部代理到parentNode上面。另外就是把render抽出來(lái),用戶(hù)只需要實(shí)現(xiàn)setUp方法。如果需要模板支持就在setUp里面調(diào)用render來(lái)渲染模板,并且可以通過(guò)setChuckdata來(lái)刷新模板,實(shí)現(xiàn)單向綁定。


  結(jié)語(yǔ)

  有了richbase,基本上組件開(kāi)發(fā)就沒(méi)啥問(wèn)題了。但是我們還是可以繼續(xù)深挖下去。

  比如組件自動(dòng)化加載渲染,局部刷新,比如父子組件的嵌套,再比如雙向綁定,再比如實(shí)現(xiàn)ng-click這種風(fēng)格的事件機(jī)制。

  當(dāng)然這些東西已經(jīng)不屬于組件里面的內(nèi)容了。再進(jìn)一步其實(shí)已經(jīng)是一個(gè)框架了。實(shí)際上最近比較流行的react,ploymer還有我們的brix等等都是實(shí)現(xiàn)了這套東西。受限于篇幅,這個(gè)以后有空再寫(xiě)篇文章詳細(xì)分析下。

  2015-3-23 更新

  鑒于有人跟我要完整代碼,其實(shí)上面都列出來(lái)了。好吧,那我就再整理下,放在github了包含具體的demo,請(qǐng)點(diǎn)這里。不過(guò)僅僅作為理解使用,最好不要用于生產(chǎn)環(huán)境。如果覺(jué)得有幫助就給我個(gè)star吧。


來(lái)源:http://purplebamboo.github.io/2015/03/16/javascript-component/

【網(wǎng)站聲明】本站除付費(fèi)源碼經(jīng)過(guò)測(cè)試外,其他素材未做測(cè)試,不保證完整性,網(wǎng)站上部分源碼僅限學(xué)習(xí)交流,請(qǐng)勿用于商業(yè)用途。如損害你的權(quán)益請(qǐng)聯(lián)系客服QQ:2655101040 給予處理,謝謝支持。

相關(guān)文檔推薦

由于實(shí)際運(yùn)行環(huán)境是在瀏覽器中,因此性能還取決于JavaScript解釋器的效率,指定的FPS幀速在低性能解釋器中可能不會(huì)達(dá)到,所以這部分不是開(kāi)發(fā)者能夠決定的,開(kāi)發(fā)者能作的是盡可能通
本文將使用HTML5提供的VideoAPI做一個(gè)自定義的視頻播放器,需要用到HTML5提供的video標(biāo)簽、以及HTML5提供的對(duì)JavascriptAPI的擴(kuò)展。,HTML5中國(guó),中國(guó)最大的HTML5中文門(mén)戶(hù)。
隨著 Hybrid 應(yīng)用的豐富,HTML5 工程師們已經(jīng)不滿足于把桌面端體驗(yàn)簡(jiǎn)單移植到移動(dòng)端,他們覬覦移動(dòng)原生應(yīng)用人性化的操作體驗(yàn),特別是原生應(yīng)用與生俱來(lái)的豐富的手勢(shì)系統(tǒng)。HTML5 沒(méi)有提
你想要在自己網(wǎng)站上分享一個(gè)產(chǎn)品,或者是一個(gè)作品集,又或者僅僅只是一個(gè)靈感。在你發(fā)布到網(wǎng)上之前,你想讓它看起來(lái)有吸引力,專(zhuān)業(yè),或者至少得看起來(lái)像那么回事。那么你接下
H5廣告,包括H5廣告的設(shè)計(jì)流程,究竟有什么講究,和階段。為了能幫助更多的人了解H5廣告,我專(zhuān)門(mén)做了一個(gè)講義。同時(shí),也讓我意外的收到了非常好反饋和認(rèn)!這是對(duì)我的極大鼓勵(lì)!我的
本文主要內(nèi)容有:框架與組件、構(gòu)建生態(tài)、開(kāi)發(fā)技巧與調(diào)試、html、css與重構(gòu)、native/hybrid/桌面開(kāi)發(fā)、前端/H5優(yōu)化、全棧/全端開(kāi)發(fā)、研究實(shí)驗(yàn)、數(shù)據(jù)分析與監(jiān)控、其它軟技能、前端技術(shù)網(wǎng)
主站蜘蛛池模板: 减速机三参数组合探头|TSM803|壁挂式氧化锆分析仪探头-安徽鹏宸电气有限公司 | 隆众资讯-首页_大宗商品资讯_价格走势_市场行情 | pos机办理,智能/扫码/二维码/微信支付宝pos机-北京万汇通宝商贸有限公司 | 小型数控车床-数控车床厂家-双头数控车床| 切铝机-数控切割机-型材切割机-铝型材切割机-【昆山邓氏精密机械有限公司】 | 脱硫搅拌器厂家-淄博友胜不锈钢搅拌器厂家 | 福建珂朗雅装饰材料有限公司「官方网站」 | 超声波分散机-均质机-萃取仪-超声波涂料分散设备-杭州精浩 | 塑料检查井_双扣聚氯乙烯增强管_双壁波纹管-河南中盈塑料制品有限公司 | 全自动烧卖机厂家_饺子机_烧麦机价格_小笼汤包机_宁波江北阜欣食品机械有限公司 | 袋式过滤器,自清洗过滤器,保安过滤器,篮式过滤器,气体过滤器,全自动过滤器,反冲洗过滤器,管道过滤器,无锡驰业环保科技有限公司 | 直流大电流电源,燃料电池检漏设备-上海政飞 | 电磁流量计厂家_涡街流量计厂家_热式气体流量计-青天伟业仪器仪表有限公司 | atcc网站,sigma试剂价格,肿瘤细胞现货,人结肠癌细胞株购买-南京科佰生物 | 气密性检测仪_气密性检测设备_防水测试仪_密封测试仪-岳信仪器 | 色油机-色母机-失重|称重式混料机-称重机-米重机-拌料机-[东莞同锐机械]精密计量科技制造商 | 没斑啦-专业的祛斑美白嫩肤知识网站-去斑经验分享 | 作文导航网_作文之家_满分作文_优秀作文_作文大全_作文素材_最新作文分享发布平台 | SEO网站优化,关键词排名优化,苏州网站推广-江苏森歌网络 | 企典软件一站式企业管理平台,可私有、本地化部署!在线CRM客户关系管理系统|移动办公OA管理系统|HR人事管理系统|人力 | 纸塑分离机-纸塑分离清洗机设备-压力筛-碎浆机厂家金双联环保 | PCB接线端子_栅板式端子_线路板连接器_端子排生产厂家-置恒电气 喷码机,激光喷码打码机,鸡蛋打码机,手持打码机,自动喷码机,一物一码防伪溯源-恒欣瑞达有限公司 假肢-假肢价格-假肢厂家-河南假肢-郑州市力康假肢矫形器有限公司 | 电动葫芦-河北悍象起重机械有限公司| [品牌官网]贵州遵义双宁口腔连锁_贵州遵义牙科医院哪家好_种植牙_牙齿矫正_原华美口腔 | Win10系统下载_32位/64位系统/专业版/纯净版下载 | 培训中心-海南香蕉蛋糕加盟店技术翰香原中心官网总部 | 大型果蔬切片机-水果冬瓜削皮机-洗菜机切菜机-肇庆市凤翔餐饮设备有限公司 | Magnescale探规,Magnescale磁栅尺,Magnescale传感器,Magnescale测厚仪,Mitutoyo光栅尺,笔式位移传感器-苏州连达精密量仪有限公司 | 【ph计】|在线ph计|工业ph计|ph计厂家|ph计价格|酸度计生产厂家_武汉吉尔德科技有限公司 | 清水-铝合金-建筑模板厂家-木模板价格-铝模板生产「五棵松」品牌 | 铝单板_铝窗花_铝单板厂家_氟碳包柱铝单板批发价格-佛山科阳金属 | 锡膏喷印机-全自动涂覆机厂家-全自动点胶机-视觉点胶机-深圳市博明智控科技有限公司 | 天津电机维修|水泵维修-天津晟佳机电设备有限公司 | 工程管道/塑料管材/pvc排水管/ppr给水管/pe双壁波纹管等品牌管材批发厂家-河南洁尔康建材 | 优考试_免费在线考试系统_培训考试系统_题库系统_组卷答题系统_匡优考试 | 【法利莱住人集装箱厂家】—活动集装箱房,集装箱租赁_大品牌,更放心 | 安德建奇火花机-阿奇夏米尔慢走丝|高维|发那科-北京杰森柏汇 | 氧化铝球_高铝球_氧化铝研磨球-淄博誉洁陶瓷新材料有限公司 | 济南冷库安装-山东冷库设计|建造|冷库维修-山东齐雪制冷设备有限公司 | 山楂片_雪花_迷你山楂片_山楂条饼厂家-青州市丰源食品厂 | 【中联邦】增稠剂_增稠粉_水性增稠剂_涂料增稠剂_工业增稠剂生产厂家 |