T3: 構建大型網站的 JavaScript 框架
15 Aug 2016T3 是一個 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
<button data-module="test-module">Test</button>
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
<button data-module="module-domeventdelegate">Test DOMEventDelegate</button>
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
<div data-module="module-broadcast-1">1</div>
<div data-module="module-broadcast-2">2</div>
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
<div data-module="module-test">test</div>
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
<div data-module="module-test">test</div>
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 框架
由於部落格搬遷至此,因此在這裡放了一份,以便閱讀;部份文章片段也做了些許修改,以期提供更好的內容。