月度归档:2013年09月

手机html特殊写法打电话,短信,email;iPhone 和Android应用

下面的这篇文章主要是说,网页中的链接如何写,可以激活电话的功能。

例如,页面中展示的是一个电话号码,当用户在手机浏览器里面点击这个电话号码的时候,手机会弹出拨号的面板,或者是短信程序会启动等。

1. 打电话

在android的浏览器中,如果电话号码是 XXX-XXX-XXXX的型式的话,用户点击的时候,拨号面板会激活,但是如果不是这一特定的格式,那么拨号功能是不会启动的。其实可以通过链接的方式激活拨号面板。

(1) IPhone的写法

[phone_number] 就是电话号码了 <a href=”callto:[phone_number]”>phone_number</a>例子: <a href=”callto:12345678″>12345678</a>

(2) Android的写法

[phone_number] 就是电话号码了 <a href=”wtai://wp/mc;[phone_number]”>phone_number</a>例子: <a href=”wtai://wp/mc;12345678″>12345678</a>

在电话号码前面可以加上 + (加号)表示国际号码。如:

<a href=”wtai://wp/mc;+12345678″>+12345678</a>

 

2. 短信

如果是需要调用短信的接口,可以将链接写成下面的格式:

sms:<phone_number>[,<phone-number>]*[?body=<message_body>]

例如:

<a href=”sms:12345678″>给 12345678 发短信</a> <a href=”sms:12345678?body=hello”>给 12345678 发送内容为”hello”的短信</a> <a href=”sms:12345678,98765432?body=hello”>给12345678和98765432 发送内容为”hello”的短信</a>

3. Android Market

如果希望一个链接能够激活Android市场的功能,可以把链接写成:

<a href=”market://search?q=[query]”>Android Market link</a>

其中<query>就是搜索的内容,你应用的名称

例子:

<a href=”market://search?q=MyApp”>MyApp</a>

4. Ovi Store
这是诺基亚Nokia的一个应用市场。

<a href=”http://store.ovi.com/content/XXXXX”>MyApp</a>

XXXX就是你的应用的ID(application Id)。

 

5. Windows Marketplace

微软的应用市场

<a href=”http://marketplace.windowsphone.com/details.aspx?appId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”>MyApp</a>

其中 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 只的就是应用的ID

 

6. BlackBerry App World

黑莓的应用市场

<a href=”http://appworld.blackberry.com/webstore/content/XXXXX”>MyApp</a>

链接中的XXXX就是应用ID。下面这个是作者页面的URL

<a href=”http://appworld.blackberry.com/webstore/vendor/XXXX”>MyApp</a>

其中的XXXX是指作者的ID

 

7. 地图定位GPS

<a href=”geopoint:[经度],[纬度]”>我的位置</a>

例如:

<a href=”geopoint:100,23″>我的位置</a>

8. 聊天工具

(1) Yahoo Messager

<a href=”ymsgr:[动作]?[用户名]&m=[消息]”>Yahoo Messager</a>

[动作]有:addfriend, sendIM, call

例子:

<a href=”ymsgr:sendIM?my.account@yahoo.com”>给my.account@yahoo.com发消息</a>

(2) Windows Messager (MSN)

<a href=”msnim:[动作]?contact=[用户名]”>Windows Messager</a>

[动作]有:chat (聊天), add (添加成联系人), voice (语音), video (视频)
例子:

<a href=”msnim:chat?contact=my.account@hotmail.com”>MSN</a>

(3) Google Talk (GTalk)

<a href=”gtalk:[动作]?jid=[用户名]&from_jid=[自己的用户名]”>GTalk</a>

[动作]有:chat (聊天),call (语音)

例子:

<a href=”gtalk:chat?jid=your@gmail.com&from_jid=my@gmail.com”>GTalk</a>

(4) Skype

<a href=”skype:[用户名]?[动作]”>Skype</a>

[动作]有:chat, add, userinfo, voicemain

例子:

<a href=”skype:mySkypeId?chat”>Skype</a>

 

9. Mail 邮件

就和普通的html一样使用mailto

<a href=”mailto:nobody@wordpress.com”></a> <a href=”mailto:nobody@wordpress.com,no.one@wordpress.com”></a> <a href=”mailto:nobody@wordpress.com?subject=Testing”></a> <a href=”mailto:nobody@wordpress.com?subject=Testing mailto&cc=no.one@wrodpress.com”></a>

PHP算法

这里是用PHP写的几个基础算法,算法的重要性貌似对于PHP程序员不怎么重要,其实是非常重 要的,经典名句:算法+数据结构=程序。作为一名真正的高级PHP程序员,我认为应该熟悉C,如果你想成为真正的程序员,请好好学C,学好数据结构与算 法。这里仅仅只是几个基础算法,还有很多东东要学……

1、首先来画个菱形玩玩,很多人学C时在书上都画过,咱们用PHP画下,画了一半。

思路:多少行for一次,然后在里面空格和星号for一次。

<?php
for($i=0;$i<=3;$i++){
echo str_repeat(“&nbsp;”,3-$i);
echo str_repeat(“*”,$i*2+1);
echo ‘<br/>’;
}

2、冒泡排序,C里基础算法,从小到大对一组数排序。

思路:这题从小到大,第一轮排最小,第二轮排第二小,第三轮排第三小,依次类推……

<?php
$arr = array(1,3,5,32,756,2,6);
$len = count($arr);
for ($i=0;$i<$len-1;$i++){
for ($j=$i+1;$j<$len;$j++){
if($arr[$i]>$arr[$j]){//从小到大
$p = $arr[$i];
$arr[$i] = $arr[$j];
$arr[$j]= $p;
}
}
}
var_dump($arr);3、杨辉三角,用PHP写。

思路:每一行的第一位和最后一位是1,没有变化,中间是前排一位与左边一排的和,这种算法是用一个二维数组保存,另外有种算法用一维数组也可以实现,一行 一行的输出,有兴趣去写着玩下。

1
1   1
1   2   1
1   3   3   1
1   4   6   4   1
1   5  10  10   5   1

<?php
//每行的第一个和最后一个都为1,写了6行
for($i=0; $i<6; $i++) {
$a[$i][0]=1;
$a[$i][$i]=1;
}

//出除了第一位和最后一位的值,保存在数组中
for($i=2; $i<6; $i++) {
for($j=1; $j<$i; $j++) {
$a[$i][$j] = $a[$i-1][$j-1]+$a[$i-1][$j];
}
}

//打印
for($i=0; $i<6; $i++){
for($j=0; $j<=$i; $j++) {
echo $a[$i][$j].’&nbsp;’;
}
echo ‘<br/>’;
}

4、在一组数中,要求插入一个数,按其原来顺序插入,维护原来排序方式。

思路:找到比要插入数大的那个位置,替换,然后把后面的数后移一位。

<?php
$in = 2;
$arr = array(1,1,1,3,5,7);
$n = count($arr);
//如果要插入的数已经最大,直接打印
if($arr[$n-1] < $in) {
$arr[$n+1] = $in; print_r($arr);
}

