T3 - 構建大型網站的JavaScript框架

T3 - 構建大型網站的JavaScript框架 T3.js
T3是一個JavaScript UI Framework,主要的功能是讓程式碼更結構化。如果網站內的程式碼很雜亂的話,很適合用來整理散落在各處的程式碼(尤其是針對年紀大需要翻新的大型網站)。而整理方式就是將程式碼分成幾個部份:Application、Module、Serveice、Behavior等來處理。頁面UI整理成Module,Module彼此共用的方法或事件整理成Behavior,而非UI邏輯且多個Module會共用的部份(例如:與Server端溝通取資料)則抽出來成為Service。

Module

將頁面功能切成模組(Module)來運作,如果頁面無法切成多個模組就將整個頁面視為一個模組。
模組的事件有以下幾種:Message Handling、Behavior、Event Handlers(例如:onclick)。如下:頁面上的模組「test-module」,可能需要針對其他模組的反應來做出回應,因此有Message Handling;不同模組但有重覆的事情要做,則可用Behavior;模組內的事件處理可使用onclick等;非UI邏輯的部份丟給Service即可。

HTML


JS

Box.Application.addModule('test-module', function(context) {
  return {
    messages: ['statechanged', 'statecompleted'],
    onmessage: function(name, data) {
      switch(name) {
        case 'statechanged':
          console.log('statechanged');
          break;
        case 'searchcomplete':
          console.log('statecompleted: ' + data.numResults + ' tasks completed.');
          break;
      }
    },
    behaviors: ['getInfo'],
    init: function(){
      console.log('module init');
    },
    onclick: function(event, element, elementType){
      console.log('click');
      context.getService('connectSomething').connect();
    }
  }
});

Service

Module與Behavior負責處理UI,而非UI的部份則交給Service。Service可當成一個共用的介面,讓不同的Module和Behavior來共同呼叫。例如:使用ajax取資料。如下範例,「getInfo」這個Service負責與Server端溝通取資料,我們呼叫這個Service的method「getSearchResultByKeyword」,並將結果回傳給Behavior「getSearchResult」。
Box.Application.addService('getInfo', function(application) {
  return {
    getSearchResultByKeyword: function(query) {
      $.ajax({
        url: '/getSearchResult',
        type: 'post',
        data: {
          query: query
        },
        dataType: 'json',
        success: function (response) {
          return response.data;
        },
        error: function (xhr) {
          alert('噢噢!發生錯誤了!請重新再試一次~');
        }
      });
    }
  };
});

Box.Application.addBehavior('getSearchResult', function(context) {
  var query = "日式料理";
  return {
    init: function() {
      var data = context.getService('getInfo').getSearchResultByKeyword(query);
      console.log(data);
    }
  };
});

Box.Application.init();

Behavior

不同模組但有重覆的事情要做,則可在模組中使用Behavior。
Box.Application.addModule('module-test-1', function(context) {
  return {
    behaviors: ['element-button'],
    init: function(){
      console.log('init');
    }
  }
});

Box.Application.addModule('module-test-2', function(context) {
  return {
    behaviors: ['element-button'],
    init: function(){
      console.log('init');
    }
  }
});

Box.Application.addBehavior('element-button', function(context) {
  return {
    init: function() {
      console.log('add behavior');
      context.getService('connectSomething').connect();
    }
  };
});

Box.Application.addService('connectSomething', function(application) {
  return {
    connect: function() {
      console.log('connect something...');
    }
  };
});

Box.Application.init();

DOMEventDelegate

委派,範例如下。

HTML


JS

Box.Application.addModule('module-domeventdelegate', function(context) {
    var element = context.getElement();
    var delegate = new Box.DOMEventDelegate(element, {
        onclick: function(event) {
            console.log('DOMEventDelegate: ' + event.type);
        }
    });

  return {
    init: function(){
      var element = context.getElement();
      delegate.attachEvents(); //DOMEventDelegate
    },
    onclick: function(event, element, elementType){
      console.log('click!');
    }
  }
});

Box.Application.init();

Context

broadcast

模組訂閱事件,事件發生時會通知模組。可用在網站的訊息中心等。

HTML

1
2

JS

Box.Application.addModule('module-broadcast-1', function(context) {
  return {
    messages: ['broadcast-a', 'broadcast-b'],
    onmessage: function(name, data) {
      switch(name) {
        case 'broadcast-a':
          console.log('broadcast-a');
          Box.Application.broadcast('broadcast-b');
          break;
        case 'broadcast-b':
          console.log('broadcast-b');
          break;
      }
    }
  }
});

Box.Application.addModule('module-broadcast-2', function(context) {
  return {
    messages: ['broadcast-a', 'broadcast-c'],
    onmessage: function(name, data) {
      switch(name) {
        case 'broadcast-a':
          console.log('broadcast-a');
          break;
        case 'broadcast-c':
          console.log('broadcast-c');
          break;
      }
    },
  }
});

Box.Application.init();
Box.Application.broadcast('broadcast-a');
Box.Application.broadcast('broadcast-c');

getGlobal

取得window物件。

HTML

test

JS

Box.Application.addModule('module-test', function(context) {
  return {
    init: function(){
      var navigator = context.getGlobal('navigator');
      console.log(window.navigator.userAgent === navigator.userAgent);
    }
  }
});

Box.Application.init();

getGlobalConfig

取得 Box.Application.init 中的設定物件。

HTML

test

JS

Box.Application.addModule('module-test', function(context) {
  return {
    init: function(){
      console.log(context.getGlobalConfig('userName'));
    }
  }
});

Box.Application.init({
    userName: 'Bob'
});

以上只列出目前比較常用的部份,T3的文件寫得很詳細,有興趣的話可以看一下。
我也將之前參加駭客松的作品吃什麼,どっち部份改版使用T3實作。
以上的程式碼都會放在Github上(含簡單的TodoList),如果有什麼建議或想法都歡迎聊聊。

由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-T3 - 構建大型網站的 JavaScript 框架
張貼留言