论坛首页 Web前端技术论坛

自创了一套JS的设计模式

浏览 4742 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (13) :: 隐藏帖 (0)
作者 正文
   发表时间:2013-01-09  
框架基于JQUERY库、及JUICER的模板引擎,以及SEAJS的框架依赖处理。
源码部分使用了John Resig的Simple JavaScript Inheritance

以下是源码部分:
define(function(require,exports, module){

    var $=require('jquery');
    require('../../hashchange/1.3.0/hashchange.js');
    var Juicer=require('../../juicer/0.6.4/juicer-debug.js');
    var WSUI=$UI={};
    WSUI.Classes={};
    WSUI.Singleton={};
    $UI.setup={};

    if(typeof jsContent=="undefined")jsContent={};
    jQuery.extend($UI.setup,jsContent);
    /* Simple JavaScript Inheritance
     * By John Resig http://ejohn.org/
     * MIT Licensed.
     */
    // Inspired by base2 and Prototype
    var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
    // The base Class implementation (does nothing)
    var Class = function(){};
    // Create a new Class that inherits from this class
    Class.extend = function(prop) {
        var _super = this.prototype;
        // Instantiate a base class (but only create the instance,
        // don't run the init constructor)
        initializing = true;
        var prototype = new this();
        initializing = false;
        // Copy the properties over onto the new prototype
        for (var name in prop) {
            // Check if we're overwriting an existing function
            prototype[name] = typeof prop[name] == "function" &&
                typeof _super[name] == "function" && fnTest.test(prop[name]) ?
                (function(name, fn){
                    return function() {
                        var tmp = this._super;

                        // Add a new ._super() method that is the same method
                        // but on the super-class
                        this._super = _super[name];

                        // The method only need to be bound temporarily, so we
                        // remove it when we're done executing
                        var ret = fn.apply(this, arguments);
                        this._super = tmp;

                        return ret;
                    };
                })(name, prop[name]) :
                prop[name];
        }
        // The dummy class constructor
        function Class() {
            // All construction is actually done in the init method
            if ( !initializing && this.init )
                this.init.apply(this, arguments);
        }
        // Populate our constructed prototype object
        Class.prototype = prototype;

        // Enforce the constructor to be what we expect
        Class.prototype.constructor = Class;

        // And make this class extendable
        Class.extend = arguments.callee;
        return Class;
    };

    WSUI.Core= Class.extend({
        init:function(_opt){
            this.observers=[];//增加观察者

            this.random=function(length, upper, lower, number){
                if( !upper && !lower && !number ){
                    upper = lower = number = true;
                }
                if(!length)length=20;
                var a = [
                    ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"],
                    ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"],
                    ["0","1","2","3","4","5","6","7","8","9"]
                ];

                //临时数组
                var b = [];

                //临时字串
                var c = "";

                b = upper ? b.concat(a[0]) : b;
                b = lower ? b.concat(a[1]) : b;
                b = number ? b.concat(a[2]) : b;

                for (var i=0;i<length;i++){
                    c += b[ Math.round(Math.random()*(b.length-1)) ];
                }

                return c;
            };
            //每个对象都产生一个随机数作为id

            this.set('id',this.random());

            //判断是否开启调试
            if(this.debug ){
                this.log=function(log,memo){//l内容,m说明
                    if(!memo)memo='';
                    //采用seajs的log替代console.log,避免IE出错
                    seajs.log(' %s 输出 %s: %o',this.name,memo,log);
                }
            }else{
                this.log=jQuery.noop;
            }
        },

        _set:function(para,val){
            var _this=this;
            this[para]=val;
            //遍历observer表,如果此属性在观察者列表里,则进行匹配
            jQuery.each(this.observers,function(i){
                if((this['para']==para)&&(this['val']!=val)){//如果参数相等,值有变更,则执行observer函数
                    this['func'](val);//将变更后的值返回给函数,并执行
                    //更新observers
                    _this.observers[i]['val']=val;
                }
            })
        },
        set:function(para,val){//设置变量,需要实现BACKBONE的变更检测
            var _this=this;
            if((arguments.length==1)&&(typeof(para)=='object')){//只有一个对象
                jQuery.each(para,function(k,i){
                    _this._set(k,i);
                })
            }else{
                this._set(para,val);
            }
            return this;
        },
        get:function(para){//获取变量
            return this[para];
        },
        addObserver:function(para,func){//观察者,参考emberjs的观察者模式实现
            var _this=this;

            this.observers.push({
                'para':para,
                'val':_this[para],
                'func':func
            });
            return this;
        },
        registerSingleton:function(){//标识单例
            var root=WSUI.Singleton;
            //目前先采用同级的方法
            root[this.name]=this;
        },
        unregisterSingleton:function(){//删除单例表示
//            var root=WSUI.Singleton;
//            //目前先采用同级的方法
//            root[this.name]=this;
        },
        registerClasses:function(_cls,id){//绑定注册表
            var root=WSUI.Classes;
            //目前先采用同级的方法
            if(!id){
                root[_cls]=this;
            }else{
                if(!root[_cls])root[_cls]={};
                root[_cls][id]=this;
            }
        },
        unregisterClasses:function(_cls){//注销
            delete WSUI.Classes[_cls];
        },
        destroy:function(){
            this.set('id',null);
            //清理IE内存
            if(jQuery.browser.msie)CollectGarbage();
        }
    });

    WSUI.Model=WSUI.Core.extend({
        debug:false,
        init:function(_opt){
            this._super(_opt);
            this.cache={};//缓存列表
        },
        //overwrite:如果存在不覆盖,不存在写入
        buildCache:function(id,val,overwrite){
            if(!overwrite)overwrite=false;
            if(this.cache[id]){
                if(overwrite){
                    this.cache[id]=val;
                }
            }else{
                this.cache[id]=val;
            }
        },
        readCache:function(id){
            if(this.cache[id]){
                return this.cache[id];
            }else{
                return undefined;
            }
        },
        clearCache:function(){
            this.cache={};
        },
        //重新定义remove方法,由于JS的特性,remove只移除本身所用到的对象,但自身还得由外部del
        destroy:function(){
            this._super();
        }
    });
    WSUI.View=WSUI.Core.extend({
        debug:false,
        name:'',
        init:function(_opt){
            if(!_opt)_opt={};
            this._super(_opt);
            this.set({
                'el':_opt.el
            });
            this._super(_opt);

            this.dom=jQuery('<div>',{id:this.id});//考虑到样式全部由模板决定,所以取消view的class,此VIEW只作为容器,标注ID即可

            this.get('el').html(this.dom);

        },
        //目前已支持局部渲染
        //注:底层框架不关心异步问题
        render:function(_opt){
            if(!_opt)_opt={};
            var option={
                data:_opt.data||{},
                tpl:_opt.tpl||'<div></div>',
                node:_opt.node||'',
                callback:_opt.callback|| $.noop
            };
            var _this=this;

            if(option.node==''){

                _this.dom.html(jQuery(Juicer(option.tpl,option.data)).html());
            }else{
                _this.dom.find(option.node).html(jQuery(Juicer(option.tpl,option.data)).find(option.node).html());
            }
            //option.callback();
        },
        destroy:function(){
            this._super();
        }
    });
    WSUI.Presenter=WSUI.Core.extend({
        debug:false,
        init:function(_opt){
            if(!_opt)_opt={};
            this._super(_opt);
            this.set('el',_opt['el']||(function(){seajs.error('target属性必须填写')})());

            //如果has_module,则remove原模块
            if(this.get('el').attr('has_module')){
                WSUI.Classes[this.get('el').attr('has_module')].remove();
            }
            //在target上声明id,方便调用其他模块时执行自动卸载
            this.get('el').attr('has_module',this.get('id'));
        },
        destroy:function(){
            this._super();
        }
    });
    //运用此方法替代new 以实现单例
    WSUI.Create=function(_class,_opt){
        if(!_opt)_opt={};
        if(WSUI.Singleton[_class.prototype.name]){//检测单例列表里是否有此类,如果有,直接返回
            return WSUI.Singleton[_class.prototype.name];
        }else{
            return new _class(_opt);
        }
    };

    return WSUI;
});
   发表时间:2013-01-09  