for($i=0; $i<$n; $i++) {
//找出要插入的位置
if($arr[$i] >= $in){
$t1= $arr[$i];
$arr[$i] = $in;
//把后面的数据后移一位
for($j=$i+1; $j<$n+1; $j++) {
$t2 = $arr[$j];
$arr[$j] = $t1;
$t1 = $t2;
}
//打印
print_r($arr);
die;
}
}

5、对一组数进行排序(快速排序算法)。

思路:通过一趟排序分成两部分,然后递归对这两部分排序,最后合并。

<?php
function q($array) {
if (count($array) <= 1) {return $array;}
//以$key为界,分成两个子数组
$key = $array[0];
$l = array();
$r = array();

//分别进行递归排序,然后合成一个数组
for ($i=1; $i<count($array); $i++) {
if ($array[$i] <= $key) { $l[] = $array[$i]; }
else { $r[] = $array[$i]; }
}
$l = q($l);
$r = q($r);
return array_merge($l, array($key), $r);
}

$arr = array(1,2,44,3,4,33);
print_r( q($arr) );
6、在一个数组查找你所需元素(二分查找算法)。

思路:以数组中某个值为界,再递归进行查找,直到结束。

<?php
function find($array, $low, $high, $k){
if ($low <= $high){
$mid = intval(($low+$high)/2);
if ($array[$mid] == $k){
return $mid;
}elseif ($k < $array[$mid]){
return find($array, $low, $mid-1, $k);
}else{
return find($array, $mid+1, $high, $k);
}
}
die(‘Not have…’);
}

//test
$array = array(2,4,3,5);
$n = count($array);
$r = find($array,0,$n,5)

7、合并多个数组,不用array_merge(),题目来于论坛。

思路:遍历每个数组,重新组成一个新数组。

<?php
function t(){
$c = func_num_args()-1;
$a = func_get_args();
//print_r($a);
for($i=0; $i<=$c; $i++){
if(is_array($a[$i])){
for($j=0; $j<count($a[$i]); $j++){
$r[] = $a[$i][$j];
}
} else {
die(‘Not a array!’);
}
}

return $r;
}

//test
print_r(t(range(1,4),range(1,4),range(1,4)));
echo ‘<br/>’;
$a = array_merge(range(1,4),range(1,4),range(1,4));
print_r($a);

8、牛年求牛:有一母牛,到4岁可生育,每年一头,所生均是一样的母牛,到15岁绝育,不再能生,20岁死亡,问n年后有多少头牛。(来自论坛)

<?php
function t($n) {
static $num = 1
for($j=1; $j<=$n; $j++){
if($j>=4 && $j<15) {$num++;t($n-$j);}
if($j==20){$num–;}
}
return $num;
}

//test
echo t(8);

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

冒泡排序 (bubble sort) — O(n2)

$data = array(3,5,9,32,2,1,2,1,8,5);

dump($data);
BubbleSort($data);
dump($data);

function BubbleSort(& $arr)
{
$limit = count($arr);
for($i=1; $i<$limit; $i++)
{
for($p=$limit-1; $p>=$i; $p–)
{
if($arr[$p-1] > $arr[$p])
{
$temp = $arr[$p-1];
$arr[$p-1] = $arr[$p];
$arr[$p] = $temp;
}
}
}
}

function dump( $d )
{
echo ‘<pre>’;
print_r($d);
echo ‘</pre>’;
}

插入排序   (insertion sort)— O(n2)

$data = array(6,13,21,99,18,2,25,33,19,84);
$nums = count($data)-1;
dump( $data );
InsertionSort($data,$nums);
dump( $data );

function InsertionSort(& $arr,$n )
{
for( $i=1; $i<=$n; $i++ )
{
$tmp = $arr[$i];
for( $j = $i; $j>0 && $arr[$j-1]>$tmp; $j– )
{
$arr[$j] = $arr[$j-1];
}
$arr[$j] = $tmp;
}
}

function dump( $d )
{
echo ‘<pre>’;print_r($d);echo ‘</pre>’;
}

希 尔排序   (shell sort)— O(n log n)

$data = array(6,13,21,99,18,2,25,33,19,84);
$nums = count($data);
dump( $data );
ShellSort($data,$nums);
dump( $data );

function ShellSort(& $arr,$n )
{
for( $increment = intval($n/2); $increment > 0; $increment = intval($increment/2) )
{
for( $i=$increment; $i<$n; $i++ )
{
$tmp = $arr[$i];
for( $j = $i; $j>= $increment; $j -= $increment )
if( $tmp < $arr[ $j-$increment ] )
$arr[$j] = $arr[$j-$increment];
else
break;

$arr[$j] = $tmp;
}
}
}

function dump( $d )
{
echo ‘<pre>’;print_r($d);echo ‘</pre>’;
}

快 速排序   (quicksort)— O(n log n)

$data = array(6,13,21,99,18,2,25,33,19,84);
dump($data);
quicks($data,0,count($data)-1);
dump($data);

function dump( $data){
echo ‘<pre>’;print_r($data);echo ‘</pre>’;
}

function QuickSort(& $arr,$left,$right)
{
$l = $left;
$r = $right;

$pivot = intval(($r+$l)/2);
$p = $arr[$pivot];

do
{
while(($arr[$l] < $p) && ($l < $right))
$l++;
while(($arr[$r] > $p) && ($r > $left))
$r–;
if($l <= $r)
{
$temp = $arr[$l];
$arr[$l] = $arr[$r];
$arr[$r] = $temp;
$l++;
$r–;
}
}
while($l <= $r);

if($left < $r)
QuickSort(&$arr,$left,$r);
if($l < $right)
QuickSort(&$arr,$l,$right);
}
==============================================================================

冒泡排序:两两交换数值,最小的值在最左边,就如最轻的气泡在最上边。对整列数两两交换一次,最小的数在最左边,每次都能得一个在剩下的数中的最小 的数,“冒”出来的数组成一个有序区间,剩下的值组成一无序区间,且有序区间中每一元素值都比无序区间的小。

快速排序:基准数,左右二个数组,递归调用,合并。

插 入排序:排序区间分成二部分,左边有序,右边无序,从右区间取第一个元素插入左区间,若此元素比左边区间最右边的元素大,留在原处,若此元素比左 边区间最右边的元素小,则插在最右边元素的原位置,同时最右边元素右移一位,计算器减一,重新和前面的元素比较,直到前面的元素比要插入元素小为止,重复 上述步骤。

注意区间端点值的处理,及数组的第一个元素下标为0.

<?php

//PHP常用排序算法

function bubblesort ($array)

{

$n = count ($array);

for ($i = 0; $i < $n; $i++)

{

for ($j = $n – 2; $j >= $i; $j–) //[0,i-1] [i,n-1]

{

if ($array[$j] > $array[$j + 1])

{

$temp = $array[$j];

$array[$j] = $array[$j + 1];

$array [$j + 1] = $temp;

}

}

}

return $array;

}

$array = array (3,6,1,5,9,0,4,6,11);

