解放你的 ALT(CMD)+TAB 键吧

做前端开发的童鞋们,最常敲的一组键就是 alt(cmd)+tab 然后紧接着是 F5 或者 ctrl(cmd) + r 了吧。这是在干什么?写好一段样式看看效果啊。

那我们能不能边写样式,边让浏览器自动加载最新的 css 呢?当然是可以的了,业界已经有很多这样的工具了。比如国内前段时间曾经成功推广过的 F5 ,还有国外的 live.js xrefresh 等工具。

但是这些工具都没有适合我的。F5 需要用它提供的 HTTP 服务,但是对于一个大型站点来说,静态资源都是单独管理的,页面上一般都写的绝对 URL 使用 cookie free 的域名或者 CDN 的域名。为了开发方便,我们一般会用 apache 在本地搭一个 style 环境,像我们现在,因为经常需要跨应用访问 css, js 这些,本地环境不一定有这些文件,还需要用 url rewrite 把这些文件 rewrite 到一个公共环境去。这些复杂功能都是 F5 所不能提供的。live.js 只能对 css 和 page 同域才有效,而且还不支持 @import ,xrefresh 也需要使用它提供的 HTTP 服务。而且这些工具都有一个共同点,是都需要往页面插入一段它的 js 。

于是,我就自己造了一个,优先满足我的需求,那就是要做到服务器无关,编辑器无关。然后要支持 css 和 page 不同域,支持 @import 。而我平时又使用 chrome 开发,那就写一个 chrome 插件好了。而且由于使用了浏览器插件技术,所以我也可以做到不在页面插入任何 JS,不改变任何 DOM 结构,尽量保证安全,不影响页面功能。

现在这个项目的源码托管在 github :https://github.com/allenm/css-auto-reload , 欢迎各位提 BUG和建议,或者来写一个其他浏览器的插件。

如果你和我一样喜欢使用 chrome 开发,那就直接来这里 https://chrome.google.com/webstore/detail/fiikhcfekfejbleebdkkjjgalkcgjoip 安装,使用很简单,只要点击插件图标,就会点亮图标,表示正在对当前标签页进行监控,再次点击会关闭监控。

详细介绍和使用说明可以看上面 github 项目的介绍。另外我还录了一段操作视频,你可以先看看,再决定要不要试试这个工具。

 

如果使用后觉得好用,确实对你有所帮助,不要忘记向你身边的 web developer 们介绍下哦,有心情的话,可以去 chrome web store 上面给我个好评哦。

jQuery deferred 对象的 promise 方法

jQuery 从 1.5 版本引入了 deferred 对象,这是一个基于 CommonJS Promises/A 的设计,为了方便异步操作,大家都知道,在 js 中,异步代码是非常的多。

这篇博客不是来详细讲解 $.Dererred 的,阮一峰 的博客里有一篇博客详细介绍了这个。但是在这篇博客中,关于 promise() 这个方法的讲解却是错误的,我发现这个是因为我徒弟去学习这个的时候,看了这篇文章,然后我让他给我讲述的时候,发现了这个错误。所以我想写篇 blog 来说明下。

update(2012.1.2):  我针对此问题给一峰发了 email, 他已经针对下面的问题进行了更新,下面的引用来自他最初的版本。现在他博客中对 promise() 的讲解是正确的,如果想了解整个  Deferred 对象,建议直接移步他的博客中学习。同时感谢阮一峰发现的我这篇博客中的一个小错误,最后面的那段代码,现在已经修复。

下面是引用自阮一峰的博客:

这里有两个地方需要注意。

首先,最后一行不能直接返回dtd,必须返回dtd.promise()。原因是jQuery规定,任意一个deferred对象有三种执行状态—-未完成,已完成和已失败。如果直接返回dtd,$.when()的默认执行状态为”已完成”,立即触发后面的done()方法,这就失去回调函数的作用了。dtd.promise()的目的,就是保证目前的执行状态—-也就是”未完成”—-不变,从而确保只有操作完成后,才会触发回调函数。

下面是 jQuery 的官方文档

The deferred.promise() method allows an asynchronous function to prevent other code from interfering with the progress or status of its internal request. The Promise exposes only the Deferred methods needed to attach additional handlers or determine the state (then,donefailalways,pipeprogress, and state), but not ones that change the state (resolverejectprogressresolveWithrejectWith, and progressWith).

也就是说,deferred.promise() 只是阻止其他代码来改变这个 deferred 对象的状态。可以理解成,通过 deferred.promise() 方法返回的 deferred promise 对象,是没有 resolve ,reject, progress , resolveWith, rejectWith , progressWith 这些可以改变状态的方法,你只能使用 done, then ,fail 等方法添加 handler 或者判断状态。

