Category: 未分类

  • 安装 TortoiseSVN 并不需要重启

    在 Windows 下面,TortoiseSVN 应该是最常用的 Subversion 客户端了。但是安装完成的时候它都会要求重启计算机,连 FAQ 上都是这么说的:

    You rebooted your PC of course after the installation? If you haven’t please do so now.

    不过我觉得实际上并不需要重启。它要求重启只是为了跟 explorer 的绑定生效,而为了重启 explorer 去重启计算机显然有点得不偿失。

    只需打开任务管理器,找到 explorer 进程并结束它,然后点击菜单“文件-新建任务(运行…)”,输入 explorer 回车,等一下任务栏出来的时候,一起就都 OK 了。

  • JavaScript/CSS 部署 – 从石器时代到工业时代

    随着前端开发在 Web 开发中的地位日渐重要,前端相关的资源部署也成了各大网站开始重视的一步。

    对于一个访问量相当大的网站来说,从前端方面考虑,除了使用 CDN 向客户端分发 JavaScript/CSS 资源,充分利用客户端浏览器缓存也是减轻服务器压力的一个重要途径。

    首先按照我们的“常识”,认为 JavaScript/CSS 默认就是在浏览器中缓存“很长”一段时间的,作为最简单的策略,我们就依赖这种缓存机制来降低服务器的压力。然而,对于一个不断改进、更新的网站来说,JavaScript/CSS 的变动是相当频繁的。

    如何让浏览器知道这些静态资源的改变呢?让我们从最简单的解决方案开始摸索和学习,慢慢改进。

    刀耕火种的方式

    把这件事扔给开发者吧。以 JSP 页面为例,首先在一个统一的 jsp 文件(例如 version.jsp)中定义所有静态资源的版本号:

    [code lang=”html”]
    <%
    String dialogVersion = “20090101”;

    %>
    [/code]

    然后在每个用到 JavaScript/CSS 的页面中包含该文件并且给静态资源加上版本号:

    [code lang=”html”]
    <%@ include file="/WEB-INF/include/common.jspf" %>



    [/code]

    开发者负责在每次 commit 代码前检查自己修改过的静态资源,去修改相应的版本号。这给本来就不堪重负的开发者又增加了一项艰巨任务,他们难免会有疏忽的时候,等到有客户反馈问题,才想起来——哦,忘记改版本号了!

    自动化

    一个成熟的开发团队应该使用版本控制工具来管理代码,一般最常用的是 Subversion. 除此之外,还应该有一套完善的部署脚本,执行一两个命令即可完成整个部署过程,而不是手动地去复制、修改代码。这个过程可能包含从 svn 检出代码、编译(解释型语言则省去了这一步)、选择性的复制(避免覆盖生产环境中的特定配置)等等。

    有了这些基础,把上面手工修改版本号的工作交给自动脚本去做就可以了。”svn info” 命令可以列出文件最后被修改的版本号,我们只要得到这个版本号,把它写在 version.jsp 中即可。写这个脚本可能稍微麻烦一点,不过 python, perl, bash, ruby, 随便什么语言都可以,或者是一个 ant task. 这是一劳永逸的事情。开发者们可以把精力集中在真正的前端开发商了。

    不要用 query string!

    我本来以为到此为止就很完美了,但查阅了一些资料之后发现我的许多观点都是完全错误的。

    首先,在上述的方法中,版本号是作为 query string 附在静态资源的 URL 上的,然而:

    According the letter of the HTTP caching specification, user agents should never cache URLs with query strings. While Internet Explorer and Firefox ignore this, Opera and Safari don’t – to make sure all user agents can cache your resources, we need to keep query strings out of their URLs.

    以上摘自文章 “Serving JavaScript Fast

    正确的办法是将版本号插在文件名之中,后缀之前,然后利用 Web Server 的 URL Rewrite 功能指向不带版本号的真实文件,实际上就是欺骗一下客户端。具体做法请参考上文,或者 “Automatically Version Your CSS and JavaScript Files“.

    仅仅 304 还不行!

    假设浏览器的缓存中什么都没有,这时用户去访问一个网站,引用到的 JavaScript/CSS 都要到服务器上去下载。服务器将这些静态资源返回给客户端的同时,会附加一个 Last-Modified 时间值。下次客户端再请求该资源,会把这个时间值附加在 If-Modified-Since 这个 header 字段中传给服务器,意思就是如果在这个时间之后修改过才有必要重新传输。如果没有修改,响应的状态码就是 304.

    下面用 fiddler 抓取 IE 浏览器请求我的 blog 首页的过程,做个测试。第一张图,蓝色虚线上面是缓存为空时访问页面的请求,虚线下面是重启浏览器之后再次访问的请求情况。为了说明问题,我只保留了与问题密切相关的一些请求,可以点击查看大图。

    ie-fiddler

    请求 #0, #28 都是请求页面本身,两次都是全部传输,状态码是200. 请求 #1, #2 分别和 #29, #30 对应,一个是 JavaScript,一个是 CSS. 从 body 那一列可以看出消息体长度都是 0,而状态码是 304. 再看请求 #3,在第二次访问时,浏览器根本没有再次请求该资源,关键就在于 Caching 那一列的属性。

    尽管减少了数据传输,请求数却是影响服务器承载能力的最重要因素。理想的情况自然是像 #3 请求的 widget02.css 那样,告诉浏览器在一年以后该资源才会过期。

    如果使用的 Web server 是 Apache httpd,那么可以利用它的 mod_expires 来实现 JavaScript/CSS 自动添加 Expires 头信息。具体方法在前面提到的文章里都有。

    最后再次列一下参考的宝贵资源:

    1. Automatically Version Your CSS and JavaScript Files
    2. Serving JavaScript Fast
    3. Best Practices for Speeding Up Your Web Site
    4. What Is Caching and How Does It Apply to the Web?

    事实上本人的实际经验还停留在刀耕火种的程度,只是把最近学到和想到的东西分享一下而已……在未来的项目中我肯定会实践这些想法,并在可能的情况下把实际的代码分享出来。文中如有错误,欢迎指正。

  • 表单自动focus并不总是好的

    许多网站的登录页面为了提升用户体验,都在页面加载完成后让用户名那个input自动获取焦点。就连struts的html:form标签也考虑到了这一点,提供了一个focus的属性。

    Gmail, Delicious都是这么做的。它们一般是hook页面的onload事件,调用input元素的focus方法。但是在网速较慢的情况下,onload可能会在表单显示之后好久才触发,而该事件触发前,用户可能已经填写了好几个表单项。假如此时用户刚好开始填写密码,就出现了下面的情景:

    auto-focus

    不仅破坏了两个已经填好的字段,对密码的安全也是一个威胁。

    某些方法例如jQuery提供的domReady可以比onload更早执行,然而在网速极慢的时候仍然无法完全避免这种情况。

    解决办法?我想大概有两种思路吧,一种是在用户名字段后面紧接着就插入一段javascript,让该字段获取焦点。

    另一种,当表单中有字段获得焦点时,hook到onload的函数不再设置焦点。实现方式可以有很多种。

  • 关于CrucialWebHost

    Crucial Web Hosting实际上是我尝试的第一家也是迄今为止唯一一家国外空间商。当时本来想在dreamhost, hostgator之类的里面挑一个的,正好CrucialWebHost做推广,价格非常优惠,$7.95/mo,然后还有优惠券,最后似乎是花了700多买了两年。最重要的是带有独立IP,这在其它空间上那里得每月几美元。

    我不知道RatePoint是一家什么样的网站,不过上面关于CrucialWebHost的评论全部是溢美之词,这也迷惑了我好一阵子。在我使用该空间一年多的时间里,空间商两次更改IP而不通知客户,每次都是发现不能访问之后与之联系才得知IP更换。他们的理由是客户应该使用他们的DNS Server,然而其管理功能极弱,自己不能随意修改A/CNAME等记录,只能通过技术支持修改。虽然他们非常乐意为你效劳,但我相信没人愿意这么麻烦……

    2008年9月24日,我的网站在访问时出现错误:

    Table ‘xxx’ was created with a different version of MySQL and cannot be read

    赶紧与客服联系。答曰,正在升级服务器的MySQL. 真让人无语。

    后来又有一天,我有个使用mysqli的小程序出错了,mysqli无法加载,与客服交流后被告知:他们正在编译该模块。

    When Dependability is Crucial Depend on Crucial Web Hosting, Ltd.

    这句话出现在每个技术支持邮件的末尾,颇具讽刺意味。

    在我买了空间不久,价格便暴涨到$25/mo,当然,涨价是不能指责人家的,他们也要活命嘛,以后买不起搬走就是了。不过最近我发现原来许多的条款例如无限个域名绑定,无限子域名,无限FTP账户都改了,有图为证:

    quota

    你可以到他们网站看最新的参数,对比2007年12月31日的样子。感谢archive.org!

    另外该空间商跟Magento不知道是什么关系,几乎在倾力为Magento hosting打造空间,官方blog的大部分文章都是关于这个的。

    总之,感觉有点失望。什么split-shared,什么crucial,都是狗屁。

  • 转让国内1000M主机空间

    年前买了捌号主机的1000M PHP空间,本来想做个小网站玩玩的,不过事情不太顺利。您也知道,在我们文明的法治国家,做什么都得有个license,我的license就迟迟没有到手,相关部门的电话也是相当的热门,永远也打不通。每当想到这些,我脑海中都涌现出他们伟大的爷爷奶奶爸爸妈妈们,可是不知道从谁开始问候,就一切尽在不言中了。

    正好又赶上了深受广大网民拥护的整治互联网低俗风系列活动,我深怕我的内容不能摆脱低俗之风,毒害“生活在懵懂之中的青少年”,遮挡住“幸福的曙光”,于是下定决心,牺牲我一个,幸福千万家,为营造一个“窗明几净的网络空间”做贡献。

    现转让该PHP空间,08年12月20号买的,现在刚好还剩10个月,买的时候是398元,现在200元转让。空间的具体参数在捌号主机网站上可以查看

    最好通过淘宝交易:商品链接

    旺旺不在线时,可以发电子邮件:email
    本人开机即随时收邮件,反应速度足够快……

  • WordPress 2.7评论CSS解惑

    WordPress 2.7内置了threaded comment,评论的class name也变得非常繁杂。Betty给出了一张WordPress 2.7 评论结构一览图,但是我想许多人大概还不知道某些class name的规则是什么样的。

    这里只说每条评论上添加的class name,总结一下,有这么多:

    comment, trackback, pingback, 这三个不用多说,用来区分评论类型,方便用CSS样式来区分不同类型的。它们互相排斥,所以要想三个有统一的样式,就得这么写规则:

    [code lang=’css’]ol.commentlist li.comment, ol.commentlist li.pingback, ol.commentlist li.trackback {

    }[/code]

    byuser, comment-author-*, bypostauthor. 如果评论者是注册用户,会加上byuser和”comment-author-[nickname]“两个类,这样可以区分注册用户与非注册用户,甚至可以根据用户的不同呈现不同的样式。如果评论者是文章的作者,还会加上一个bypostauthor,这个最重要,很多blog用来突出显示文章作者的评论。

    odd, alt, even. 评论的交替样式。评论从0开始计数,奇数时是odd, alt,偶数时是even. 注意,odd和alt两个类必然同时存在。一般情况下,根据odd, even来写CSS,另一种方式是根据有无alt这个类来写,两种方式选择其一即可。还要注意计数的顺序是按照“前序遍历”,具体可参考文章“WordPress 2.7自定义单条评论结构”中的图片。因此你不能保证这个thread(顶级评论)是奇数,下一个thread就是偶数。

    thread-odd, thread-alt, thread-even. 和odd, alt, even类似,不过只计顶层评论。

    depth-[number]. 表示评论嵌套的深度,顶层评论的class是depth-1,向下递增。可以根据这个调整输入框大小,以我的为例:

    [code lang=’css’]#comment {
    width: 530px;
    border: 1px solid #C4EF95;
    font-size: 12px;
    }
    .depth-1 #comment {
    width: 470px;
    }
    .depth-2 #comment {
    width: 410px;
    }
    .depth-3 #comment {
    width: 350px;
    }[/code]

    parent, 有回复的评论会加上这个class.

    应该就是这些。

    最后我想说,要是再有两个depth-odd, depth-even就好了。我想实现的效果是顶层评论背景色交替,每个thread里面不同深度的嵌套回复背景色也要交替。没有这两个类,我就只好用depth-1, depth-2裸写,写到我限制的深度就行了。有兴趣的可以看看我的style.css.

    以上的逻辑查看wp-includes/comment-template.php即可得知,关键是get_comment_class函数。

  • JavaScript性能相关资源

    客户端应用程序的日益丰富越来越显示出JavaScript的性能劣势,由于这种语言太大的灵活性,大多数实现都是解释器类型,性能都不会太好,人们甚至怀疑基于JavaScript的重量级web应用是否还有前途。好消息是Google Chrome的JavaScript引擎是一个虚拟机叫做v8,Firefox 3.1将会把原来的SpiderMonkey替换为更强大的TraceMonkey,一个应用JIT技术的引擎。

    但是Chrome才刚刚出现,Firefox所占的市场份额也还不足够让人把它当作准绳。我们时时得考虑古老、陈旧、笨重的Internet Explorer,至少我们还没看见IE8的JavaScript实现有任何值得欢呼的线索。

    即使所有浏览器都有了高效的实现,注重代码本身的性能依旧是一个好的习惯。写出优质的代码,比把劣质的代码扔给编译器/解释器去优化好得多,即使编译器/解释器的优化很强大,因为起点不一样。

    下面是一些对了解JavaScript性能有帮助的一些资源:

    • IE Blog里关于JS性能的文章,我建议新手先不要看IE/JScript Blog里的东西,他们经常会拿出自家的一些私藏来招待你。是的,IE没有实现JavaScript,他们实现的是一个叫JScript的东西,看起来跟JavaScript有点像。

      IE + JavaScript Performance Recommendations – Part 1 变量使用的建议,你会看到,解释器不会做一丁点的优化,一些看似无用的多余代码可能会帮助你改善性能。
      Part 2: JavaScript Code Inefficiencies 关于字符串拼接,eval和switch.
      Part 3 提到一点内存泄漏问题。

    • jQuery的作者指出提高客户端性能的工作不仅存在于JavaScript本身。
      JavaScript Performance Stack
    • When innerHTML isn’t Fast Enough 我们知道如果有一大堆HTML要插入DOM,innerHTML方式要比document.createElement然后插入快得多。这篇文章给出一种比innerHTML更快的方式。
    • Comet Information Systems写的系列文章:

      String Manipulation 字符串拼接
      DOM Manipulation innerHTML, createElement, cloneNode.
      General Tips

    • 对于编译器来说,循环不变量外提是最基本的优化之一,然而你可不要认为JavaScript解释器也会为你做这个:
      JavaScript loop performance
    • 微软曾经针对IE推出一个自动更新,声称内存泄漏得到了解决,一时间开发者们欢欣鼓舞。但是马上人们就发现了问题,和他们在blog里的惯用手法一样,这只不过是掩人耳目,夸大其词。

      IE’s Memory Leak Fix Greatly Exaggerated

  • WordPress 2.7自定义单条评论结构

    WordPress 2.7内置了threaded comment之后,原来主题的comments.php文件中繁琐的评论循环简化成了一个函数调用:wp_list_comments.

    然而不加任何参数调用该函数的话,它输出的HTML结构是固定的。尽管它给每个元素都提供了丰富的class name供CSS样式使用,但是CSS并不足够灵活(除非使用一些我认为不是太好的方式,比如大量绝对定位),可以任意显示固定的DOM结构。某些情况下,我们还是希望能够自己来定义DOM结构。

    WordPress开发者当然考虑到了这一点,在Codex上的文档页就给出了一个例子。实际上就是wp_list_comments接收的数组参数中可以传入两个回调函数。一个是callback,负责输出一条评论的主体部分。另一个是end-callback,负责输出评论的结束部分。

    解释一下。threaded comments实际上在内部就是一个“森林”,每一条顶层评论和它的所有回复、回复的回复构成一棵树。wp_list_comments对每棵树做一次遍历,首先调用参数callback指定的函数,然后递归遍历其子节点(输出回复评论),完成之后再调用参数end-callback指定的函数。如果不指定这两个函数,WordPress就会输出默认的结构,实际上大家都是把默认输出复制到自己的回调函数中,然后修改一下结构。简单的说,就是callback输出一个块的开头加上该评论自己的内容,end-callback输出这个块的结尾,中间是子评论。如下图所示的遍历顺序,偏下的箭头表示调用callback,偏上的箭头表示调用end-callback.

    tree (by qingbo)

    以上提到的关键代码都在wp-includes/comment-template.php中。

  • HTML编辑器之选择-FCKEditor/TinyMCE/NicEdit

    这三个都是开源的HTML所见即所得(WYSIWYG)编辑器,下面简单地做一个比较。

    FCKEditorTinyMCE都是非常老牌的了,我接触web开发的时候它们看起来都已经非常成熟了。TinyMCE至今仍然是WordPress的默认可视化编辑器。

    但是老牌的不一定是好的,它们在长年累月的积累中,吸纳了各种各样的需求,最后形成一个大而全的系统,无所不能。于是它们庞大而笨重,分别看一下它们的demo页吧,它们可能是要决心与微软的Word软件竞争,而不是给开发者提供一个适合浏览器环境的编辑器。

    各种命令都带有一堆看似合理实际上毫无用处的选项,对于开发者要面对的用户,更是会让他们无所适从。例如要插入图片,弹出的窗口里,有”alternative text”, “vertical space”, “title”, “horizontal space”等参数,有些连熟悉web开发的人都感到陌生,更不用说普通用户了。

    当然了它们都号称customizable,然而我确定这是有限的。以我使用FCKeditor的经验,要想在插入图片窗口删除那些无用的参数,仅保留一个链接地址的输入,就必须在它的源代码中做修改,而不是配置文件。修改源代码必然造成升级的困难。

    庞大的代码蕴含着丰富的功能,而我们只用其一小部分,这对于带宽来说明显是个浪费。没有具体数过,根据我的印象,它们至少应该有数以百计的小文件吧,简直无法忍受。

    这两个大家伙早期的时候还更过分,按钮的图片都是一张一张的,打开页面时就看到它们像多米诺骨牌一样依次展开。以前它们都使用window.open打开一个新窗口显示文本对象的属性,在许多情况(大多是个别用户客户端的问题)下,弹出的窗口大小有问题而且用户无法调整,反馈给我们也束手无策。还好这两个问题现在都已经解决,CSS sprite被使用,选项窗口也使用div来模拟实现,这两点是值得肯定的进步。

    ————反衬结束,主角登场————

    上一个项目要选择编辑器的时候,我死活不想再用FCKeditor了。在网上做了许多搜索,刚好发现一个新出现的编辑器:NicEdit.

    What is NicEdit?

    NicEdit is a Lightweight, Cross Platform, Inline Content Editor to allow easy editing of web site content on the fly in the browser.

    NicEdit Javascript integrates into any site in seconds to make any element/div editable or convert standard textareas to rich text editing.

    轻量级是最大的优点。此外它还有许多优良的特性,比如输入框随文本自动拉高,良好的扩展性等。用它来实现了插入自定义表情的功能,非常简单、优美。

    不用多说了,把前面提到的那两个编辑器的缺点反过来,主语换成NicEdit放在这里就可以了。

    嗯,我就是要唱衰FCKEditor/TinyMCE,力挺NicEdit.

    也许FCKEditor/TinyMCE曾经也像NicEdit一样小巧玲珑?希望NicEdit能够守住贞操,不要被那些永无止境的Feature request给f*ck了。FCKEditor不会就是这样得名的吧?-joke.

    NicEdit现在还不是很完美,但是它在不断改进。如果你要用,建议先逛一下它的论坛,看看大家都遇到了什么问题,对你关键不关键。

  • 表单富文本输入,选择什么方式?

    现在几乎所有的网站开发都必须面对一个问题,用户怎么输入带格式的文本呢?最基本的功能包括链接、图片、文字加粗、字体大小颜色等。不同的网站有不同的考虑,有不同的实现。实现得越简单,给用户的门槛就越高。我就尝试从简到繁列一下一些常见的实现方式。

    1. textarea里写HTML代码

    适用于非常简单的文本,如链接、图片。

    例子豆瓣FacebookFlickr

    当然了,大部分这样的网站都不会要求你换行都用”<br />”来实现的,文本显示的时候,换行符会被自动转换成HTML的换行。有的网站还会给一些辅助,比如豆瓣的日记编辑器上方有几个按钮,可以帮助生成链接等HTML代码。WordPress的quick tag editor也是这样。

    对于开发者来说,几乎没有工作量,只需一个html textarea元素即可解决问题。

    然而对于用户的要求就比较高了,他们要想插入链接、图片就必须学一些简单的HTML语法。不过用户及时一点HTML都不懂也没关系,他仍然可以发布内容,只是没有链接、图片而已。像Flickr这样流行的服务,有无数的人为它开发帮助用户输入的浏览器扩展、Greasemonkey脚本等,更降低了输入HTML代码的难度。

    2. textarea里写自定义语法

    例子WikipediaGoogle Code,大部分论坛

    比较常见的语法有wiki markupMarkdown,以及各种论坛系统常用的BBCode等等。当然了,wiki markup语法的目的不仅仅是为了方便用户输入带格式的文本,它还方便了页面之间的互相链接,但是这里也把它归在这一类。在文本要在浏览器中显示的时候,系统将它翻译成HTML.

    用户不需要学习HTML了,但这些语法也需要学习,虽然看起来稍微简单点。和第一种方式一样,许多网站也提供辅助按钮。自定义语法的最大优点在于其安全性,稍后讨论。

    Flickr的评论里实际上除了支持一些基本的HTML代码,其实也有自定义输入链接的方式。

    3. WYSIWYG编辑器

    用户门槛很低,可以实现的效果很复杂。

    例子FCKeditorTinyMCENicEdit

    这是国内最常见的方式。国内网站大部分认为用户非常低端,而实际上的确如此。就拿已经够直观的FCKeditor来说,有些用户还是弄不明白插入图片时的一大堆属性都是什么东西,这是可以理解的,你自己使用以下就知道了。所以即使拿来现成的可视化编辑器,你还得好好简化简化。比如插入图片,就输入一个图片地址就够了。不过还真有用户连图片地址是什么都不知道的,那……实在不行就让他上传吧。

    使用这些编辑器的缺点是客户端需要启用JavaScript才可以正常使用。另外前两个编辑器非常庞大笨重,二位的目标是打败微软的Word产品,所以我们要考虑好用牛刀来杀鸡是不是合适。

    安全性考虑

    SQL注入的话题这里就不讨论了。

    允许用户提交HTML代码,就带来了潜在的危险。一方面是网站数据的安全,另一方面是页面显示的安全。前几天feelinglucky就披露了yupoo的一个漏洞,尽管这不是由于输入带格式文本造成的,但这是允许HTML代码提交的常见漏洞,危害到网站数据的安全。

    如果对用户提交的数据不加处理,还可能会造成显示错乱,比如数据中大量没有关门的标签。如果用户想恶作剧,还可以增加一个元素让它占据整个网页,隐藏所有内容。

    千万不要试图仅仅在客户端过滤代码,这就好像敌人要来进攻,你去人家门口堵着一样,敌人很容易绕过这道防线,从后门出来就可以了。服务器端的过滤是绝对必要的。

    自定义语法大都不使用尖括号,这给安全性带来了帮助。在服务器端,所有的尖括号均转换成HTML entity表示,然后做语法翻译,这是最简单的方式。

    上述一、三方式都是提交HTML代码,服务器端就必须至少过滤JavaScript代码,不能让它在客户端执行。另外还要分析代码的标签是否完整,某些标签是否允许,等等。