print_r (bubblesort ($array));

echo ‘<hr>’;

function quicksort ($array)

{

$n = count ($array);

if ($n <= 1)

{

return $array;

}

$key = $array[‘0’];

$array_r = array ();

$array_l = array ();

for ($i = 1; $i < $n; $i++)

{

if ($array[$i] > $key)

{

$array_r[] = $array[$i];

}

else

{

$array_l[] = $array[$i];

}

}

$array_r = quicksort ($array_r);

$array_l = quicksort ($array_l);

$array = array_merge ($array_l, array($key), $array_r);

return $array;

}

print_r (quicksort ($array));

echo ‘<hr>’;

function insertsort ($array)

{

$n = count ($array);

for ($i = 1; $i < $n; $i++) //[0,i-1] [i,n]

{

$j = $i – 1;

$temp = $array[$i];

while ($array[$j] > $temp)

{

$array[$j + 1] = $array[$j];

$array[$j] = $temp;

$j–;

}

}

return $array;

}

print_r (insertsort ($array));

?>

 

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

 

<?php
/*
【插 入排序(一维数组)】
【基本思想】:每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素 全部插入完为止。
【示例】:
[初始关键字] [49] 38 65 97 76 13 27 49
J=2(38) [38 49] 65 97 76 13 27 49
J=3(65) [38 49 65] 97 76 13 27 49
J=4(97) [38 49 65 97] 76 13 27 49
J=5(76) [38 49 65 76 97] 13 27 49
J=6(13) [13 38 49 65 76 97] 27 49
J=7(27) [13 27 38 49 65 76 97] 49
J=8(49) [13 27 38 49 49 65 76 97]
*/

function insert_sort($arr){
$count = count($arr);
for($i=1; $i<$count; $i++){
$tmp = $arr[$i];
$j = $i – 1;
while($arr[$j] > $tmp){
$arr[$j+1] = $arr[$j];
$arr[$j] = $tmp;
$j–;
}
}
return $arr;
}

/*
【选择排序(一维数组)】
【基 本思想】:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
【示例】:
[初 始关键字] [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第 二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第 四趟排序后 13 27 38 49 [49 97 65 76]
第五趟排序后 13 27 38 49 49 [97 97 76]
第 六趟排序后 13 27 38 49 49 76 [76 97]
第七趟排序后 13 27 38 49 49 76 76 [ 97]
最 后排序结果 13 27 38 49 49 76 76 97
*/
function select_sort($arr){
$count = count($arr);
for($i=0; $i<$count; $i++){
$k = $i;
for($j=$i+1; $j<$count; $j++){
if ($arr[$k] > $arr[$j])
$k = $j;
}
if($k != $i){
$tmp = $arr[$i];
$arr[$i] = $arr[$k];
$arr[$k] = $tmp;
}
}
return $arr;
}

/*
【冒泡排序(一维数组) 】
【基本思想】:两两比较待排序数据元素的大小,发现两个数据元素的次序 相反时即进行交换,直到没有反序的数据元素为止。
【排序过程】:设想被排序的数组R[1..N]垂直竖立,将每个数据元素看作有重量的气泡,根据 轻气泡不能在重气泡之下的原则,
从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上”漂浮”,如此反复进行,直至最后任何两个气泡都是 轻者在上,重者在下为止。
【示例】:
49 13 13 13 13 13 13 13
38 49 27 27 27 27 27 27
65 38 49 38 38 38 38 38
97 65 38 49 49 49 49 49
76 97 65 49 49 49 49 49
13 76 97 65 65 65 65 65
27 27 76 97 76 76 76 76
49 49 49 76 97 97 97 97
*/
function bubble_sort($array){
$count = count($array);
if ($count <= 0) return false;

for($i=0; $i<$count; $i++){
for($j=$count-1; $j>$i; $j–){
if ($array[$j] < $array[$j-1]){
$tmp = $array[$j];
$array[$j] = $array[$j-1];
$array[$j-1] = $tmp;
}
}
}
return $array;
}

/*
【快速排序(一 维数组)】
【基本思想】:在当前无序区R[1..H]中任取一个数据元素作为比较的”基准”(不妨记为X),
用此基准将当前无序区划分为 左右两个较小的无序区:R[1..I-1]和R[I 1..H],且左边的无序子区中数据元素均小于等于基准元素,
右边的无序子区中数据元素均大 于等于基准元素,而基准X则位于最终排序的位置上,即R[1..I-1]≤X.Key≤R[I 1..H](1≤I≤H),
当R[1..I-1] 和R[I 1..H]均非空时,分别对它们进行上述的划分过程,直至所有无序子区中的数据元素均已排序为止。
【示例】:
初始关键字 [49 38 65 97 76 13 27 49]
第一次交换后 [27 38 65 97 76 13 49 49]
第二次交换后 [27 38 49 97 76 13 65 49]
J向左扫描,位置不变,第三次交换后 [27 38 13 97 76 49 65 49]
I向右扫描,位置不变,第四次交换后 [27 38 13 49 76 97 65 49]
J向左扫描 [27 38 13 49 76 97 65 49]
(一次划分过程)

初始关键字 [49 38 65 97 76 13 27 49]
一趟排序之后 [27 38 13] 49 [76 97 65 49]
二趟排序之后 [13] 27 [38] 49 [49 65]76 [97]
三趟排序之后 13 27 38 49 49 [65]76 97
最后的排序结果 13 27 38 49 49 65 76 97
各趟排序之后的状态
*/
function quick_sort($array){
if (count($array) <= 1) return $array;
$key = $array[0];
$left_arr = array();
$right_arr = array();
for ($i=1; $i<count($array); $i++){
if ($array[$i] <= $key)
$left_arr[] = $array[$i];
else
$right_arr[] = $array[$i];
}
$left_arr = quick_sort($left_arr);
$right_arr = quick_sort($right_arr);

return array_merge($left_arr, array($key), $right_arr);
}

/*打印数组全部内容*/
function display_arr($array){
$len = count($array);
for($i = 0; $i<$len; $i++){
echo $array[$i].’ ‘;
}
echo ‘<br />’;
}

/*
几种排序算法的比较和选择
1. 选取排序方法需要考虑的因素:
(1) 待排序的元素数目n;
(2) 元素本身信息量的大小;
(3) 关键字的结构及其分布情况;
(4) 语言工具的条件,辅助空间的大小等。
2. 小结:
(1) 若n较小(n <= 50),则可以采用直接插入排序或直接选择排序。由于直接插入排序所需的记录移动操作较直接选择排序多,因而当记录本身信息量较大时,用直接选择排序较 好。
(2) 若文件的初始状态已按关键字基本有序,则选用直接插入或冒泡排序为宜。
(3) 若n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序。 快速排序是目前基于比较的内部排序法中被认为是最好的方法。
(4) 在基于比较排序方法中,每次比较两个关键字的大小之后,仅仅出现两种可能的转移,因此可以用一棵二叉树来描述比较判定过程,由此可以证明:当文件的n个关 键字随机分布时,任何借助于”比较”的排序算法,至少需要O(nlog2n)的时间。
(5) 当记录本身信息量较大时,为避免耗费大量时间移动记录,可以用链表作为存储结构。
*/

/*排序测试*/

$a = array(’12’,’4′,’16’,’8′,’13’,’20’,’5′,’32’);

echo ‘The result of insert sort:’;
$insert_a = insert_sort($a);
display_arr($insert_a);

echo ‘The result of select sort:’;
$select_a = select_sort($a);
display_arr($select_a);

echo ‘The result of bubble sort:’;
$bubble_a = bubble_sort($a);
display_arr($bubble_a);

echo ‘The result of bubble sort:’;
$quick_a = quick_sort($a);
display_arr($quick_a);

?>

/**
* 排列组合
* 采用二进制方法进行组合的选择,如表示5选3时,只需有3位为1就可以了,所以可得到的组合是 01101 11100 00111 10011 01110等10种组合
*
* @param 需要排列的数组 $arr
* @param 最小个数 $min_size
* @return 满足条件的新数组组合
*/
function pl($arr,$size=5) {
$len = count($arr);
$max = pow(2,$len);
$min = pow(2,$size)-1;
$r_arr = array();
for ($i=$min; $i<$max; $i++){
$count = 0;
$t_arr = array();
for ($j=0; $j<$len; $j++){
$a = pow(2, $j);
$t = $i&$a;
if($t == $a){
$t_arr[] = $arr[$j];
$count++;
}
}
if($count == $size){
$r_arr[] = $t_arr;
}
}
return $r_arr;
}

 

$pl = pl(array(1,2,3,4,5,6,7),5);

var_dump($pl);
//递归算法
//阶乘
function f($n){
if($n == 1 || $n == 0){
return 1;
}else{
return $n*f($n-1);
}
}
echo f(5);
//遍历目录
function iteral($path){
$filearr = array();
foreach (glob($path.’\*’) as $file){
if(is_dir($file)){
$filearr = array_merge($filearr,iteral($file));
}else{
$filearr[] = $file;
}
}
return $filearr;
}
var_dump(iteral(‘d:\www\test’));

Web前端优化最佳实践及工具集锦

前端的性能对于一个Web应用来说非常重要,如果一个Web应用的页面加载速度非常快、对于用户的操作可以及时响应,那么产品的用户体验将会极大地提升。下图显示了页面加载速度对于用户体验的影响。

\

你的Web页面的速度是否已经足够快了?其实可能还有很多可以提升的地方。Google和雅虎也提出了一些Web应用的前端优化建议,并发布了一些工具,你可以逐一检验你的Web应用,以便达到更高的性能。

这些优化不仅仅可以给用户提供更好的体验,从开发者角度来说,进行优化还可以减少页面的请求数、降低请求所占的带宽、减少资源的浪费。

下面来看看Google和雅虎提供的Web页面优化最佳实践。

一、Google的Web优化最佳实践

1.  避免坏请求

有时页面中的HTML或CSS会向服务器请求一个不存在的资源,比如图片或HTML文件,这会造成浏览器与服务器之间过多的往返请求,类似于:

 

  • 浏览器:“我需要这个图像。”
  • 服务器:“我没有这个图像。”
  • 浏览器:“你确定吗?这个文档说你有。”
  • 服务器:“真的没有。”

 

如此一来,会降低页面的加载速度。因此,检查页面中的坏链接非常有必要,你可以通过Google的PageSpeed工具来检测,找到问题后,补充相应的资源文件或者修改资源的链接地址即可。

2.  避免CSS @import

使用@import方法引用CSS文件可以能会带来一些影响页面加载速度的问题,比如导致文件按顺序加载(一个加载完后才会加载另一个),而无法并行加载。

你可以使用CSS delivery工具来检测页面代码中是否存在@import方法。比如,如果检测结果中存在

1.@import url("style.css")

则建议你使用下面的代码来替代。

 

1.<link rel="style.css" href="style.css" type="text/css">

3.  避免使用document.write

在JavaScript中,可以使用document.write在网页上显示内容或调用外部资源,而通过此方法,浏览器必须采取一些多余的步骤——下载资源、读取资源、运行JavaScript来了解需要做什么,调用其他资源时需要重新再执行一次这个过程。由于浏览器之前不知道要显示什么,所以会降低页面加载的速度。

要知道,任何能够被document.write调用的资源,都可以通过HTML来调用,这样速度会更快。检查你的页面代码,如果存在类似于下面的代码:

1.document.write('<script src="another.js" _cke_saved_src="another.js"></script>');

 

建议修改为:

1.<script src="another.js"></script>

 

4.  合并多个外部CSS文件

在网站中每使用一个CSS文件,都会让你的页面加载速度慢一点点。如果你有一个以上的CSS文件,你应该将它们合并为一个文件。

你可以通过 CSS delivery工具来检测页面代码中的CSS文件,然后通过复制粘贴的方式将它们合并为一个。合并后记得修改页面中的引用代码,并删除旧的引用代码。

5.  合并多个外部JavaScript文件

大部分情况下,网站往往会包含若干个JavaScript文件,但并不需要将这些文件都独立出来,其中有些是可以合并为一个文件的。

你可以通过resource check工具来检测页面中所引用的JavaScript文件数,然后可以通过复制粘贴的方式将多个文件合并为一个。

6.  通过CSS sprites来整合图像

如果页面中有6个小图像,那么浏览器在显示时会分别下载。你可以通过CSS sprites将这些图像合并成1个,可以减少页面加载所需的时间。

CSS sprites需要有两个步骤:整合图像、定位图像。比如你可以通过下面的代码来分别定位下面图像中的上下两部分。

1..megaphone {width:50pxheight:50pxbackground:url(images/sprite.png) 0 0px;}
2..smile {width:50pxheight:50pxbackground:url(images/sprite.png) 0 -50px;}

7. 延迟JavaScript的加载

浏览器在执行JavaScript代码时会停止处理页面,当页面中有很多JavaScript文件或代码要加载时,将导致严重的延迟。尽管可以使用defer、异步或将JavaScript代码放到页面底部来延迟JavaScript的加载,但这些都不是一个好的解决方案。

下面是Google的建议。

 

01.<script type="text/javascript">
02.function downloadJSAtOnload() {
03.var element = document.createElement("script");
04.element.src = "defer.js";
05.document.body.appendChild(element);
06.}
07.if (window.addEventListener)
08.window.addEventListener("load", downloadJSAtOnload, false);
09.else if (window.attachEvent)
10.window.attachEvent("onload", downloadJSAtOnload);
11.else window.onload = downloadJSAtOnload;
12.</script>

这段代码的意思是等待页面加载完成后,然后再加载外部的“defer.js”文件。下面是测试结果。

8.  启用压缩/ GZIP

使用gzip对HTML和CSS文件进行压缩,通常可以节省大约50%到70%的大小,这样加载页面只需要更少的带宽和更少的时间。

你可以通过这个Gzip压缩工具来检测页面是否已经经过Gzip压缩。