以下是具体的使用代码
define(function(require,exports, module){
    var WSUI=require('../../../assets/wsui/3.0.0/wsui.js');
    var 作者_Template='<div>\n    <ul>\n        会什么:<br />\n        {@each skillLevel as item,index}\n        ${index} 等级${item}\n        {@/each}\n    </ul>\n\n    <ul>\n        不会什么:\n        {@each worstSkill as item}\n        ${item}\n        {@/each}\n    </ul>\n\n</div>';
    var 作者_Model=WSUI.Model.extend({
        name:'作者_Model',
        init:function(选项){
            if(!选项)选项={};
            this._super(选项);
            this.set('个人信息',{
                'skillLevel':{// {skillName:level 1~5}
                    'html':5,
                    'css':5,
                    'javascript':4,
                    'photoshop':5,
                    '性能优化':4,
                    '搜索引擎优化':4,
                    '设计模式':3,
                    'UI设计':4,
                    'UE设计':3
                },
                'worstSkill':['后台开发能力','服务器部署维护','专业的单元测试']
            })
        },
        获取个人信息:function(){
            return this.get('个人信息');
        }
    });
    var 作者_View=WSUI.View.extend({
        name:'作者_View',
        init:function(选项){
            if(!选项)选项={};
            this._super(选项);
        }
    });
    var 作者_Presenter=WSUI.Presenter.extend({
        name:'作者_Presenter',
        init:function(选项){
            if(!选项)选项={};
            this._super(选项);
            this.模型层=WSUI.Create(作者_Model);
            this.表现层=WSUI.Create(作者_View,{
                el:this.get('el')
            });
            this.表现层.render({
                tpl:作者_Template,
                data:this.模型层.获取个人信息()
            })
        }
    });

    module.exports=WSUI.Create(作者_Presenter,{
        el:$('#个人')
    });
});
0 请登录后投票
   发表时间:2013-01-09   最后修改:2013-01-09
代码说明:
line 3:定义模板
line 4~27:定义模型
line 28~34: 定义视图层
line 35~51:定义了主持层,也可以理解为控制层

设计思路
模板HTML模板只需关心页面HTML及CSS即可
数据模型数据层只需提供数据,并根据需要做数据缓存,而无需关心表现。
视图层:负责视图本身的JS控件及模板文件的选择。注:视图控件不会涉及到视图以外的部分以及数据的请求
主持层:负责视图的选择、数据模型的选择,当然还包括控件的选择,他所使用的控件与视图层的控件最大差别就是 可以操作视图及模型。

原则上为了将耦合度降到最低~只需主持层决定模型及视图的调用,而视图决定模板的选择,数据层及模板完全可以独立开发。

补充:这套结构有点类似MVC,但个人觉得他并不能列入MVC的设计模式,因为
1、目前位置他的MODEL操作并没有绑定视图层,而需要通过主持层来手动刷新模板。
2、主持层支持多个MODEL的介入。

目前设计的还不是很完善,后期会增加类似BACKBONE的ROUNTER的功能~。

希望大家有什么意见能补充~~~
0 请登录后投票
   发表时间:2013-01-15  
太漫长了!
0 请登录后投票
   发表时间:2013-03-21  
能不能给出一个完整的demo附件啊?能不能!
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics