指的是一类框架结构,通过提供一些子系统,指定它们的职责,并且将它们调理清晰组织在一起。主要包含了同步模块模式、异步模块模式、Widget模式、MVC模式、MVP模式、MVVM模式
模块化
把复杂的系统分解为高内聚、低耦合的模块,让系统开发变得可控、可维护、可拓展,提高模块的复用率。
同步模块模式(SMD)
请求发出以后,不论模式是否存在。立即执行后续的逻辑,实现模块开发中对模块的立即引用。
模块管理与创建方法
/*** * 同步模块模式 */ //定义模块管理单体对象 var F = F || {}; /** * 定义模块方法 * @param str 模块路由 * @param fn 模块方法 */ F.define = function(str,fn){ //解析路由 var parts = str.split('.'), old = parent = this, //old为当前模块的祖父模块。parent是当前模块的父模块 i =len = 0; //第一个模式是模块管理器单体对象,则移除 if (parts[0]==="F") { parts = parts.slice(1); } //屏蔽对define和Module模块方法的重写 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; } //创建模块 F.define("string",function(){ return { trim:function(str){ return str.replace(/^\s+|\s+$/g,"") } } }); //获取元素方法dom(),html()设置元素内容 F.define("dom",function(){ var $ = function(id){ $.dom = document.getElementById(id); return $; } $.html = function(html){ if (html) { this.dom.innerHTML = html; return this; }else{ return this.dom.innerHTML; } } return $; }); //添加addclass方法 F.dom.addClass = function(type,fn){ return function(className){ if (!~this.dom.className.indexOf(className)) { this.dom.className += " " + className; } } }(); //模块调用方法 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); } //测试 F.module(['dom',document],function( dom,doc){ dom('test').html("new addd"); doc.body.style.background = "red" }); F.module("dom","string.trim",function(dom,trim){ var html = dom("test").html(); var str = trim(html);//除去字符串的空白部分 console.log(html+"****"+str) }) F.dom('test').addClass("tghfgh"); F.dom('test').html(154654);
异步模块模式(AMD)
请求发出之后,继续其他业务逻辑,直到模块加载完成执行后续的逻辑,实现模块开发中对模块加载完成后的引用。
/*** * 异步模块 */ //在闭包中传入模块管理器对象F ~(function(F){ //模块缓存器,存储已经创建的模块 var moduleCache = {} })((function(){ return window.F = {}; })()); /*** * 创建或者调用模块方法 * @param url 参数为模块的url * @param deps 参数为依赖模块 * @param callback 参数为模板主函数 */ F.module = function(url,modDeps,modCallback){ //把参数转化为数组 var args =[].slice.call(argsuments), //获取模块构造函数(参数数组中最后一个参数成员) callback = args.pop(), //获取依赖模块(紧邻回调函数参数,,且数据类型为数组) deps =(args.length && args[args.length-1] instanceof Array) ? args.pop() :[], //模块url(模块ID) url = args.length ? args.pop() : null, //依赖模块序列 params = [], //未加载的依赖模块数量统计 depsCount = 0, //依赖模块序列的索引 i = 0, // 依赖模块序列的长度 len; if (len = deps.length) { //遍历依赖模块 while (i<len) { //闭包保存i (function(i){ //增加未加载的依赖模块 depsCount++; loadModule(deps[i],function(mod){ //依赖模块序列中添加依赖模块接口引用 params[i] = mod; //依赖模块加载完成,依赖模块数量统计减一 depsCount--; //若依赖模块全部加载 if (depsCount===0) { //在模块缓存器中矫正该模块,并且执行构造函数 setModule(url,params.callback); } }); })(i) i++; } }else{ setModule(url,[],callback) } } var moduleCache = {}, /*** * 异步加载依赖模块所在文件 * @param moduleName 模块;路径(ID) * @param callback 模块加载完成回调函数 */ loadModule = function(moduleName,callback){ var _module ; if (moduleCache[moduleName]) { _module = moduleCache[moduleName]; if (_module.status === "loaded") { setTimeout(callback(_module.exports), 0); }else{ _module.onload.push(callback); } }else{ moduleCache[moduleName] = { moduleName : moduleName, status:'loading', exports:null, onload:[callback] }; loadScript(getUrl(moduleName)); } }, //获取文件路径 getUrl = function(moduleName){ return String(moduleName).replace(/\.js$/g,"")+".js"; }, loadScript = function(src){ var _script = document.createElement("script"); _script.type = "text/script"; _script.charset = "utf-8"; _script.async = true; _script.src = src; document.getElementsByTagName("head")[0].appendChild(_script); }; /** * 设置模块并且执行模块构造函数 * @param moduleName 模块ID名称 * @param params 依赖模块 * @param callback 模块构造函数 */ setModule = function(moduleName,params,callback){ var _module,fn; if (moduleCache[moduleName]) { _module = moduleCache[moduleName]; _module.status = "loaded"; _module.exports = callback ? callback.apply(_module,params) : null; while (fn = _module.onload.shift()) { fn(_module.exports); } }else{ callback && callback.apply(null,params); } } //测试 F.module("lib/dom",function(){ return { g:function(id){ return document.getElementById(id); }, html:function(id,html){ if (html) { this.g(id).innerHTML = html; }else{ return this.g(id).innerHTML; } } } });
Widget模式
Web Widget:指的是一块可以在任意地方执行的代码块。Widget模式是借用Web Widget的思路把页面分解为组件,针对组件开发,最后合成最终完整的页面。
/**** * Widget 模式 * 视图模块化 */ //模板引擎模板 F.module("lib/template",function(){ /** * 模板引擎 处理数据与编译模板入口 * @param str 模板容器ID 模板字符串 * @param data 渲染数据 */ var _TplEngine = function(str,data){ // if (data instanceof Array) { var html = "", i = 0, len = data.length; for(;i<len;i++){ html += _getTpl(str)(data[i]); } return html; }else{ return _getTpl(str)(data); } }, /** * 获取模板 * @param str 模板容器ID,或者模板字符串 */ _getTpl = function(str){ var ele = document.getElementById(str); if (ele) { //若是input或者textarea元素就获取该元素的value值,否则获取元素内容。 var html = /^(textarea | input)$/i.test(ele.nodeName) ? ele. value : ele.innerHTML; //编译模板 return _compileTpl(html) }else{ //编译模板 return _compileTpl(str); } }, //处理模板 _dealTpl = function(str){ //左右分隔符 var _left = '{%', _right = '%}'; return String(str) // 转义标签内的<,> .replace(/</g,'<') .replace(/>/g,">") //过滤回车符、制表符和空格符 .replace(/[\r\t\n]/g,'') //替换内容 .replace(new RegExp(_left+'=(.*?)'+_right,'g'),"',typeof($l)==='undefined'?'' : $l,'") //替换左边分隔符 .replace(new RegExp(_left,'g'),"');") //替换右边分隔符 .replace(new RegExp(_right,'g'),"template_array.push('"); }, /** * 编译执行 * @param str 模板数据 */ _compileTpl = function(str){ var fnBody= "var template_array = [];\n var fn = (function(data){\nvar template_key = '';\nfor(key in data){\ntemplate_key += ('var ' + key+'=data[\"'+key+'\"];');\n}\neval(template_key);\ntemplate_array.push('"+=_dealTpl(str)+"');\ntemplate_key = null;\n})(templateData);\nfn = null;\nreturn template_array.join('');"; return new Function("templateData",fnBody); }, return _TplEngine; });
MVC模式
就是模型(model)——视图(view)——控制器(controller),使用一种将 业务逻辑、数据、视图分离的方式组织架构代码。
/** * MVC模式 */ window.onload = function(){ //初始化MVC对象 var MVC = MVC || {}; // 初始化MVC数据模型层 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对应数据v M.data[m] = v; return this; }, //设置配置数据(通常在页面中执行某些操作,为做记录而更配置数据) setConf:function(c,v){ //配置数据字段c对应的配置数据V M.conf[c] = v; return this; } } }(); //初始化MVC视图层 MVC.view = function(){ //模型数据层对象操作方法引用 var M = MVC.model; //内部视图创建方法对象 var V = {}; //获取视图接口方法 return function(v){ //根据视图名称返回视图(由于获取的是一个方法,需要将该执行方法执行一遍以获取相应的视图) V[v]() } }(); //初始化MVC控制器层 MVC.ctrl = function(){ //模型数据对象操作方法引用 var M = MVC.model; //视图数据层对象操作方法引用 var V = MVC.view; //控制器创建方法对象 var C = {}; }(); }
MVP 模式
模型(model)——视图(View)——管理器(Presenter);View层不能直接使用model层内的数据,通过Presenter层实现对Model层内的数据访问。所有的交互都在Presenter中进行。代码思路如下(仅供参考):
/** * MVP模式 */ ~(function(window){ //MVP构造函数 var MVP = function(){}; //数据层 MVP.model = function(){ var M ={}; M.data = {}; var data = {}; var conf = {}; return { getData:function(m){ return M.data[m]; }, /** * 设置数据 * @param m 模块名称 * @param v 模块数据 */ setData:function(m,v){ M.data[m] = v; return v; }, getConf:function(c){ return M.conf[c] }, /** * 设置配置 * @param c 配置项的名称 * @param v 配置项值 */ setConf:function(c,v){ M.conf[c] = v; return v; } } }(); //视图层 MVP.view = function(){ var REPLACEKEY = '__REPLACEKEY__'; /** * 获取完整模板 * @param {*} str 元素字符串 * @param {*} type 元素类型 */ function getHTML(str,type){ //只处理字符串中第一个{}中的内容 return str.replace(/^(\w+)([^\{\}]*)?(\{([@\w]+)\})?(.*?)$/,function(match,$1,$2,$3,$4,$5){ $2 =$2 || ""; //元素属性参数容错处理 $3 = $3 ||""; //{元素内容}参数容错处理 $4 = $4 || ""; //元素内容参数容错处理 $5 = $5.replace(/\{([@\w]+)\}/g,''); //去除元素内容后面添加的元素属性中的{}内容 return type === "in"? '<'+$1+$2+$5+'>'+$4+REPLACEKEY+'</'+$1+">" : type === "add" ? '<'+$1+$2+$5+">"+$4+"</"+$1+'>'+REPLACEKEY : '<'+$1+$2+$5+">"+$4+"</"+$1+">"; }).replace(/#([@\-\w]+)/g,' id="$l"') //处理特殊标识 #--id .replace(/#([@\-\s\w]+)/g,' class="$l"')//处理特殊标识 #--class .replace(/\[(.+)\]/g,function(match,key){//处理其他属性组 var a = key .replace(/'|"/g,'')//过滤其中的引号 .split(" "),//以空格分组 h = ""; //属性模板字符串 for(var j =0,len = a.length;j<len;j++){ //处理拼接每一个属性 h += ' ' + a[j].replace(/=(.*)/g,'="$l"'); } //返回属性组模板字符串 return h; }) //处理可以替换内容, .replace(/@(\w+)/g,'{#$1#}'); } /** * 数组迭代 * @param arr 数组 * @param fn 回调函数 */ function eachArray(arr,fn){ for(var i=0,len = arr.length;i<len;i++){ fn(i,arr[i],len); } } /** * 替换兄弟元素模板或者子元素模板 * @param str 原始字符串 * @param rep 兄弟元素模板或者子元素模板 */ function formateItem(str,rep){ return str.replace(new RegExp(REPLACEKEY,"g"),rep); } /** * 模板解析 */ return function(str){ var part = str.replace(/^\s+|\s+$/g,"").replace(/^\s(>)\s+$/g,"$l").split(">"), html = REPLACEKEY, item, nodeTpl; eachArray(part,function(partIndex,partValue,partLen){ item = partValue.split("+"); nodeTpl = REPLACEKEY; eachArray(item,function(itemIndex,itemValue,itemLen){ nodeTpl = formateItem(nodeTpl,getHTML(itemValue,itemIndex === itemLen - 1 ? (partIndex === partLen -1 ? "":"in"):"add")); }); html = formateItem(html,nodeTpl); }) return html; } }(); //管理器层 MVP.presenter = function(){ var V = MVP.view; var M = MVP.model; var C = { /*** * 导航管理 * @param M 数据层对象 * @param V 视图对象 */ nav:function(M,V){ var data = M.getData("nav"); data[0].choose = 'choose'; data[data.length-1].list = "last"; var tpl =V('li.@mode @choose @last[data-mode=@mode]>a#nav_@mode.nav-@mode[href=url title=@text]>i.nav-icon-@mode+span{@text}'); $.create("ul",{ 'class':"navigation", "id":"nav" }).html( A.formateString(tpl,data) ).appendTo("container"); } }; return { init:function(){ for(var i in C){ C[i] && C[i](M,V,i) } } } }(); //MVP入口 MVP.init = function(){ this.presenter.init() } //暴露MVP对象。在外部可以访问MVP window.MVP = MVP; })(window); window.onload = function(){ MVP.init(); }
MVVM模式
模型(model)——视图(view) ——视图模型(viewModel):给视图层定做视图模型,并且在视图模型中创建属性和方法。
/** * MVVM模式 */ ~(function(){ var window = this || (0,eval)('this'); var FONTSIZE = function(){ return parseInt(document.body.currentStyle ? document.body.currentStyle["fontSize"] : getComputedStyle(document.body,false)["fontSize"]); }(); var VM = function(){ var Method = { /** * 进度条 * @param {*} dom 进度条容器 * @param {*} data 进度条数据模型 */ progressbar :function(dom,data){ var progress = document.createElement("div"); var param = data.data; progress.style.width = (param.progress || 100)+"%"; dom.className += ' ui-progressbar'; dom.appendChild(progress); }, /** * @name 滑动条组创建方法 * @param dom 滑动条容器 * @param data 滑动条数据模型 */ 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)+"%" }; } 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; } } } function getBindData(){} 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](dosm[i],ctx); } } }(); window.VM = VM; })();
文章评论