9.  启用Keep-Alive

HTTP协议采用“请求-应答”模式,当使用普通模式(非KeepAlive模式)时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP协议为无连接的协议);当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。

在HTTP 1.0中Keep-Alive默认是关闭的,需要在HTTP头中加入“Connection: Keep-Alive”,才能启用Keep-Alive;在HTTP 1.1中Keep-Alive默认启用,加入“Connection: close”可关闭。目前大部分浏览器都是用HTTP 1.1协议,也就是说默认都会发起Keep-Alive的连接请求了,所以是否能完成一个完整的Keep- Alive连接就看Web服务器的设置情况。

10.  将小的CSS和JavaScript代码内嵌到HTML中

如果你的CSS代码比较小,可以将这部分代码放到HTML文件中,而不是一个外部CSS文件,这样可以减少页面加载所需的文件数,从而加快页面的加载。同样,也可以将小的JavaScript脚本代码内嵌到HTML文件中。

1.<style type="text/css">
2.<!--CSS代码-->
3.</style>
4.
5.<script type="text/javascript">
6.<!--JavaScript代码-->
7.</script>

11.  利用浏览器缓存

在显示页面时,浏览器需要加载logo、CSS文件和其他一些资源。浏览器缓存所做的工作就是“记住”已经加载的资源,让页面的加载速度更快。

12.  压缩CSS代码

不管你在页面中如何使用CSS,CSS文件都是越小越好,这会帮助你提升网页的加载速度。你可以通过Minify CSS工具来压缩你的CSS代码。

压缩前:

01.body
02.{
03.background-color:#d0e4fe;
04.}
05.h1
06.{
07.color:orange;
08.text-align:center;
09.}

 

压缩后:

1.body {background-color:#d0e4fe;}
2.h1 {color:orange;text-align:center;}

13.  尽量减少DNS查询次数

当浏览器与Web服务器建立连接时,它需要进行DNS解析,将域名解析为IP地址。然而,一旦客户端需要执行DNS lookup时,等待时间将会取决于域名服务器的有效响应的速度。

虽然所有的ISP的DNS服务器都能缓存域名和IP地址映射表,但如果缓存的DNS记录过期了而需要更新,则可能需要通过遍历多个DNS节点,有时候需要通过全球范围内来找到可信任的域名服务器。一旦域名服务器工作繁忙,请求解析时就需要排队,则进一步延迟等待时间。

因此,减少DNS的查询次数非常重要,页面加载时就尽量避免额外耗时。为了减少DNS查询次数,最好的解决方法就是在页面中减少不同的域名请求的机会。

你可以通过request checker工具来检测页面中存在多少请求,然后进行优化。

14.  尽量减少重定向

有时为了特定需求,需要在网页中使用重定向。重定向的意思是,用户的原始请求(例如请求A)被重定向到其他的请求(例如请求B)。

但是这会造成网站性能和速度下降,因为浏览器访问网址是一连串的过程,如果访问到一半而跳到新地址,就会重复发起一连串的过程,这将浪费很多的时间。所以我们要尽量避免重定向,Google建议:

 

  • 不要链接到一个包含重定向的页面
  • 不要请求包含重定向的资源

 

15.  优化样式表和脚本的顺序

Style标签和样式表调用代码应该放置在JavaScript代码的前面,这样可以使页面的加载速度加快。

 

01.<head>
02.<meta name=description content="description"/>
03.<title>title</title>
04.<style>
05.page specific css code goes here
06.</style>
07.<script type="text/javascript">
08.javascript code goes here
09.</script>
10.</head>

16.  避免JavaScripts阻塞渲染

浏览器在遇到一个引入外部JS文件的<script>标签时,会停下所有工作来下载并解析执行它,在这个过程中,页面渲染和用户交互完全被阻塞了。这时页面加载就会停止。

谷歌建议删除干扰页面中第一屏内容加载的JavaScript,第一屏是指用户在屏幕中最初看到的页面,无论是桌面浏览器、手机,还是平板电脑。

17.  缩小原始图像

如果无需在页面中显示较大的图像,那么就建议将图像的实际大小缩小为显示的大小,这样可以减少下载图像所需的时间。

18.  指定图像尺寸

当浏览器加载页面的HTML代码时,有时候需要在图片下载完成前就对页面布局进行定位。如果HTML里的图片没有指定尺寸(宽和高),或者代码描述的尺寸与实际图片的尺寸不符时,浏览器则要在图片下载完成后再“回溯”该图片并重新显示,这将消耗额外的时间)。

所以,最好为页面中的每一张图片都指定尺寸,不管是在HTML里的<img>标签中,还是在CSS中。

更多信息:https://developers.google.com/speed/docs/insights/rules

二、雅虎的Web优化最佳实践

1.  内容优化

 

  • 尽量减少HTTP请求:常见方法包括合并多个CSS文件和JavaScript文件,利用CSS Sprites整合图像,Image map(图像中不同的区域设置不同的链接),内联图象(使用 data: URL scheme 在实际的页面嵌入图像数据)等。
  • 减少DNS查找
  • 避免重定向
  • 使Ajax可缓存
  • 延迟加载组件:考虑哪些内容是页面呈现时所必需首先加载的、哪些内容和结构可以稍后再加载,根据这个优先级进行设定。
  • 预加载组件:预加载是在浏览器空闲时请求将来可能会用到的页面内容(如图像、样式表和脚本)。当用户要访问下一个页面时,页面中的内容大部分已经加载到缓存中了,因此可以大大改善访问速度。
  • 减少DOM元素数量:页面中存在大量DOM 元素,会导致JavaScript遍历DOM的效率变慢。
  • 根据域名划分页面内容:把页面内容划分成若干部分可以使你最大限度地实现平行下载。但要确保你使用的域名数量在2个到4个之间(否则与第2条冲突)。
  • 最小化iframe的数量:iframes 提供了一个简单的方式把一个网站的内容嵌入到另一个网站中。但其创建速度比其他包括JavaScript和CSS的DOM元素的创建慢了1-2个数量级。
  • 避免404:HTTP请求时间消耗是很大的,因此使用HTTP请求来获得一个没有用处的响应(例如404没有找到页面)是完全没有必要的,它只会降低用户体验而不会有一点好处。

 

2. 服务器优化

 

  • 使用内容分发网络(CDN):把你的网站内容分散到多个、处于不同地域位置的服务器上可以加快下载速度。
  • 添加Expires或Cache-Control信息头:对于静态内容,可设置文件头过期时间Expires的值为“Never expire(永不过期)”;对于动态内容,可使用恰当的Cache-Control文件头来帮助浏览器进行有条件的请求。
  • Gzip压缩
  • 设置ETag:ETags(Entity tags,实体标签)是web服务器和浏览器用于判断浏览器缓存中的内容和服务器中的原始内容是否匹配的一种机制。
  • 提前刷新缓冲区:当用户请求一个页面时,服务器会花费200到500毫秒用于后台组织HTML文件。在这期间,浏览器会一直空闲等待数据返回。在PHP中,可以使用flush()方法,它允许你把已经编译的好的部分HTML响应文件先发送给浏览器,这时浏览器就会可以下载文件中的内容(脚本等)而后台同时处理剩余的HTML页面。
  • 对Ajax请求使用GET方法:当使用XMLHttpRequest时,浏览器中的POST方法会首先发送文件头,然后才发送数据。因此使用GET最为恰当。
  • 避免空的图像src

 