deferred.promise() 改变不了 deferred 对象的状态,作用也不是保证目前的状态不变,它只是保证你不能通过 deferred.promise() 返回的 deferred promise 对象改变 deferred 对象的状态。如果我们这个地方直接返回 dtd,也是可以工作的,.done 的处理函数还是会等到 dtd.resolve() 之后才会执行.

具体在那篇博客的例子, 如果我们把代码改成如下的形式:

var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(dtd){
    var tasks = function(){
        alert("执行完毕!");
        dtd.resolve(); // 改变deferred对象的执行状态
    };
    setTimeout(tasks,5000);
    return dtd;
};
$.when(wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });

这样的执行结果和先前返回 dtd.promise 的结果是一样的。

差别在什么地方呢?如果我们把 $.when 的这块的代码改成这样的:

var d = wait(dtd);
$.when(d)
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
d.resolve();

我们会发现 alert(“哈哈,成功了!”) 会立即执行,“执行完毕”却需要5秒后才弹出来。

但是如果我们 wait 函数最后是 return dtd.promise() 这里 d.resolve() 就会报错了,因为对象 d 不存在 resolve() 方法。

同样如果我们把代码改成:

var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(dtd){
    var tasks = function(){
       alert("执行完毕!");
       dtd.resolve(); // 改变deferred对象的执行状态
   };
   setTimeout(tasks,5000);
   return dtd.promise();
};
dtd.resolve();
$.when( wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });

我们也可以发现 alert(“哈哈,成功了!”) 会立即执行,因为 dtd 这个 deferred 对象在被传入 wait 之前,已经被 resolve() 了,而 deferred 对象一旦被 resolve 或者 reject 之后,状态是不会改变的。

然后我们再把 $.wait 这块的代码改成:

$.when( wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
dtd.resolve();

我们也会发现 alert(“哈哈,成功了!”); 被立即执行,虽然 wait(dtd) 执行的时候, dtd 还没有被 resolve,而且 wait 方法返回的是 dtd.promise(), 但是 dtd 这个原始的 deferred 对象是暴露在外面的,我们还是可以从外面改变它的状态。

于是,如果我们真的不想让其他代码能改变 wait 方法内部的 deferred 对象的状态,那我们应该写成这样:

var wait = function(){
    var dtd = $.Deferred(); // 新建一个deferred对象
    var tasks = function(){
        alert("执行完毕!");
       dtd.resolve(); // 改变deferred对象的执行状态
   };
   setTimeout(tasks,5000);
   return dtd.promise();
};
$.when( wait())
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });

也就是不要把 deferred 直接暴露出来,最后返回 deferred.promise() ,让其他地方的代码只能添加 handler 。

WebSocket Protocol 介绍与实现

今天在方凳会上分享了 websocket protocol以及简单实现,PPT 和代码都放出来,有兴趣的可以围观

然后用 python 简单的实现了 websocket server,代码如下。在网上找的很多代码,都不再兼容 websocket draft 10, 因为 websocket 的草案进化太快,下面的代码是按照 draft10 写的。 chrome 14+ , firefox 7/8 都没有问题。

关于 websocket 草案,请阅读 http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10

 

另外推荐一个 Python 的 websocket server http://www.tavendo.de/autobahn/home.html, 基于 twisted 的异步模型。

使用SVN hook 让前端BUG少一点

你有没有发生过,发布的代码中含有 console ,然后在 IE 下报错?或者代码中有调试用的alert,用户点击某个东西后,就会弹出来?

这种错误 ,我相信前端开发者或多或少都会犯,要避免这类的BUG ,需要非常非常小心,但是人总会有疏忽的时候,这样的事情交给机器办比较合适,因为机器会按照指定的规则来检查你提交的代码,每次都完全按照规则来。

版本管理软件一般都有预置的hook,我们可以写一些脚本在我们对代码仓库做操作的时候来执行,这里,我将给出一个 TortoiseSVN 可以使用的 client side hook 脚本来做这种检测。

这个是目前我在实际生产环境使用过一段时间的,因为我们这里使用的 SVN 做版本控制,SVN 标准只需要 server 端实现 hook script,客户端不需要,由于大公司server端不那么容易去更改,恰巧 TortoiseSVN 实现了 clientside hook ,那就用这个来实现吧。

按照官方文档给的 pre commit hook script 修改而来,使用前,先要检查你的windows 是否用 WScript 关联了 .js 文件,绝大部分前端开发人员都关联了某个编辑器吧。

