[ Content | Sidebar ]

Posts tagged javascript

相对完美的设计思路~

有没有可能写一套代码适应以下三种情况:
1. 客户端没有开启javascript,或者使用ucweb一类的弱智浏览器访问时,功能没有影响
2. 客户端开启了javascript,每个页面都有单独加载的js,但是禁用ajax,只帮助用户交互
3. 客户端开启了javascript,为了提高载入速度,尽量使所有操作用ajax完成

略有所得: http://www.blogo2.com
源代码: http://code-of-emptyhua.googlecode.com/svn/trunk/blogo2

没有禁用js的时候,页面切换,表单提交(注册和登录没有用)尽量的使用ajax,而且部分操作会使用弹层(登录后的管理),禁用js之后,则所有操作换成普通表单提交在页面之间跳来跳去。。

还有一个功能没有开启,就是可以选择不用ajax,但是独立开的页面js依然有效。

有兴趣的同学可以check源代码,这种模式的实现:
1. 开发的时候禁用ajax,单个页面开发,而且要求禁用js时业务可以走通,页面内的js和css统一由模板引入。
2. 对需要开启ajax的链接增加hook,例如data-ajax=”dialog:{}”,则表明在开启ajax模式后,点击这个链接需要弹窗
3. ajax模式下只需要对单个页面更换特有的模板,动态加载需要的js和css即可。

这种设计的特点:
1. 尽量一个人包前后端。。
2. json不潮了,ajax返回html代码,优点是js不用套页面了,缺点是流量大

over,虽然没有完美的代码,但是我尽量~

———–
post by gmail~

js中简单实现数据和ui绑定

很简单的例子

//计数加一
count ++
//更新ui
jQuery('#count').text(count);
Copy Code 

相信几乎所有前端工程师都会写过上面这样的代码,更新数据,然后更新ui,行云流水般的。 然而也是相当一部分内测bug的起源,数据删除了,ui里的计数没有变,或者ui变了,数据里确没有删除。而在逻辑复杂的应用里,更是很难保证不出这种低级错误。

其实我们可以通过自定义事件,这样重写

this.data_bind('count', 'change', function(count)
{
    //更新计数
    jQuery('#count').text(count);
});
....
 
this.data_bind('count', 'change', function(count)
{
    //刷新分页
    refresh_pager(count);
});
 
this.set_data('count', this.get_data('count') + 1);
Copy Code 

上次D2上听金大为讲模板技术,当时感觉耳目一新,最近边写边考虑这些新思想,其实使用现有的自定义事件要简单的多,而且灵活性,适用性更好。

用过flex的同学看完这篇文章肯定会说:靠,你们写js的还真是原始社会。。

js业务框架中对ajax的再次包装

一个简单的load动作,如果简单用任何一个js框架实现

startloding();
new ajax({
    url : 'test.php',
    compete : function()
    {
         stoploading()
    },
    success : function(data)
    {
        try
        {
            var obj = json_decode(data);
        }
        catch(e)
        {
            //服务异常,未返回json造成异常
            error('服务器异常');
            return;
        }
 
        //正常的业务逻辑错误
        if ( json['code'] !== 0 )
        {
            error(json['desc']);
            return;
        }
 
        ...
    },
    error : function()
    {
        //网络请求失败造成异常
        error('网络异常');
    }
});
Copy Code 

而一个业务框架对ajax再次包装,成为一个action

new action({
    action : 'load',
    action_name : '加载',
    url : 'test.php'
});
 
api.add_action('load_start', startloading);
api.add_action('load_complete', stoploading);
//这里的成功不是ajax成功,而是判定json['code']之后action真正的成功,这就要求所有action需要有统一的成功判定标准,和json包装格式
api.add_action('load_success', function(msg, data){  .... });
//这里没有添加异常处理,因为没有特殊需要,所以action会执行默认的错误处理,可能是一个弹窗
Copy Code 

不仅仅是节省了代码,后者有更好的可读性,load动作本身也具有了更好的重用性

具体的框架代码已经在用,还不是很完美,待贴~

wordpress api的js实现版本

上次提到的一个想法的实现版本,正在应用到偶开发的项目当中~
比wordpress的action和filter增加两个特性

  • 能够添加一个只运行一次的action或者filter
  • 可以通过返回{end:true}这种特殊对象来结束一个action或者filter

这两个特性在web开发里灰常有用