3. Cookie优化

 

  • 减小cookie大小:去除不必要的coockie,并使coockie体积尽量小以减少对用户响应的影响
  • 针对Web组件使用域名无关的Cookie:对静态组件的Cookie读取是一种浪费,使用另一个无Cookie的域名来存放静态组件是一个好方法,或者也可以在Cookie中只存放带www的域名。

 

4. CSS优化

 

 

5. JavaScript优化

 

 

6. 图像优化

 

 

7. 针对移动优化

 

更多信息:http://developer.yahoo.com/performance/rules.html中文翻译

 

三、一些工具

1.  Google PageSpeed

Google提供了PageSpeed工具,这是一个浏览器插件,可以很好地应用上文中Google所提到的Web优化实践——帮助你轻松对网站的性能瓶颈进行分析,并为你提供优化建议。

 

 

2.  雅虎 YSlow

YSlow是雅虎推出的一款浏览器插件,可以帮助你对网站的页面进行分析,并为你提供一些优化建议,以提高网站的性能。

 

 

3. 其他分析优化工具

 

 

通过以上的优化建议和优化工具,可以轻松找到影响你的Web页面性能的瓶颈,轻松实现Web页面性能的提升。如果你也有Web优化方面的经验,欢迎分享。

Mozilla开源TogetherJS:让网站拥有实时协作功能

TogetherJS是由Mozilla打造的一款可以给网站添加实时协作功能的JavaScript库,TogetherJS免费并且开源,遵循MPL 2.0开源协议,并且托管在Mozilla服务器上。

为什么要使用TogetherJS?

 

  • 共同创建:TogetherJS可以让用户在网站上实现实时协作;
  • 针对Web构建:TogetherJS 基于JavaScript开发,无需安装软件或插件即可安装使用,对现有页面非常友好,开发者也可以根据自己需求,对其进行自定义添加。

 

有哪些内置功能?

 

  • 支持语音或视频聊天:TogetherJS采用Web RTC技术,强调用户交流的重要性;
  • 文本聊天:用户也可以通过即时消息与朋友进行沟通;
  • 用户焦点:用户还可以看到彼此的鼠标动态;
  • 共同浏览:用户可以在同一个域里互相跟随对方去浏览不同的页面;
  • TogetherJS让用户可以实时看到对方;
  • 支持内容实时同步。

小试一把

1.把下面这段JavaScript代码拷贝到源码中:

2.添加HTML button

 

把上面那段代码拷贝到源码后,再拷贝下面这段代码到网站中:

 

<button onclick="TogetherJS(this); return false;">Start TogetherJS</button>

官网:https://togetherjs.com/

文档及教程:https://togetherjs.com/docs/

托管地址:https://github.com/mozilla/togetherjs

此外,该库也在HN上引起了大家的广泛关注与讨论,欢迎大家与我们分享使用心得与体会。

6款移动网站开发的 jQuery 图片滑块插件

 随着智能手机的普及,越来越多的用户喜欢通过手机中浏览网页。今天这篇文章为大家推荐最佳6款用于移动应用的 jQuery 图片滑块插件,这些插件很好的处理了触屏事件,效果平滑,帮助你构建用户体验良好的移动网站。

PhotoSwipe

PhotoSwipe 是一款免费的 jQuery 图片库插件,支持 iPhone, iPad, Android, Blackberry 等各种移动设备。

Mobile Photo Album

Mobile Photo Album 是一款非常易用的相册插件,帮助你在移动网站中构建优秀的相册功能。

Unoslider

Responsive jQuery Slider Plugins

特别推荐 unoslider 这款幻灯片插件,基于响应式设计,拥有众多切换效果,支持触屏。

Touch Gallery

Touch Gallery 帮助你在移动浏览器中实现类似原生的照片浏览应用的效果。

swipejs

Swipe 是一款轻量的,用于移动网站开发的 jQuery 图片滑块插件,简单易用。

TN3 Gallery

压轴的这款 jQuery 相册插件是这里面功能最丰富的,支持幻灯片展示、不同的布局方式以及多样的切换效果。

PHP正则表达式验证是否含有中文研究长记录

凌乱的记录。。。。。


if (preg_match("/[\x7f-\xff]/", $string)) {
echo "true";
}else{
echo "false";
}
//"/^([\x81-\xfe][\x40-\xfe])+$/"
///[\x7f-\xff]/

判断是否全部是中文

//if (preg_match("/^[".chr(0xa1)."-".chr(0xff)."]+$/", $str)) { //只能在GB2312情况下使用
if (preg_match("/^[\x7f-\xff]+$/", $str)) { //兼容gb2312,utf-8
echo "true";
} else {
echo "false";
}

