jQuery最佳实践


上周,我整理了《jQuery设计思想》

那篇文章是一篇入门教程,从设计思想的角度,讲解怎么使用jQuery”。今天的文章则是更进一步,讲解如何用好jQuery”

我主要参考了Addy Osmani的PPT《提高jQuery性能的诀窍》(jQuery Proven Performance Tips And Tricks)。他是jQuery开发团队的成员,具有一定的权威性,提出的结论都有测试数据支持,非常有价值。

==============================================

jQuery最佳实践

阮一峰 整理

clip_image001

1. 使用最新版本的jQuery

jQuery的版本更新很快,你应该总是使用最新的版本。因为新版本会改进性能,还有很多新功能。

下面就来看看,不同版本的jQuery性能差异有多大。这里是三条最常见的jQuery选择语句:

$(‘.elem’)

$(‘.elem’, context)

context.find(‘.elem’)

我们用1.4.2、1.4.4、1.6.2三个版本的jQuery测试,看看浏览器在1秒内能够执行多少次。结果如下:

clip_image002可以看到,1.6.2版本的运行次数,远远超过两个老版本。尤其是第一条语句,性能有数倍的提高。

其他语句的测试,比如.attr(“value”).val(),也是新版本的jQuery表现好于老版本。

2. 用对选择器

在jQuery中,你可以用多种选择器,选择同一个网页元素。每种选择器的性能是不一样的,你应该了解它们的性能差异。

(1)最快的选择器:id选择器和元素标签选择器

举例来说,下面的语句性能最佳:

