JavaScript设计模式-架构型设计模式简介

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuelian3015/article/details/89483679

指的是一类框架结构,通过提供一些子系统,指定它们的职责,并且将它们调理清晰组织在一起。主要包含了同步模块模式、异步模块模式、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(/&lt;/g,'<')
                
                .replace(/&gt;/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#[email protected]@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;
})();

猜你喜欢

转载自blog.csdn.net/xuelian3015/article/details/89483679