判断中文和编码有关 gbk是双字节,utf8是三字节,可以根据中文的范围来判断
编码范围1. GBK (GB2312/GB18030)
\x00-\xff GBK双字节编码范围
\x20-\x7f ASCII
\xa1-\xff 中文
\x80-\xff 中文
2. UTF-8 (Unicode)
\u4e00-\u9fa5 (中文)
\x3130-\x318F (韩文
\xAC00-\xD7A3 (韩文)
\u0800-\u4e00 (日文)
ps: 韩文是大于[\u9fa5]的字符
正则例子:
preg_replace(”/([\x80-\xff])/”,””,$str);
preg_replace(”/([u4e00-u9fa5])/”,””,$str);

mysql模式之sql_mode思考

mysql可以运行在不同sql mode模式下面,sql mode模式定义了mysql应该支持的sql语法,数据校验等!

查看默认的sql mode模式:
select @@sql_mode;
我的数据库是:
STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
在此模式下面,如果插入的数据的长度大于定义的长度,那么就会报错!

set session sql_mode=’REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI’;
在这种模式下面:插入的数据的长度大于定义的时候,就会截取,并警告,但是可以插入进去
session表示只在本次中有效
global:表示在本次连接中不生效,而对于新的连接就生效

启用NO_BACKSLASH_ESCAPES模式,使反斜线成为普通字符,在导入数据时候,如果数据中有反斜线,启用这个模式是个不错的选择

启用PIPES_AS_CNCAT模式,将||看成是普通字符串

常用的sql mode:
sql mode值  说明
ANSI  ‘REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE和ANSI组合’,这种模式使语法和行为更符合标准的sql
STRICT_TRANS_TABLES  使用与事务和非事务表,严格模式
TRADITIONAL  也是严格模式,对于插入不正确的值给出错误而不是警告。用在事务时,只要发生错误就立即回滚
1、看机器配置,指三大件:cpu、内存、硬盘
2、看mysql配置参数
3、查系mysql行状态,可以用mysqlreport工具来查看
4、查看mysql的慢查询
依次解决了以上问题之后,再来查找程序方面的问题

my.cnf缓存优化
在 my.cnf 中添加/修改以下选项:

#取消文件系统的外部锁
skip-locking

#不进行域名反解析,注意由此带来的权限/授权问题
skip-name-resolve
#索引缓存,根据内存大小而定,如果是独立的db服务器,可以设置高达80%的内存总量
key_buffer = 512M

#连接排队列表总数
back_log = 200
max_allowed_packet = 2M

#打开表缓存总数,可以避免频繁的打开数据表产生的开销
table_cache = 512

#每个线程排序所需的缓冲
sort_buffer_size = 4M

#每个线程读取索引所需的缓冲
read_buffer_size = 4M

#MyISAM表发生变化时重新排序所需的缓冲
myisam_sort_buffer_size = 64M

#缓存可重用的线程数
thread_cache = 128

#查询结果缓存
query_cache_size = 128M

#设置超时时间,能避免长连接
set-variable = wait_timeout=60

#最大并发线程数,cpu数量*2
thread_concurrency = 4

#记录慢查询,然后对慢查询一一优化
log-slow-queries = slow.log
long_query_time = 1

#关闭不需要的表类型,如果你需要,就不要加上这个
skip-innodb
skip-bdb

MariaDB 5.5 在 Windows 下的性能测试

我们有很长时间没有发布过在 Windows 下的基准测试文章了,而 Windows 下的 MariaDB 包含一些专门做的改进,这些改进很多人并不知晓,因为我们自己也很少提及。本文的目的是向你展示 MariaDB 在 Windows 下的性能表现。

进行测试的机器包含2个CPU共8核的处理器(这是我手头上能找到最好的机器了)、10K SAS 磁盘(RAID1),使用 sysbench 0.4 测试单表共100万行记录。我通过网络来运行这个压力测试,并发的客户端从 4 到 4096。

这里是 OLTP-只读 吞吐量测试结果:

说明:

  • 绝大多数的测试结果表明,MariaDB 的吞吐量比 MySQL 高出 10% 左右
  • 在 4096 并发客户端时,MariaDB 的吞吐量是 MySQL 5.5 的 四倍多 476% (2382 vs 413 TPS).

很多人理所当然的认为吞吐量并不能代表数据库的整体性能表现。在 OLTP 的基准测试中,响应时间同样很重要,实际上它比吞吐量更加重要,这点我同意,因此,下面是查询的响应时间,意味着 95% 的事务都在指定的时间内处理完毕。

OLTP 只读响应时间

并发数 4 8 16 32 64 128 256 512 1024 2048 4096
MariaDB 4.87 6.81 8.83 12.35 22.12 43.56 90.35 180.57 619.05 1003.88 1965.77
MySQL 4.86 7.14 9.96 16.21 37.39 101.33 238.89 499.63 971.07 2241.83 25215.29

 

上表中显示,MariaDB 5.5 不管是在吞吐量还是响应时间方面都是优于 MySQL 的。

但是,为什么 MariaDB 在 Windows 下的只读测试由于 MySQL 5.5 呢?二者基于同一个代码,表现应该也相同啊。这个问题的答案并不是 MariaDB 做了什么优化,也无关 XtraDB 和 InnoDB 的优劣。答案是 MariaDB threadpool. 这个线程池在 Windows 平台是默认启用的。

可是,为什么使用线程池就可以有如此好的性能呢?答案是 MariaDB 承担了通过调整线程池的大小并回调到对应的 Windows 本身的线程池,这在操作系统这一级别上相当于黑盒排序,因此能获取良好的性能。关键在于 Windows 内置的线程池,受益于 IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型。这是 Windows 专有的特性,运行在其上的服务器应该使用这种技术。要让这项技术运行良好的招数是:

  • 不要让同一时间在同一个 CPU 上运行太多的线程,这样可减少上下文切换,这是提高吞吐量的最重要的因素
  • 在完成的 LIFO 顺序中激活线程等待,热门的线程保持热门,可降低缓存失效
  • 顺序处理 IO 完成,这是响应时间表现良好的因素
  • 最后便是降低热锁的争用

由此,线程池是只读性能表现佳的主要因素。

下一个有趣的问题是在写操作上 MariaDB 表现是否一致。因此我们使用写模式来运行 sysbench 工具,也就是 update_non_index (每个查询对一个非索引的整数字段进行加值处理)。为了最大化写的吞吐量,我们设置了参数 innodb_flush_log_at_trx_commit 值为 0,每次日志的写入是每秒一次,而不是每次事务提交一次。

测试结果如下:

OLTP write-only (update_non_index/flush_log=0) 吞吐量:

这个结果看起来很棒,差别来源于多个因素,包括 XtraDB 的写性能、分组提交、线程池等都对这个结果会有影响。但我想 Windows 平台下的 MariaDB 的 asynchronous IO optimization (异步 IO 优化) 是最主要的因素。

在上述测试中,所有 IO 相关的参数和 InnoDB 参数都使用的是默认值,结果看起来太好了以至于让我们怀疑这是真的。我真的想通过调整为 innodb_io_capacity and/or innodb_write_io_threads 参数为 MySQL 带来更加的性能,有人知道该如何调整吗?

OLTP writeonly (update_non_index/flush_log=0) 响应时间, 95 percentile

并发数 4 8 16 32 64 128 256 512 1024 2048
MariaDB 0.33 0.63 0.75 1.06 1.94 3.85 8.25 21.38 129.79 274.40
MySQL 0.32 0.61 0.73 1.61 7.62 26.82 96.45 219.29 661.19 2723.36

 

[mysqld]
sql-mode="NO_ENGINE_SUBSTITUTION"
back_log=500
user=root
port=3306
max_connections=4096
max_connect_errors=5000
max_prepared_stmt_count=50000
table_cache=2048
transaction_isolation=REPEATABLE-READ
loose-skip-external-locking
innodb_status_file=0
innodb_data_file_path=ibdata1:200M:autoextend
innodb_buffer_pool_size=1G
innodb_additional_mem_pool_size=20M
innodb_log_file_size=650M
innodb_log_buffer_size=100M
innodb_support_xa=0
innodb_doublewrite=0
innodb_flush_log_at_trx_commit=0
query-cache-size=0
query-cache-type=0
symbolic-links=0

skip-grant-tables

红薯翻译:http://www.oschina.net/question/12_58908

mysql大数据表性能测试

机器
CPU:12核  Intel(R) Xeon(R) CPU E5-2620 0 @ 2.00GHz
内存:64G
硬盘:1.4T

mysql
表名:pre_userinfo
类型:MyISAM
行数:104,677,320
字段数:205
大小:22.7 GB

无where的select查询:

执行SQL:SELECT * FROM `pre_userinfo`LIMIT 0 , 30
消耗时间:0.094 sec


执行SQL:SELECT * FROM `pre_userinfo`LIMIT 100 , 30
消耗时间:0.078 sec


执行SQL:SELECT * FROM `pre_userinfo`LIMIT 1000 , 30
消耗时间:0.172 sec

执行SQL:SELECT * FROM `pre_userinfo`LIMIT 5000 , 30
消耗时间:0.156 sec

执行SQL:SELECT * FROM `pre_userinfo`LIMIT 10000 , 30
消耗时间:0.219 sec

执行SQL:SELECT * FROM `pre_userinfo`LIMIT 50000 , 30
消耗时间:0.375 sec

执行SQL:SELECT * FROM `pre_userinfo`LIMIT 100000 , 30
消耗时间:0.656 sec

执行SQL:SELECT * FROM `pre_userinfo`LIMIT 200000 , 30
消耗时间:0.938 sec

执行SQL:SELECT * FROM `pre_userinfo`LIMIT 500000 , 30
消耗时间:2.016 sec

执行SQL:SELECT * FROM `pre_userinfo`LIMIT 1000000 , 30
消耗时间:3.922 sec

根据主键查询的select:

执行SQL:SELECT *FROM `pre_userinfo`WHERE `id`='100'
消耗时间:0.094 sec

执行SQL:SELECT *FROM `pre_userinfo`WHERE `id`='1000'
消耗时间:0.094 sec

执行SQL:SELECT *FROM `pre_userinfo`WHERE `id`='5000'
消耗时间:0.078 sec

执行SQL:SELECT *FROM `pre_userinfo`WHERE `id`='10000'
消耗时间:0.109 sec

执行SQL:SELECT *FROM `pre_userinfo`WHERE `id`='500000'
消耗时间:0.156 sec

执行SQL:SELECT *FROM `pre_userinfo`WHERE `id`='1000000'
消耗时间:0.109 sec

执行SQL:SELECT *FROM `pre_userinfo`WHERE `id`='50000000'
消耗时间:0.125 sec

执行SQL:SELECT *FROM `pre_userinfo`WHERE `id`='500000000'
消耗时间:0.093 sec

执行SQL:SELECT *FROM `pre_userinfo`WHERE `id`='1000000000'
消耗时间:0.093 sec
 

JavaScript与有限状态机

有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物。

简单说,它有三个特征:

  * 状态总数(state)是有限的。
* 任一时刻,只处在一种状态之中。
* 某种条件下,会从一种状态转变(transition)到另一种状态。

它对JavaScript的意义在于,很多对象可以写成有限状态机。

举例来说,网页上有一个菜单元素。鼠标悬停的时候,菜单显示;鼠标移开的时候,菜单隐藏。如果使用有限状态机描述,就是这个菜单只有两种状态(显示和隐藏),鼠标会引发状态转变。

代码可以写成下面这样:


  var menu = {

    // 当前状态
    currentState: 'hide',

    // 绑定事件
    initialize: function() {
      var self = this;
      self.on("hover", self.transition);
    },

    // 状态转换
    transition: function(event){
      switch(this.currentState) {
        case "hide":
          this.currentState = 'show';
          doSomething();
          break;
        case "show":
          this.currentState = 'hide';
          doSomething();
          break;
        default:
          console.log('Invalid State!');
          break;
      }
    }

  };

可以看到,有限状态机的写法,逻辑清晰,表达力强,有利于封装事件。一个对象的状态越多、发生的事件越多,就越适合采用有限状态机的写法。

另外,JavaScript语言是一种异步操作特别多的语言,常用的解决方法是指定回调函数,但这样会造成代码结构混乱、难以测试和除错等问题。有限状态机提供了更好的办法:把异步操作与对象的状态改变挂钩,当异步操作结束的时候,发生相应的状态改变,由此再触发其他操作。这要比回调函数、事件监听、发布/订阅等解决方案,在逻辑上更合理,更易于降低代码的复杂度。

下面介绍一个有限状态机的函数库Javascript Finite State Machine。这个库非常好懂,可以帮助我们加深理解,而且功能一点都不弱。

该库提供一个全局对象StateMachine,使用该对象的create方法,可以生成有限状态机的实例。


  var fsm = StateMachine.create();

生成的时候,需要提供一个参数对象,用来描述实例的性质。比如,交通信号灯(红绿灯)可以这样描述:


  var fsm = StateMachine.create({

    initial: 'green',

    events: [
      { name: 'warn',  from: 'green',  to: 'yellow' },
      { name: 'stop', from: 'yellow', to: 'red' },
      { name: 'ready',  from: 'red',    to: 'yellow' },
      { name: 'go', from: 'yellow', to: 'green' }
    ]

  });

交通信号灯的初始状态(initial)为green,events属性是触发状态改变的各种事件,比如warn事件使得green状态变成yellow状态,stop事件使得yellow状态变成red状态等等。

生成实例以后,就可以随时查询当前状态。

* fsm.current :返回当前状态。
* fsm.is(s) :返回一个布尔值,表示状态s是否为当前状态。
* fsm.can(e) :返回一个布尔值,表示事件e是否能在当前状态触发。
* fsm.cannot(e) :返回一个布尔值,表示事件e是否不能在当前状态触发。

Javascript Finite State Machine允许为每个事件指定两个回调函数,以warn事件为例:

* onbeforewarn:在warn事件发生之前触发。
* onafterwarn(可简写成onwarn) :在warn事件发生之后触发。

同时,它也允许为每个状态指定两个回调函数,以green状态为例:

* onleavegreen :在离开green状态时触发。
* onentergreen(可简写成ongreen) :在进入green状态时触发。

假定warn事件使得状态从green变为yellow,上面四类回调函数的发生顺序如下:onbeforewarn → onleavegreen → onenteryellow → onafterwarn。

除了为每个事件和状态单独指定回调函数,还可以为所有的事件和状态指定通用的回调函数。

* onbeforeevent :任一事件发生之前触发。
* onleavestate :离开任一状态时触发。
* onenterstate :进入任一状态时触发。
* onafterevent :任一事件结束后触发。

如果事件的回调函数里面有异步操作(比如与服务器进行Ajax通信),这时我们可能希望等到异步操作结束,再发生状态改变。这就要用到transition方法。


  fsm.onleavegreen = function(){
    light.fadeOut('slow', function() {
      fsm.transition();
    });
    return StateMachine.ASYNC;
  };

上面代码的回调函数里面,有一个异步操作(light.fadeOut)。如果不希望状态立即改变,就要让回调函数返回StateMachine.ASYNC,表示状态暂时不改变;等到异步操作结束,再调用transition方法,使得状态发生改变。

Javascript Finite State Machine还允许指定错误处理函数,当发生了当前状态不可能发生的事件时自动触发。


  var fsm = StateMachine.create({
    // ...
    error: function(eventName, from, to, args, errorCode, errorMessage) {
      return 'event ' + eventName + ': ' + errorMessage;
    },
    // ... 
  });

比如,当前状态是green,理论上这时只可能发生warn事件。要是这时发生了stop事件,就会触发上面的错误处理函数。

Javascript Finite State Machine的基本用法就是上面这些,更详细的介绍可以参见它的主页

(完)