$(‘#id’)

$(‘form’)

$(‘input’)

遇到这些选择器的时候,jQuery内部会自动调用浏览器的原生方法(比如getElementById()),所以它们的执行速度快。

(2)较慢的选择器:class选择器

$(‘.className’)的性能,取决于不同的浏览器。

Firefox、Safari、Chrome、Opera浏览器,都有原生方法getElementByClassName(),所以速度并不慢。但是,IE5-IE8都没有部署这个方法,所以这个选择器在IE中会相当慢。

(3)最慢的选择器:伪类选择器和属性选择器

先来看例子。找出网页中所有的隐藏元素,就要用到伪类选择器:

$(‘:hidden’)

属性选择器的例子则是:

$(‘[attribute=value]’)

这两种语句是最慢的,因为浏览器没有针对它们的原生方法。但是,一些浏览器的新版本,增加了querySelector()和querySelectorAll()方法,因此会使这类选择器的性能有大幅提高。

最后是不同选择器的性能比较图

clip_image003可以看到,ID选择器遥遥领先,然后是标签选择器,第三是Class选择器,其他选择器都非常慢。

3. 理解子元素和父元素的关系

下面六个选择器,都是从父元素中选择子元素。你知道哪个速度最快,哪个速度最慢吗?

$(‘.child’, $parent)

$parent.find(‘.child’)

$parent.children(‘.child’)

$(‘#parent > .child’)

$(‘#parent .child’)

$(‘.child’, $(‘#parent’))

我们一句句来看。

(1) $(‘.child’, $parent)

这条语句的意思是,给定一个DOM对象,然后从中选择一个子元素。jQuery会自动把这条语句转成$.parent.find(‘child’),这会导致一定的性能损失。它比最快的形式慢了5%-10%。

(2) $parent.find(‘.child’)

这条是最快的语句。.find()方法会调用浏览器的原生方法(getElementById,getElementByName,getElementByTagName等等),所以速度较快。

(3) $parent.children(‘.child’)

这条语句在jQuery内部,会使用$.sibling()和javascript的nextSibling()方法,一个个遍历节点。它比最快的形式大约慢50%。

(4) $(‘#parent > .child’)

jQuery内部使用Sizzle引擎,处理各种选择器。Sizzle引擎的选择顺序是从右到左,所以这条语句是先选.child,然后再一个个过滤出父元素#parent,这导致它比最快的形式大约慢70%。

(5) $(‘#parent .child’)

这条语句与上一条是同样的情况。但是,上一条只选择直接的子元素,这一条可以于选择多级子元素,所以它的速度更慢,大概比最快的形式慢了77%。

(6) $(‘.child’, $(‘#parent’))

jQuery内部会将这条语句转成$(‘#parent’).find(‘.child’),比最快的形式慢了23%。

所以,最佳选择是$parent.find(‘.child’)。而且,由于$parent往往在前面的操作已经生成,jQuery会进行缓存,所以进一步加快了执行速度。

具体的例子和比较结果,请看这里

4. 不要过度使用jQuery

jQuery速度再快,也无法与原生的javascript方法相比。所以有原生方法可以使用的场合,尽量避免使用jQuery。

请看下面的例子,为a元素绑定一个处理点击事件的函数:

$(‘a’).click(function(){

alert($(this).attr(‘id’));

});

这段代码的意思是,点击a元素后,弹出该元素的id属性。为了获取这个属性,必须连续两次调用jQuery,第一次是$(this),第二次是attr(‘id’)。

事实上,这种处理完全不必要。更正确的写法是,直接采用javascript原生方法,调用this.id:

$(‘a’).click(function(){

alert(this.id);

});

根据测试,this.id的速度比$(this).attr(‘id’)快了20多倍。

5. 做好缓存

选中某一个网页元素,是开销很大的步骤。所以,使用选择器的次数应该越少越好,并且尽可能缓存选中的结果,便于以后反复使用。

比如,下面这样的写法就是糟糕的写法:

jQuery(‘#top’).find(‘p.classA’);

jQuery(‘#top’).find(‘p.classB’);

更好的写法是:

var cached = jQuery(‘#top’);

cached.find(‘p.classA’);

cached.find(‘p.classB’);

根据测试,缓存比不缓存,快了2-3倍。

6. 使用链式写法

jQuery的一大特点,就是允许使用链式写法。

$(‘div’).find(‘h3’).eq(2).html(‘Hello’);

采用链式写法时,jQuery自动缓存每一步的结果,因此比非链式写法要快。根据测试,链式写法比(不使用缓存的)非链式写法,大约快了25%。

7. 事件的委托处理(Event Delegation)

javascript的事件模型,采用”冒泡”模式,也就是说,子元素的事件会逐级向上”冒泡”,成为父元素的事件。

利用这一点,可以大大简化事件的绑定。比如,有一个表格(table元素),里面有100个格子(td元素),现在要求在每个格子上面绑定一个点击事件(click),请问是否需要将下面的命令执行100次?

$(“td”).bind(“click”, function(){

$(this).toggleClass(“click”);

});

回答是不需要,我们只要把这个事件绑定在table元素上面就可以了,因为td元素发生点击事件之后,这个事件会”冒泡”到父元素table上面,从而被监听到。

因此,这个事件只需要在父元素绑定1次即可,而不需要在子元素上绑定100次,从而大大提高性能。这就叫事件的”委托处理”,也就是子元素”委托”父元素处理这个事件。

具体的写法有两种。第一种是采用.delegate()方法:

$(“table”).delegate(“td”, “click”, function(){

$(this).toggleClass(“click”);

});

第二种是采用.live()方法:

$(“table”).each(function(){

$(“td”, this).live(“click”, function(){

$(this).toggleClass(“click”); 
}); 
});

这两种写法基本等价。唯一的区别在于,.delegate()是当事件冒泡到指定的父元素时触发,.live()则是当事件冒泡到文档的根元素后触发,因此.delegate()比.live()稍快一点。此外,这两种方法相比传统的.bind()方法还有一个好处,那就是对动态插入的元素也有效,.bind()只对已经存在的DOM元素有效,对动态插入的元素无效。

根据测试,委托处理比不委托处理,快了几十倍。在委托处理的情况下,.delegate()又比.live()大约快26%。

8. 少改动DOM结构

(1)改动DOM结构开销很大,因此不要频繁使用.append()、.insertBefore()和.insetAfter()这样的方法。

如果要插入多个元素,就先把它们合并,然后再一次性插入。根据测试,合并插入比不合并插入,快了将近10倍。

(2)如果你要对一个DOM元素进行大量处理,应该先用.detach()方法,把这个元素从DOM中取出来,处理完毕以后,再重新插回文档。根据测试,使用.detach()方法比不使用时,快了60%。

(3)如果你要在DOM元素上储存数据,不要写成下面这样:

var elem = $(‘#elem’);

elem.data(key,value);

而要写成

var elem = $(‘#elem’);

$.data(elem,key,value);

根据测试, 后一种写法要比前一种写法,快了将近10倍。因为elem.data()方法是定义在jQuery函数的prototype对象上面的, 而$.data()方法是定义jQuery函数上面的,调用的时候不从复杂的jQuery对象上调用,所以速度快得多。(此处可以参阅下面第10点。)

9. 正确处理循环

循环总是一种比较耗时的操作,如果可以使用复杂的选择器直接选中元素,就不要使用循环,去一个个辨认元素。

javascript原生循环方法for和while,要比jQuery的.each()方法,应该优先使用原生方法。

10. 尽量少生成jQuery对象

每当你使用一次选择器(比如$(‘#id’)),就会生成一个jQuery对象。jQuery对象是一个很庞大的对象,带有很多属性和方法,会占用不少资源。所以,尽量少生成jQuery对象。

举例来说,许多jQuery方法都有两个版本,一个是供jQuery对象使用的版本,另一个是供jQuery函数使用的版本。下面两个例子,都是取出一个元素的文本,使用的都是text()方法。你既可以使用针对jQuery对象的版本:

var $text = $(“#text”);

var $ts = $text.text();

也可以使用针对jQuery函数的版本:

var $text = $(“#text”);

var $ts = $.text($text);

由于后一种针对jQuery函数的版本不通过jQuery对象操作,所以相对开销较小,速度比较快

【经典问题】现代操作系统经典问题回顾(哲学家就餐问题C#实现)

 在1971年,著名的计算机科学家艾兹格·迪科斯彻提出了一个同步问题,即假设有五台计算机都试图访问五份共享的磁带驱动器。稍后,这个问题被托尼·霍尔重新表述为哲学家就餐问题。这个问题可以用来解释死锁和资源耗尽。

哲学家就餐问题描述:五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很光滑,所以需要两把叉子才能夹住,相邻两个盘子之间放有一把叉子。哲学家的生活中有两种交替活动时段:即吃饭和思考(一种抽象而已)。当一个哲学家觉得饿了时,他就试图分两次去取其左边和右边的叉子,每次拿到一把,但不分次序。如果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。问题是为哲学家写一段描述其行为的程序,且决不会死锁。

源码:

哲学家就餐问题

// ************************************************************
// 在1971年,著名的计算机科学家艾兹格·迪科斯彻提出了一个同步问题,
// 即假设有五台计算机都试图访问五份共享的磁带驱动器。稍后,这个问
// 题被托尼·霍尔重新表述为哲学家就餐问题。这个问题可以用来解释死
// 锁和资源耗尽。
// 
// 哲学家就餐问题描述:五个哲学家围坐在一张圆桌周围,每个哲学家面
// 前都有一盘通心粉。由于通心粉很光滑,所以需要两把叉子才能夹住,
// 相邻两个盘子之间放有一把叉子。哲学家的生活中有两种交替活动时段:
// 即吃饭和思考(一种抽象而已)。当一个哲学家觉得饿了时,他就试图
// 分两次去取其左边和右边的叉子,每次拿到一把,但不分次序。如果成功
// 地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。问题是为
// 哲学家写一段描述其行为的程序,且决不会死锁。
// 
// ************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace DiningPhilosophers
{
    
/// <summary>
    
/// 哲学家就餐问题是现代操作系统中很经典的问题。
    
/// 一个哲学家的封装。
    
/// </summary>
    public class Philosopher : WorkerThread
    {
        
public const int READY = 0;
        
public const int EATING = 1;
        
public const int THINKING = 2;
        
public const int FINISHED = 3;

        
public Philosopher(object data) : base(data) { }
        
public delegate void StateSwitchedHandler(Object sender, StateSwitchedEventArgs args);
        
public event StateSwitchedHandler StateSwitch;

        
protected override void Run()
        {
            PhilosopherData pd 
= (PhilosopherData)Data;
            Ran
dom rnd 
= new Random(pd.PhilosopherId);
            StateSwitch(
thisnew StateSwitchedEventArgs(READY, pd));
            WaitHandle[] forks 
= new WaitHandle[] { pd.LeftFork, pd.RightFork };

            
while (pd.TotalFood > 0)
            {
                
// 如果两边的哲学家有拿着叉子的,则等待。 
                WaitHandle.WaitAll(forks);
                
// 否则,开始吃通心粉。 
                StateSwitch(thisnew StateSwitchedEventArgs(EATING, pd));
                
// 饭吃掉一部分移除。 
                pd.TotalFood -= pd.AmountToEat;
                Thread.Sleep(rnd.Next(
1001000));// 模拟正在吃通心粉

                StateSwitch(
thisnew StateSwitchedEventArgs(THINKING, pd));
                
// 放下左边和右边的叉子开始思考。 
                pd.RightFork.ReleaseMutex();
                pd.LeftFork.ReleaseMutex();

                Thread.Sleep(rnd.Next(
1001000));// 模拟正在思考
            }

            
// 至此,通信粉都吃完了。 
            StateSwitch(thisnew StateSwitchedEventArgs(FINISHED, pd));
        }
    }

    
/// <summary>
    
/// 哲学家相当于一个线程,这是工作线程抽象类,作为对线程操作的封装。
    
/// </summary>
    public abstract class WorkerThread
    {
        
private object _threadData;
        
private Thread _rawThread;

        
public object Data
        {
            
get { return _threadData; }
            
set { _threadData = value; }
        }

        
public WorkerThread(object data)
        {
            _threadData 
= data;
        }

        
public WorkerThread()
        {
     &
nbsp;      _threadData 
= null;
        }

        
public void Start()
        {
            _rawThread 
= new Thread(new ThreadStart(this.Run));
            _rawThread.Start();
        }

        
protected abstract void Run();
    }

    
/// <summary>
    
/// 封装哲学家数据的结构。
    
/// </summary>
    public struct PhilosopherData
    {
        
public int PhilosopherId;   // 代表哲学家
        public Mutex RightFork;     // 代表右边的叉子
        public Mutex LeftFork;      // 代表左边的叉子
        public int AmountToEat;     // 吃掉的通心粉
        public int TotalFood;       // 盘中的总的通心粉
    }

    
/// <summary>
    
/// 事件:用来通知外部现在哲学家的状态。
    
/// </summary>
    public class StateSwitchedEventArgs : EventArgs
    {
        
public int type;
        
public PhilosopherData philosopherData;
        
public StateSwitchedEventArgs(int t, PhilosopherData pd)
        {
            type 
= t;
            philosopherData 
= pd;
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace DiningPhilosophers
{
    
class Program
    {
        
static void Main(string[] args)
    
    {
            
// 准备数据
            Mutex[] forks = new Mutex[N];
            
for (int i = 0; i < N; i++)
            {
                forks[i] 
= new Mutex(false);// 开始时叉子放在桌子上,没有哲学家拿到。
            }

            Philosopher[] p 
= new Philosopher[N];
            
for (int i = 0; i < N; i++)
            {
                PhilosopherData pd;
                pd.PhilosopherId 
= i;
                pd.RightFork 
= forks[(i + 1% N];// 右边的叉子编号
                pd.LeftFork = forks[(i + N  1% N];// 左边的叉子编号
                pd.AmountToEat = N_EAT;// 暂时大锅饭,大家都一样。
                pd.TotalFood = N_TOTAL;// 暂时大锅饭,大家都一样。
                p[i] = new Philosopher(pd);
                p[i].StateSwitch 
+=
                    
new Philosopher.StateSwitchedHandler(
                        (sender, data) 
=>
                        {
                            Console.WriteLine(
Philosopher:{0} IS {1}.,
                                data.philosopherData.PhilosopherId,
                                TranslateState(data.type));
                        });
            }

            
for (int i = 0; i < N; i++)
                p[i].Start();

            Console.ReadKey();
        }

        
private static string TranslateState(int state)
        {
            
switch (state)
            {
                
case Philosopher.READY:
                    
return READY;
                
case Philosopher.EATING:
                    
return EATING;
                
case Philosopher.THINKING:
                    
return THINKING;
                
case Philosopher.FINISHED:
                    
return FINISHED;
                
default:
                    
throw new Exception(*NOT* FOUND.);
            }
        }

        
public const int N = 5;// 哲学家或叉子数目。
        public const int N_EAT = 2;// 每次吃掉的通心粉数量。
        public const int N_TOTAL = 20;// 盘中通心粉总数。
    }
}

 

运行结果:

Philosopher:0 IS READY.
Philosopher:0 IS EATING.
Philosopher:1 IS READY.
Philosopher:1 IS EATING.
Philosopher:3 IS READY.
Philosopher:2 IS READY.
Philosopher:4 IS READY.
Philosopher:1 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:0 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:4 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:1 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:0 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:0 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:0 IS EATING.
Philosopher:0 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:3 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:2 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:0 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:0 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:3 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:2 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:0 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:0 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:1 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:4 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:0 IS EATING.
Philosopher:0 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:0 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:0 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:0 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:0 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:0 IS EATING.
Philosopher:1 IS FINISHED.
Philosopher:4 IS THINKING.
Philosopher:0 IS THINKING.
Philosopher:3 IS EATING.Philosopher:2 IS EATING.
Philosopher:4 IS FINISHED.
Philosopher:2 IS THINKING.
Philosopher:3 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:3 IS FINISHED.
Philosopher:2 IS THINKING.
Philosopher:0 IS EATING.
Philosopher:0 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:0 IS FINISHED.
Philosopher:2 IS THINKING.
Philosopher:2 IS FINISHED.

 

源码下载

10个既绚又实用的jQuery特效教程

导读:作者Aamir Baigdesignersdare.com分享了一篇文章《10 Must See jQuery Latest Tutorials-Beginners and Advance Level》,文中对初学者和资深学者分别提供了不同级别的教程,使用JQuery做出的既炫丽又实用的效果,现将本文摘译,以下是文章内容:

此篇文章是JQuery高级教程,但对初学者学习JQuery也有一定的帮助。

首先让我们先对初学者谈谈如何开始学习JQuery。对于初学者学习JQuery来说是一个很不错的简短教程。

初学者教程:

让我们开始为初学者讲解JQuery的初步知识。在本教程中,您将学到如何下载JQuery库、添加项目、开发简单的程序、加载页面警告消息弹出窗口。

第一步:下载JQuery库

首先点击链接http://code.jquery.com/jquery-1.4.2.min.js下载JQuery库,保存在本地文件中。

第二步:在网页中添加JQuery库

在页面中添加Query库代码,代码如下:

  1. 		<script type=”text/javascript” src=”jquery-1.4.2.min.js”></script> 

第三步:添加警告窗口JQuery代码

在页面中添加页面上加载警告窗口代码,代码如下:

  1. 		<Script language=”JavaScript”> 
  2.  
  3. $(document).ready(function() { 
  4.  
  5. alert(“jQuery tutorial for beginners Example”); 
  6.  
  7. }); 
  8.  
  9. </Script> 

$(document).ready(function() {}函数调用已经准备就绪。在这个函数中添加了JavaScript函数显示警告信息。这个是一个非常简单的例子,添加后在屏幕上提示信息“jQuery tutorial for beginners Example(JQuery初学者示例教程)”。

点击查看示例:http://designersdare.com/jquery-tutorial/jquery-tutorial.html

中级和高级用户使用的JQuery教程

1.JQuery随机背景滚动效果教程

 

使用JQuery做出的动画背景,这是一个简单但很实用的简短教程。点击查看示例

2.JQuery制作聚光灯教程

聚光灯效果随着鼠标的移动而突显图像。点击查看示例

3.JQuery下拉菜单效果教程

简单实用、易于定制的下拉菜单。点击查看示例

4.JQuery教程设计

在此处列出教程中有价值的标题:

如何寻找元素?

1.简单的幻灯片模板

2.简单的消失效果

3.过渡效果

4.可折叠标签

5.动画悬停效果

6.全部模块可点击

7.可折叠模板

8.类似WordPress的后台评论

9.图片画廊

10.不同样式的链接类型

5.如何添加图片放大镜效果

非常专业、有趣的教程,创建属于自己的放大图片效果。

6.跨浏览器blockquote使用CSS和JQuery

网页设计师们经常使用Blockqoutes在自己的作品中。本教程可以教您如何在页面上引用<blockquote>标签。

7.处理大量元素的控件提示

有很多的JQuery控件提示工具,其中有一些是非常实用的,供大家参考。

8.JQuery创建分页系统

本教程提供如何为您的网站创建一个分页系统。点击查看示例

9.JQuery的AJAX标签

在网页上自由创建您自己想要的标签。使您的网页脚本更加快速、安全。

10.漂亮的滑动导航:CSS、JQuery教程

我将告诉您如何创建出滑动效果的菜单和导航,当您的鼠标移到导航区域,导航自动滑出,鼠标离开导航自动隐藏。点击查看示例

jQuery移动版Beta 1发布:更快 更兼容 可扩展

在2010年10月21日,CSDN发文《jQuery移动版发布:支持iOS,黑莓和Android平台》,对jQuery移动版有比较全面的介绍。时隔近12周,jQuery移动移动版Beta 1终于在2011年6月20日发布了!Beta 1比原始版本更快,更强的可扩展性能更好的支持动态JS驱动的网站,兼容性更好,增加更广泛的设备支持(例如智能手机、平板电脑等)。

jQuery 用户界面兼jQuery移动版用户界面总设计师Todd Parker称,jQuery 移动版的Beta 2版本差不多在下个月发布。

要注意的一点就是jQuery Mobile 1.0要求装备jQuery core 1.6,未来将会支持两个最新版本的core。

原文链接:http://jquerymobile.com/blog/2011/06/20/jquery-mobile-beta-1-released/

分享12款优秀的在线文件格式转换工具

在线的文件转换工具有很多,但是要找到一款好用的却不容易,所以今天这篇文章收集了12款优秀的在线文件格式转换工具,看看有没有适合您的需要的工具。

iWebPrint

打印网页为PDF文件,可以定制打印尺寸、方向和模式,可作为Chrome和Firefox插件使用。

i2OCR

从图片((TIF, JPEG, PNG, BMP, GIF, PBM, PGM, PPM))中提取文本,支持中文等33种语言。

PDF to Flash Page Flip

这款在线工具可以帮助你把PDF转换成类似电子杂志的Flash文件,读起来更加互动和有趣。

FontConverter

使用这款在线字体格式转换工具,你可以转换各种常用的字体文件。

Free Online OCR

这款免费工具可以帮助你轻松的把传真、截图和照片中的文字准换成可编辑和可检索的文本。

Free online file converter

这款免费的在线转换工具可以让你把音频、视频、图片等多媒体文件从一种格式轻松的转换成另一种格式。

Picasion

Picasion 是一款在线生成GIF动画图片的工具,可以从本地上传图片,也可以填写网址。

Brickify

Brickify 是一款很好的工具,可以帮你把图片转换成特别的砖砌模式。

RoboVoice

RoboVoice 是一个文本阅读服务,帮助你听网页,竟然支持中文,太不可以思议了。

CMYK Converter

CMYK Converter 是一款小工具,让你的图片色彩模式在RGB 和 CMYK 之间相互转换。

Neevia Document Converter

在线文档格式转换器,支持很多很多种文件格式。

PDF to JPG

如果你想把PDF文件转换成JPG文件,试试这款工具吧。

原文出自:博客园

创业公司如何招聘优秀工程师

创业公司面对的现实问题是难以招到优秀的工程师。和大公司相比,创业公司的待遇低、压力大、稳定性差,而且既符合职位条件又有创业意愿的人很难寻觅,还需要时机合适,我就遇见过几次候选人因为家事而放弃机会的情况。

现在我每天花1/3的时间在开发工作,1/3的时间在团队事务和沟通,1/3的时间在招揽各种优秀的人才上。

如何找到优质的候选人

在自己的同事和朋友中找。我们团队一位工程师曾经获得招聘达人称号,他本人分享的获胜秘诀是:每天温习通讯录,给新朋旧友推荐机会,平均每天电话量达300个。他号称是专职的猎头、兼职的工程师!

利用在业界的影响力。如果你在业界有一定的影响力和号召力,要好好利用。专业的权威性是最为牢固的领导力。或者试着做一款优秀的应用,进入开发者圈子,你会在里面找到很多合适的候选人。

多认识业界的朋友,让他们帮忙推荐。利用聚会或者人脉认识业界的朋友,让他们帮忙推荐合适的人选。并且经常向他们询问朋友里是否有合适的人选,遇到好的人选是需要时机的,如果你不主动询问他们,重要的信息就可能会错过。参加一些技术沙龙也是很好的选择,多和周围的人交流,他们可能就是你的潜在候选者。

利用新型互联网应用。考虑微博、类似LinkedIn的商务社交网络、类似Quora的SNS问答社区等应用。它们都具有媒体或者社交网络属性,能达到精准和自传播的效果。

如何说服候选人

作为创业团队的领导者,说服力非常重要,这是需要时间和经历来磨炼的。创业能为创业者提供锻炼的机会,能提供大公司所不具备的独特经历,能更大地实现自身价值,能提供更积极向上的工作环境。如果再加上成型的产品、稳定的核心团队、靠谱的商业模式以及对人才的重视态度,是很有可能成功说服对方的。

在说服候选人的过程中,可能会遇到的主要困难包括以下几个方面。

薪酬待遇低。为优秀人才提供适当股权激励是比较合适的手段,另外可以让他们负责重要的产品或业务,许多人很重视这样独当一面的机会。

创业决心不坚决。很多时候候选人的创业决心可能并不坚决。因为我在创业公司和大公司都有过工作经历,所以对各自的优劣和环境都有了解,面对不同的候选人,我可以根据实际情况有针对性地来说服他们。

周围环境影响。候选人可能会遇到一些周围环境的阻力。我在招聘一个优秀工程师加入团队时,正巧遇上他的妻子怀孕,他需要每天早点回家照顾。我立即提出他可以每天提早回家,照顾好妻子后在家工作。

机会众多。优秀的候选人可能同时面临很多机会,所以一定要快速跟进。我曾经在了解一位候选人的背景后,立即联系见面,沟通了解,达成共识,直到最后敲定,所用时间没有超过12小时。

如何留住优秀人才

团队气氛。创业团队的工作很辛苦,要保持快乐的工作状态,团队气氛就一定要积极、平等、开放、向上,不能很压抑。

感情维系。大公司靠文化、中型公司靠制度、小公司靠感情。创业团队必须要靠感情来维系,所以我们经常组织团队活动和拓展来加强成员之间的沟通和了解。

持续激励。一定要给予团队里重要的和做出贡献的人以激励,股权也需要按照多劳多得的原则来进行分配。

统一的理想。我们现在有来自腾讯、微软、百度等大公司的优秀人才,团队非常稳定。因为我们有成熟的产品、众多用户的肯定以及统一的理想,所以大家都愿意齐心协力,一起将共同的事业进行下去。

甄别和吸引优秀人才的加入,这是每个创业团队的领导者永远需要关注的重点话题。

(本文选自《程序员》杂志11年06期,更多精彩内容敬请关注06期杂志)

URL的设计

导读:URL的设计是一个很复杂的问题,不能说有什么“正确”的解决方案——其挺类似于其他方面的设计的,有好的URL设计,有糟糕的URL设计,在这两者之间的情况也不同——它是主观的。不过这并不意味着不存在用于创建出非常好的URL的最佳做法。原文作者kneath总结了这些年来学到的一些URL设计的最佳做法,希望能够给你留下深刻的印象。

以下是文章内容:

你应该花一些时间来设计一下你的URL地址结构。在读完本文之后,如果有一件事情是我希望你记住的话,那就是花一些时间来设计你的URL地址的结构。不要把它留给你的框架来决定,不要听天由命,依赖运气。要仔细地考虑,认真摸索出一种经验。

URL的设计是一个很复杂的问题,我不能说有什么“正确”的解决方案——其挺类似于其他方面的设计的,有好的URL设计,有糟糕的URL设计,在这两者之间的情况也个个不同——它是主观的。

不过这并不意味着不存在用于创建出非常好的URL的最佳做法。我希望我这些年来学到的一些URL设计的最佳做法能够给你留下深刻的印象,并且我会解释为什么我认为使用新的HTML5 javascript的history API来工作是一件很令人兴奋的事情。

为什么需要对你的URL进行一番设计

URL栏已经成为了现代浏览器的一个主要吸引人的地方了,且它再也不仅是一个URL栏那么简单——你可以输入部分的URL,然后浏览器就像是会使用黑魔法似的召唤出了你正要查找的确切的完整地址。当我在我的URL栏中输入了resque issues时,得到的第一个结果是https://github.com/defunkt/resque/issues。

URL是全球统一的,它们可用在Chrome、Safari、Internet Explorer、cURL、wget、你的iPhone、Android上,甚至会被写在便签上。它们就是web网络的一种全球通用的语法。但是不要把这看成是理所当然的。

任何一个定期访问你的网站的半技术化的用户都应该能够基于内存中的URL结构来浏览你的应用的90%部分。为了能够实现这一点,你的URL必需是要注重实用性的,就几乎仿佛它们就是数学方程式一样——许多简单的规则组合成一种策略性的方式,以此来获得他们想要的页面。

顶层的部分是最为重要的

URL最有价值的方面在于其顶层的部分。在我看来,在想法形成了之后,这就是接下来的任何启动都应该最先要讨论的事情,要远在任何的技术讨论之前,要远在任何的代码编写之前。这一顶层部分将会改变形成你的网站功能的基础。

我是不是有些夸张了?看起来可能会是这样——但是以后会有1,000,000 个用户,想想它会带来多大的影响。想一下Facebook推出用户名是多么重大的一件事。可用的URL就像是不动产,而顶层的部分就是体现在外面的最好的资产。

另一个快速提示——每当你构建一个新的站点时,考虑一下这一组不实用的URL的黑名单列表(或许可从Quora的URL中了解到一点糟糕的URL设计)

命名空间是一种很棒的扩展URL的工具

命名空间可以作为一种很棒的建立实用的URL结构的方式,这种结构在后续的使用中很容易被记住。我在这里说的命名空间指的是什么?我的意思是,URL中指明了不同内容的那部分。一个例子:

https://github.com/defunkt/resque/issues

在上面的URL中,defunkt/resque 就是命名空间。为什么这会有用?这是因为在这一个URL之后的任何部分都突然变成了一个新的顶层部分,因此你可以去到任何的一个《user》/《repo》 , 然后加上/issues或者可能是/wiki,取得相同的页面,但是是在不同的命名空间下。

保持命名空间的清晰,不要一开始就把一些内容放在/feature/《user》/《repo》下,一些放在/《user》/《repo》/feature下。对于命名空间来说,要发挥效用就必须是统一的。

查询串是很棒的过滤和排序的手段

关于查询串web有着一个混乱的过去,我见过各式各样的事情,从每个网页都使用同一个URL加上不同的查询参数的网站,到一个查询串参数都不用的网站,各种情况都有。

我喜欢把查询串想象成URL的旋钮——其调整你的当前视图,把它按照你的喜好来进行微调,这就是为什么它们用在排序和过滤这些行为上会如此之棒。坚持一种统一的模式(比如说sort=alpha&dir=desc ),你就会把通过URL栏进行的排序和过滤变得简单易记。

关于查询串还有最后一件事情:在没有附加查询串的情况下,页面应该是有效的,其可能给出的是一个不同的页面,但没有查询串的URL应该是要呈现出页面的。

英文网站的非ASCII URL是很糟糕的

这个世界是一个复杂的地方,充满着¿ümlåts?, ¡êñyés!和各种令人畏惧的字符☄。这些字符在任何英文网站的URL中都是不会有一席之地的。使用英文的键盘输入这些字符很复杂,很多时候延展成浏览器中的一些混乱的字符(有在url中见过xn--n3h吗?这是一个☃)。

URL是为人设计的——而非为搜索引擎设计的

我是在这一行业中成长起来,学会了如何玩搜索引擎(好吧,就是Google)的把戏,以此来从我的联盟营销中赚钱。因此关键词堆砌URL的做法对我来说并不陌生。像下面这样来来结束一个URL的情况相当常见:

http://guitars.example.com/best-guitars/cheap-guitars/popular-guitar

就SEO的目的来说,使用这种URL的效果会很好,幸运的是,2003年Google的飓风式的更新消除了这类URL的任何排名优势。遗憾的是,专业的SEO行业被强取豪夺给围绕着,因此其可能还会建议你使用许多你尽可能想得到的关键字来堆砌你的URL

记住另外的一些要点:

  • 1. 下划线只有一个糟字可言,坚持使用破折号。
  • 2. 使用短的、完整的并且是大家都知道的单词。如果某个部分中有一个破折号或是一个特殊的字符的话,这个词就有可能太长。

URL是提供给人用的,为使用的人设计它们。

URL是一种协议

URL是一种协议,在一个可预见的位置尽可能长久地供应某些东西。一旦你的首个访问者点击了URL,那么你就隐式地进入了这样的一种协议中,即如果他们记住了来过该页面或是点击了刷新按钮话,那么他要看到相同的东西。

在已经向公众推出之后就不要再改变你的URL,如果你绝对有必要改变你的URL的话,加上重定向——这不那么会引起惊慌。

任何事物都应该有一个URL

在一个理想的环境中,你的网站上的任何一个单独的屏幕显示都应该得出一个URL,这一URL可被拷贝和粘贴来在另一个选项卡或是浏览器中再次产生相同的屏幕内容。公平地说,这并不是完全有可能的。除非是新近使用了一些新的HTML5浏览器的history Javascript API。值得注意的是,有两个新的方法:

onReplaceState—该方法代替了浏览器历史中的当前URL,并让后退(back)按钮不受影响。

onPushState–该方法把一个新的URL压入到浏览器的历史中,代替URL栏中的URL,并把它加入到浏览器的历史栈中(影响到后退按钮)。

何时使用onReplaceState 以及何时使用onPushState

这些新方法允许我们改变URL栏中的整个路径,而不仅是锚元素。随着这一新的强大功能而来的是一种新的设计责任——我们需要摸索出后退按钮的使用经验。

为了确定使用哪一个方法,问你自己这样的一个问题:这一行为产生了新的内容呢?抑或是相同内容的不同显示?

1.产生了新的内容——你应该使用onPushState(例如:分页链接)

2.产生了相同内容的不同显示——你应该使用onReplaceState(例如:排序和过滤)

使用你自己的判断,不过这两个规则应该会符合你80%的情况。考虑一下,当你点击后退按钮时,你希望看见什么,然后做到你所希望的。

链接的行为就应该像一个链接

诸如《a》和《button》之类的链接元素有着许多很棒的内建功能。如果你中键点击或是命令点击它们的话,它们会打开一个新的窗口。当你悬停在其之上时,你的浏览器会在状态栏中告诉你它的URL地址。在用到onReplaceState和onPushState时,不要破坏了这一行为。

1.把AJAX请求的位置嵌放在锚元素的href属性中。

2.在人们中键点击或是命令点击它们时,从Javascript的点击处理程序中返回true值。

这是一个相当简单的做法,在你的单击处理程序内部使用一个快速的条件判断。下面是一个jQuery兼容的例子片段:

代码:

  1. 		$(‘a.ajaxylink’).click(function(e){  
  2. // API 浏览器不支持history API的后备  
  3. if (!(‘replaceState’ in window.history)) return true  
  4.  
  5. // 确保是中键的、控制的和命令的正常点击行为  
  6. if (e.which == 2 || e.metaKey || e.ctrlKey){  
  7. return true  
  8. }  
  9.  
  10. // 做一些很棒的事情,然后改变URL  
  11. window.history.replaceState(null, “New Title”, ‘/some/cool/url’)  
  12. return false  
  13. }) 

特定于POST行为的URL需要废除

在过去,开发社区很爱创建一些不能被再次使用的URL,我喜欢把它们称为特定于POST行为(POST-specific)的URL——这是一些会在你提交了一个表单之后出现在你的地址栏中的URL,但是当你尝试着拷贝和粘贴这些url到新的选项卡中时,你就会得到一个错误的地址。

这类UR完全没有存在的借口,特定于Post行为的URL是用于重定向和API的——而非给最终用户的。

一个很棒的例子

1.用户生成的URL部分只用ASCII字符(defunkt、resque)。

2.“pull”是“pull request”的简短版本——单个单词,很容易关联到来源词。

3.拉请求(pull request)号局限的范围为defunkt/resque (此处是从1开始)。

4.锚指向一个滚动位置,内容不会被挡住。

知识点精萃:URL还有许多不同的格式——找出patch和diff的版本看看。

一个时代的开始

我希望随着新的Javascript API的使用的增多,设计者和开发者会花一些时间来设计一下URL。这对于任何网站的可用性来说都是一个很重要的部分,但我却见到太多忽略了这一点的 URL了。尽管重新设计网站的外观和感觉很容易,但重新设计URL的结构却要难得多。

但我也很激动,这些年来我有观察到URL的改变。有时是硬链接被牺牲在了AJAX这一祭坛上,有时是牺牲性能来为用户生成真实的URL。最终我们会来到这样的一个时间点上,到那时,我们既可以得到部分页面渲染的性能和可用性优势,同时又获得设计有条理的和精炼的URL的经验。

译文出自:译言网

原文出自:URL Design