<html>
<script>
//2010.4.10 第一版
(function(ns)
{
    var filters = {};
    var max_priority = 10;
    var default_priority = 10;
 
    var array_include = function(a, v)
    {
        for ( var i = 0, j = a.length; i < j; i ++ )
        {
            if ( a[i] == v ) return true;
        }
        return false;
    };
 
    var to_array = function(a)
    {
        var rt = [];
        for ( var i = 0, j = a.length; i < j; i ++ )
        {
           rt.push(a[i]); 
        }
        return rt;
    };
 
    function _set(func, tag, priority, key, value)
    {
        func['__filter_' + tag + '_' + priority + '_' + key] = value; 
    }
 
    function _get(func, tag, priority, key)
    {
        return func['__filter_' + tag + '_' + priority + '_' + key]; 
    }
 
    ns.add_filter = ns.add_action = function(tag, func, priority, run_once) 
    {
        run_once = run_once === undefined ? false : run_once;
        priority = priority || default_priority; 
        if ( !filters[tag] ) filters[tag] = {};   
        if ( !filters[tag][priority] ) filters[tag][priority] = [];
        if ( !array_include(filters[tag][priority], func) )
        {
            filters[tag][priority].push(func);
        }
 
        _set(func, tag, priority, 'run_once', run_once);
    };
 
    ns.add_once_filter = ns.add_once_action = function(tag, func, priority)
    {
        ns.add_filter(tag, func, priority, true);
    };
 
    function filter_or_action(ac, tag, value)
    {
        if (!filters[tag]) return value;
        if ( ac == 'action' )
        {
            var args = to_array(arguments);
            args.shift();
            args.shift();
        }
        var _cfs = filters[tag];
        var i = 1, func; 
        var rt = value;
        while ( i <= max_priority )
        {
            if ( !_cfs[i] || _cfs[i].length === 0 ) 
            {
                i ++;
                continue;
            }
 
            for ( var j = 0, length = _cfs[i].length; j < length; j ++ )
            {
                func = _cfs[i][j];
 
                if ( ac == 'action' )
                {
                    rt = func.apply(null, args);
                }
                else
                {
                    rt = func.call(null, rt);
                }
 
                if ( _get(func, tag, i, 'run_once') ) 
                {
                    _cfs[i].splice(j, 1);
                    j --;
                    length --;
                }
                if ( rt && rt['end'] ) return rt['value'] || null;
            }
 
            i ++;
        }
        return rt;
    };
 
    ns.apply_filters = function(tag, value)
    {
        return filter_or_action('fitler', tag, value)
    };
 
    ns.do_action = function(tag)
    {
        var args = to_array(arguments);
        filter_or_action.apply(null, ['action'].concat(args));
    };
})(window)
 
function log(str)
{
    document.write('<p>' + str + '</p>');
}
 
//普通的filter
add_filter('status', function(text)
{
    return text.replace(/orz/g, '超人');
});
log(apply_filters('status', 'orzorz'));
//再添加一个filter
add_filter('status', function(text)
{
    return text.replace('超人', '动感');
});
log(apply_filters('status', 'orzorz'));
 
//添加一个优先级较高的filter,并通过返回包含end字段的对象结束过滤器
add_filter('status', function(text)
{
    return {end:true, value:text};
}, 5);
log(apply_filters('status', 'orzorz'));
 
//但是下面这个优先级较高的会执行
add_filter('status', function(text)
{
    return text + '管不着我';
}, 1);
log(apply_filters('status', 'orzorz'));
 
log('-----------------------------');
log('action测试');
log('-----------------------------');
 
//下面是action
add_action('ac_success', function(a, b, c)
{
    log('返回数据:' +  a + b + c); 
});
 
//添加一个只会执行一次的action回调
add_once_action('ac_success', function(a, b, c)
{
    log('我只会执行一次的');
});
 
add_action('ac_success', function(a, b, c)
{
    log('俺的优先级比较高');
}, 5);
 
log('----------第一次执行----------------');
do_action('ac_success', '1', '2', '3');
 
log('----------第二次执行----------------');
do_action('ac_success', '4', '5', '6');
 
add_action('ac_success', function(a, b, c)
{
    log('全部屏蔽');
    return {end:true}; 
}, 1);
 
log('----------第三次执行----------------');
do_action('ac_success', '1', '2', '3');
</script>
</html>
Copy Code | Run Code

例举js中组装类的好处

先举个简单的组装的例子

//纸
var paper = new Paper();
//处理下
enable_fire_destory(paper);
//ok,可以燃烧了..
paper.fire();
//猪
var pig = new Pig();
enable_fire_destory(pig);
//不管怎么着,也能着了
pig.fire();
Copy Code 

这种组装方式用于给毫不相关的对象快速添加一些通用的方法, 用法得当将节省大量代码~

这里举个实际开发中的例子,下面的代码肯定是我们平时经常写的,用于防止一次ajax请求没有完毕就请求第二次

obj = {
....
is_loading : false,
load_sth : function(callback)
{
    if ( this.is_loading ) return;
    this.is_loading = true;
    new Ajax({
        url : 'xxx.php',
        onComplete : function()
            This.is_loading = false;
        }});
    });
},
....
};
Copy Code 

使用组装的方法改写

//组装函数
function enable_single_thread_ajax(obj)
{
    extend(obj, {
        _is_ajax_ing : false,
        is_ajax_ing : function()
        {
            return this._is_ajax_ing;
        },
        ajax : function(params)
        {
            if (this.is_ajax_ing()) return;
            var This = this;
            this._is_ajax_ing = true;
 
            var _tmp_func = function(){};
 
            //为了保持ajax的参数不变,要做点小牺牲
            if (params['onComplete'])
            {
                _tmp_func = params['onComplete'];
            }
 
            params['onComplete'] = function()
            {
                This._is_ajax_ing = false; 
                _tmp_func.apply(this, arguments);
            };
 
            return Ajax(params);
        }
    });
}
 
//还是那个对象 
obj = { 
load_sth : function()
{
    //打完收工了~
    this.ajax({
        url : 'xxx.php'
    });
}   
};
//给对象扩充方法
enable_single_thread_ajax(obj);
Copy Code 

还可以直接扩展原型,达到扩充类的目的~,以前写过一篇自定义事件的文章,也是用的这种方法 : 简单实现js里的自定义事件

今天特地翻开没怎么看的<javascript设计模式>,里面定义这种扩展方式为”掺元类”,感觉这么翻译不是特好理解…