检测方法:随便新建一个 js 文件,里面写上”WScript.Echo(“Hello World!”);”,双击看能不能运行,如果你关联了编辑器,就肯定不行了,如果弹出个窗口“hello world”,那就可以跳过这一节了。

恢复windows 默认 .js 文件关联程序的方法:修改注册表,[HKEY_CLASSES_ROOT\.js] 项下的那个默认值改成 “JSFile”。修改完后看看刚才的测试文件是否正常运行。

然后,在本地磁盘任意位置新建 .js 文件,保存此代码:

/**
* author: Allen.M
* blog: http://blog.allenm.me
* date: 2010-12-13
* 根据 http://tortoisesvn.googlecode.com/svn/trunk/contrib/hook-scripts/client-side/PreCommit.js.tmpl 修改
* this script is a local pre-commit hook script.
*/
var objArgs,num;

objArgs = WScript.Arguments;
num = objArgs.length;
if (num != 4)
{
    WScript.Echo("Usage: [CScript | WScript] PreCommit.js path/to/pathsfile depth path/to/messagefile path/to/CWD ");
    WScript.Quit(1);
}

var paths = readPaths(objArgs(0));
var message = "list of paths selected for commit:\n";
var pattern = /^\s*(?!\/\/)(alert|console)/;
var haveWarning = false;
var i = 0;
while (i < paths.length)
{
	message = message + paths[i] + "\n";

	var content = readPaths(paths[i]);

	for(var m = 0, l = content.length; m < l; m++){
		if(pattern.test(content[m])){
			haveWarning = true;
			message = message + 'warning: ' + paths[i] + ' line ' + (m+1) + ' have "alert" or "console" \n';
		}
	}
	i = i + 1;
}
message = message + "path of message file is: " + objArgs(2) + "\n";

if(haveWarning){
	WScript.Echo(message);
}
WScript.Quit(0);

function readPaths(path)
{
	var retPaths = new Array();
	var fs = new ActiveXObject("Scripting.FileSystemObject");
	if (fs.FileExists(path))
	{
		var a = fs.OpenTextFile(path, 1, false);
		var i = 0;
		while (!a.AtEndOfStream)
		{
			var line = a.ReadLine();
			retPaths[i] = line;
			i = i + 1;
		}
		a.Close();
	}
	return retPaths;
}

然后按照下图所示配置脚本。

需要注意的是,指定脚本文件的地方,一定要在前面加上WScript,否则你 ci 代码的时候会得到一个出错信息。

此脚本不干涉你CI代码,即使有 alert ,console 等关键字,也只会给出个 warning ,代码还是可以 ci 成功,当出现 warning 的时候,你需要做的是检查代码是不是有问题。 //console 这样被注释掉的代码是被忽略的。另外你的代码要使用 windows格式的换行符,否则会有检测失败的情况。

检测代码的关键代码是中间的那行正则,你可以修改为适合你的。

warning 是这种形式显示的,如果无warning 信息,则会安安静静的。

Magic canvas & PNG

用 canvas 配合 PNG 可以做出什么好玩的东西呢?

昨天阿里中文站的方凳会,我为大家分享了这个主题。这是我第一次在方凳会上做大分享,也算迈出了这一步,以后会努力多在这个舞台上表现。

主要讲了运用 canvas 来做动态 favicon 和用 PNG 来打包字符串(js) 然后前端通过 js 对这个图片解码。

PPT 在 http://share.allenm.me/fd/ ,有兴趣的同学可以来看,我就不在这里重复一遍内容了。

使用google closure 压缩合并 js 文件的 python 脚本

google很早就推出了 google closure ,但是是JAVA的,你需要安装 JAVA的运行环境、配置等等。另外虽然google 也推出了WEB版的closure,但是用起来也是比较麻烦的,操作步骤比较多。如果你想使用 google closure,又懒得配置,又喜欢命令行的方便,现在有了另外的选择,google closure 提供了 API,这样我们就可以用其他的方式来压缩合并文件了。

我根据google 的python 事例,做了一些小修改,来方便压缩合并文件。这基本上算是我第一次写python,并放出来让人使用,python我只懂皮毛,大家看了代码不要笑话我。

项目地址:http://github.com/allenm/js-css-compressor 使用方式详见README

目前默认压缩级别是:SIMPLE_OPTIMIZATIONS 将来会有选项让用户选择压缩级别。

另外项目名称虽然是 js-css-compressor。但是目前没有 css 压缩的功能,因为 closure 不提供CSS的压缩,这个将来我会使用其他方式实现。

我把这个脚本放在VPS上来压缩合并我博客上的JS文件,准备一步步的优化这个博客的性能,拿这个作为试验田。

欢迎给我提意见和建议,联系方式在博客的About me 页面。