架構型設計模式
一類框架結構,通過提供一些子系統,指定他們的職責,並將它們條理清晰的組織在一起
1.同步模塊模式
模塊化:將複雜的系統分解成高內聚、低耦合的模塊,使系統開發變得可控、可維護、可拓展、複用率更高
同步模塊模式(SMD),請求發出後,無論模塊是否存在,立即執行後續的邏輯,對模塊的立即引用
1.1排隊開發
多人實現,某一處耦合了多人的實現代碼,出現排隊開發
1.2模塊化開發
工程師獨立的去開發自己的模塊,模塊之間也可相互調用
首先得有一個模塊管理器,管理着模塊的創建和調度
調度:同步模塊調度 異步模塊調度
1.3模塊管理器
定義一個模塊管理對象,再爲其創建一個模塊定義方法define
//定義模塊管理器單體對象
var F = F || {};
//模塊方法按理應該放在閉包中,這裏爲了更清晰明白
F.define = function(str, fn){
var parts = str.split('.'),
old = parent = this,
i = len = 0;
if(parts[0] === 'F'){
parts = parts.slice(1);
}
if(parts[0] == 'define' || parts[0] === 'module'){
return;
}
for(len = parts.length; i < len; i++){
if(typeof parent[parts[i]] === 'undefined'){
parent[parts[i]] = {};
}
old = parent;
parent = parent[parts[i]];
}
if(fn){
old[parts[--i]] = fn();
}
return this;
}
1.4模塊調用方法
要使用模塊,需要一個'使用'模塊方法-module
F.module = function(){
var args = [].slice.call(arguments),
fn = args.pop(),
parts = args[0] && args[0] instanceof Array ? args[0] : args,
modules = [],
modIDs = '',
i = 0,
ilen = parts.length,
parent, j, jlen;
while(i < ilen){
if(typeof parts[i] === 'string'){
parent = this;
modIDs = parts[i].replace(/^F./, '').split('.');
for(j = 0, jlen = modIDs.length; j < jlen; j++){
parent = parent[modIDs[j]] || false;
}
modules.push(parent);
}else{
modules.push(parts[i]);
}
i++;
}
fn.apply(null, modules);
}
1.5調用模塊
參數分爲兩部分,依賴模塊與回調執行函數(最後一個參數)
首先遍歷並獲取所有的依賴模塊,並一次保存在依賴模塊列表
然後將這些依賴模塊作爲參數傳入執行函數中執行
F.module(['dom', document], function(dom, doc){
dom('test').html('new add!');
doc.body.style.background = 'red';
});
2.異步模塊模式
異步模塊模式(AMD):請求發出後,繼續其他業務邏輯,知道模塊加載完成執行後續的邏輯
實現模塊開發中對模塊加載完成後的引用
2.1異步加載文件中的模塊
瀏覽器中加載文件是異步加載的,也就是說文件開始加載,還可以繼續做其他的事情
同步模塊模式會立即引用這個模塊,加載未完成,是引用不到該模塊
2.2給定依賴模塊,耐心等待所有模塊加載完成再執行
3.Widget模式
一塊可以在任意頁面中執行的代碼塊
將頁面分解成部件,針對部件開發,最終組合成爲完整的頁面
4.MVC模式
模型(model)-視圖(view)-控制器(controller)
用一種將業務邏輯、數據、視圖分離的方式組織架構代碼
組件式架構開發,常常將視圖、數據、業務邏輯寫在一個模塊內
組件內容過多會造成層次混亂,增加開發與維護的成本
4.1混亂
當看別人組件的時候,缺少註釋,很難追蹤數據,代碼塊不清晰
4.2MVC模式
分層:數據層、視圖層、控制層
視圖層可以調用數據層創建視圖
控制器層可以調用數據層數據與視圖層內視圖創建頁面增添邏輯
//頁面加載後創建MVC對象
$(function(){
//初始化MVC對象
var MVC = MVC || {};
MVC.model = function(){};
MVC.view = function(){};
MVC.ctrl = function(){};
});
4.3數據層
每個對象是一個自動執行的函數
3個層次對象可被調用,聲明的函數在執行之前不能被調用
執行一遍是爲了爲其他對象調用提供接口方法
MVC.model = function(){
var M = {};
M.data = {};
M.conf = {};
return {
getData : function(m){
return M.data[m];
},
getConf : function(c){
return M.conf[c]
},
setData : function(m, v){
M.data[m] = v;
return this;
}
setConf : function(c. v){
M.conf[c] = v;
return this;
}
}
}();
通過model接口對象返回的4個操作方法即可對model內部的服務器端數據與配置數據做
增刪改查,這樣我們就可以在視圖對象和控制器對象內部輕鬆處理數據模型內部數據
4.4視圖層
MVC.view = function(){
var M = MVC.model;
var V = {}
return function(v){
V[v]();
}
}();
4.5控制器
MVC.ctrl = function(){
var M = MVC.model;
var V = MVC.view;
var C = {};
}();
4.6側邊導航欄數據模型層
MVC.model = function(){
var M = {};
M.data = {
slideBar : [
{
text : 'xxx',
icon : 'left.png',
content : 'xxx',
img : 'xxx.png',
href : 'xxx.com'
}
]
}
M.conf = {
slideBarCloseAnimate : false
}
return {/*接口方法*//}
}();
4.7側邊導航欄視圖層
4.8側邊導航欄控制器層
4.9執行控制器
5.MVP模式
模型(model)-視圖(view)-管理器(presenter)
view層不直接引用model層內的數據,而是通過presenter來實現數據訪問
所有層次的交互都發生在presenter中
在MVC中視圖層因爲渲染直接去數據層拿數據,而contrl常常不知道
5.1MVP模式
~(function(window){
var MVP = function(){};
MVP.model = function(){};
MVP.view = function(){};
MVP.presenter = function(){};
MVP.init = function(){}
window.MVP = MVP;
})(window)
5.2數據層
在數據層修改不大,在視圖層大刀闊斧
5.3視圖層
MVP.view = MVP.view = function(){
return function(str){
return html;
}
}();
5.4管理器
MVP.presenter = function(){
var V = MVP.view;
var M = MVP.model;
var C = {};
return {
init : function(){
for(var i in C){
C[i] && C[i](M, V, i);
}
}
};
}();
5.5將數據層與視圖層完全解耦,互不影響
6.MVVM模式
模型(model)-視圖(view)-視圖模型(viewmodel)
爲視圖層量身定做一套視圖模型,在視圖模型中創建屬性和方法
爲視圖層綁定數據並實現交互
6.1MVP需要創建管理器,一些開發者對JS瞭解的不是很深入,操作管理器成本太大
能否通過html來創建視圖實現頁面需求?
6.2視圖層的思考
創建視圖也就是創建頁面內的視圖,本質就是寫HTML代碼
如果將視圖作用提升,通過直接書寫HTML代碼創建視圖組件
讓控制器或管理器去監聽這些視圖組件,並進行處理完成預期功能
這對那些只懂HTML代碼的開發者,輕鬆完成功能需求
6.3視圖模型層
//~屏蔽壓縮報錯
~(function(){
//在閉包中獲取全局變量
var window = this || (0, eval)('this');
//獲取頁面字體大小,作爲創建頁面UI尺寸參照物
var FONTSIZE = function(){
//獲取頁面body元素字體大小並轉換成整數
return parseInt(document.body.currentStyle ? document.body.currentStyle
['fontSize'] : getComputedStyle(document.body, false)['fontSize']);
}();
//視圖模型對象
var VM = function(){
//組件創建策略對象
var Method = {
//進度條組件創建方法
progressbar : function(){},
slider : function(){}
}
}();
//將視圖模型對象綁定在window上,供外部獲取
window.VM = VM;
})();
6.4創建進度條
progressbar : function(dom, data){
//進度條進度完成容器
var progressbar = document.createElement('div'),
param = data.data;
progress.style.width = (param.position || 100) + '%';
dom.className += ' ui-progressbar';
dom.appendChild(progress);
}
6.5創建滑動條
slider : function(dom, data){
var bar = document.createElement('span'),
progress = document.createElement('div'),
totleText = null,
progressText = null,
param = data.data,
width = dom.clientWidth,
left = dom.offsetLeft,
realWidth = (param.position || 100) * width /100;
dom.innerHTML = '';
if(param.totle){
text = document.createElement('b');
progressText = document.createElement('em');
text.innerHTML = param.totle;
dom.appendChild(text);
dom.appendChild(progressText);
}
setStyle(realWidth);
dom.className += ' ui-slider';
dom.appendChild(progress);
dom.appendChild(bar);
function setStyle(w){
progress.style.width = w+ 'px';
bar.style.left = w - FONTSIZE / 2 + 'px';
if(progressText){
progressText.style.left = w - FONTSIZE / 2 * 2.4 + 'px';
progressText.innerHTML = parseFloat(w / width * 100).tofixed(2) + '%';
};
}
}
6.6讓滑動條動起來
slider : function(dom, data){
bar.onmousedown = function(){
document.onmousemove = function(event){
var e = event || window.event;
var w = e.clientX - left
setStyle(w < width ? (w > 0 ? w : 0) : width);
}
document.onselectstart = function(){
return false;
}
}
document.onmouseup = function(){
document.onmousemove = null;
document.onselectstart = null;
}
}
6.7爲組件添睛
獲取數據的方法getBindData,通過元素中給定的數據映射標識獲取對應數據來渲染我們的視圖
//視圖模型對象
var VM = function(){
//組件創建策略對象
var Method = {
//....
}
function getBindData(dom){
var data = dom.getAttribute('data-bind');
return !!data && (new Function("return ({" + data +"})");
}
}();
6.8查找組件
獲取需要渲染的組件
//視圖模型對象
var VM = function(){
//組件創建策略對象
var Method = {
//....
}
function getBindData(dom){}
return function(){
var doms = document.body.getElementsByTagName('*'),
ctx = null;
for(var i = 0; i < doms.length; i++){
ctx = getBindData(doms[i]);
ctx.type && Method[ctx.type] && Method[ctx.type](doms[i], ctx);
}
}
}();
6.9展現組件
var demo1 ={
position : 60,
totle : 200
},
demo2 = {
position : 20
},
demo3 = {position : 50};
window.onload = function(){
//渲染組件
VM();
}