Category Archives: 业界杂谈

Hello World 业界杂谈 他山石

[转]来自浏览器创造者的web优化技巧

Jatinder Mann是微软Internet Explorer产品的一名项目经理,在BUILD 2012大会上,他做了题为“提高HTML5应用和网站性能的50条秘技(50 performance tricks to make your HTML5 apps and sites faster)”的演讲,介绍了很多为Web应用提速的技巧。

Mann的建议是按照下面六个原则组织的。

1. 快速响应网络请求

  • 避免重定向。排名前1000的网站中,63%使用了重定向。如果不执行重定向的话,页面速度可以提高10%。
  • 避免Meta-refresh。世界上14%的URL使用了Meta-refresh。
  • 尽可能通过CDN定位用户,使服务器响应时间最小化。
  • 从不同的域下载资源,使并发连接的应用最大化。
  • 复用连接。不要在响应请求时关闭连接。
  • 确保页面加载不会因合作伙伴网站提供的数据而延迟。
  • 了解耗时的网络组件,如重定向、缓存、DNS、请求和响应等。在IE 9和10中可以使用Navigation Timing API来测量浏览器花在每个操作上的时间。

2. 最小化下载的字节数

  • 加载页面时,要尽量减少下载的数据量。平均而言,每个页面要下载的数据量达777KB,其中有474KB的图片、128KB的脚本和84KB的Flash。
  • 请求gzip压缩的内容。
  • 将资源保存在本地的包中,比如为Windows商店应用生成的包资源索引(Package Resource Index)文件。这样当需要这些资源时就可以很容易地获取到。
  • 使用HTML5 App Cache缓存动态资源。App Cache会只下载一次资源,从而避免多次网络行程。当应用的版本号发生变化时,它会自动重新下载相应资源。
  • 尽量在响应中使用“Expires”字段来提供可缓存的内容。
  • 通过设定请求的If-Modified-Since字段来使用条件请求。
  • 缓存数据请求,如HTTP、XML和JSON等,因为大约95-96%的请求不会整天变化。虽然这个想法很合理,但实际缓存接收到的请求的网站所占比重还不到1%。
  • 用大写将文件命名标准化。虽然服务器可能把Icon.jpg当作 icon.jpg,但是对于Web平台而言,它们是不同的资源,对应不同的网络请求。

3. 高效地组织标记

  • 对于IE而言,请使用最新的标记标准,因为它速度最快。IE 10也能识别早期的IE6-IE9标记风格,但是其速度不如新的标记风格。
  • 特定的业务Web应用可能需要强制IE运行于传统模式,请使用HTTP头字段“X-UA-Compatible: IE=EmulateIE7”来代替HTML标签 ,这样速度会快一些。
  • 为了平滑地渲染,样式表应该链接在页面顶部的<head>之中的<title>后面。
  • 绝对不要在页面底部链接样式表。否则加载页面时可能会出现闪烁。
  • 对于分层样式,不要使用“@import”,因为它是同步的,会阻塞CSS数据结构的创建和屏幕绘制。
  • 避免样式的嵌入和内联,因为它会强制浏览器在HTML和CSS解析器之间进行上下文切换。
  • 仅包含必要的样式。不要下载和解析用不到的样式。
  • 仅在页面底部链接JavaScript。这可以确保脚本执行时图片和CSS等资源已经加载,无需等待,也避免了上下文切换。
  • 不要在页面开头链接JavaScript。如果某些脚本必须在开始处加载的话,请使用“defer”属性。
  • 不要内联JavaScript,这样可以避免上下文切换。
  • 使用“async”属性加载JavaScript,这样整个脚本就可以异步加载和执行。
  • 避免冗余代码。世界上52%的网页包含100行甚至更多的冗余代码,比如一个JavaScript文件被链接了两次。
  • 将一个JS框架标准化,无论是jQuery,Dojo,Prototype.js还是其他框架。浏览器没有必要加载多个功能基本相同的框架。
  • 不要加载FB和Twitter等网站的脚本,只是看起来很酷而已,它们会争用资源。

4. 优化多媒体资源的使用

  • 图片是最常用的资源,每个页面平均会下载58张图片。
  • 尽量避免下载太多图片,根据页面加载时间将图片最大数量控制在20-30之间。
  • 使用Image Sprites将多个图片组合成一个。该技术可以减少网络连接数,也会减少下载的字节数并节省GPU处理周期。
  • 手动创建Image Sprites,因为工具创建的可能会留下较大的空洞,这会加大需要下载的数据量,也需要更多的GPU 处理周期。
  • 使用PNG格式的图片,该格式在下载大小、解码时间、兼容性和压缩率之间实现了完美的折中。JPEG格式可以用于照片。
  • 使用原始的图像分辨率,这样可以避免下载不必要的数据以及缩放所需的CPU 处理。
  • 尽可能使用CSS3 Gradient替代图片。
  • 尽可能使用CSS3 border radius替代图片。
  • 使用CSS3 Transform来创建移动、旋转和倾斜效果。
  • 对于小型的单个图片,可以使用Data URI。这样可以节省一张图片的下载量。
  • 避免复杂的SVG,因为它们会延长下载和处理时间。
  • 当包含HTML5时,指定一个预览图片。这样浏览器就不必下载整个视频文件来确定预览图片了。
  • 使用HTML5来代替Flash、Silverlight或QuickTime。HTML5速度更快,而且其他几种形式的运行时插件会消耗系统资源。
  • 主动地以异步方式下载富媒体资源并将其保存在App Cache中。

5. 编写快速的JavaScript

  • 在JavaScript中进行数学运算时尽量使用整型。JavaScript的浮点运算比相应的整型运算耗费的时间要多得多。在进行数学运算,特别是计算密集型运算时,请使用Math.floor和Math.ceil将浮点数转换为整型数。
  • 降低JavaScript代码量,这样不但可以减少下载的数据量,而且能够提供更好的运行时性能。
  • 按需初始化JS。当需要时动态加载JS。
  • 通过缓存变量(如document和body等)使DOM交互减到最少。
  • 使用内置的DOM代码,如element.firstChild或node.nextSibling等。这些代码都是高度优化的,相对于第三方库能提供更好的性能。
  • 访问大量DOM元素时,使用querySelectorAll。
  • 使用.innerHTML来构建动态页面。
  • 批量标记更改。
  • 维护小巧而健壮的DOM——将其元素数目控制在1000以内。.
  • JSON快于XML。
  • 使用浏览器的JSON原生方法。
  • 不要滥用正则表达式。

6. 知道你的应用在做什么

  • 理解JavaScript定时器,了解setTimeout和clearInterval。除非确定要使用定时器完成一些功能,否则不要启动定时器。组合定时器也是如此。
  • 如果监视器的刷新率是60Hz,请将显式帧的定时器调整为16.7 ms。
  • 在IE 10、Chrome和Firefox中,图形处理请使用requestAnimationFrame动画函数。其绘制通过回调实现,因此不需要定时器。
  • 使用可见性API(document.hidden和 Visibilityhange)来确定应用程序的可见状态,如果页面是隐藏的,就关闭该活动。这样可以节省CPU和电池寿命。
  • Mann建议在IE中使用Windows Performance Tools来测试Web页面的性能, 并以减少CPU时间和增加并发性为目标进行优化

—————-
转自:http://www.searchsoa.com.cn/showcontent_68434.htm

Hello World 业界杂谈

微信二维码登录功能的实现

微信的二维码登录是个很酷的功能:客户端登录后,通过扫描web端的二维码,实现统一用在web端的登录

下面要讲的就是如何这一个功能:

实现思路:
web:访问web页面时,如果未登陆,会根据随机字符串生成二维码,显示到页面上,同时将该字符串保存起来,然后页面会通过ajax轮询方式访问web端,查看这个二维码是否可以登录,如果可以登录,则将用户名/id写入session,然后将页面跳转到登录状态
客户端:扫描页面的二维码后,通过http方式访问web端,将二维码源字符串,以及客户端登录用户的用户名/id发送过去,告诉web端这个用户可以登录

思路很简单,实现起来也不麻烦,下面会列一些核心代码,详细的,请看这个demo,代码可以到这里下载

web端:
二维码生成,使用qrcode库:

/*
 * $source  二维码源字符串
 * $temp  图片缓存目录
 * $errorCorrectionLevel, $matrixPointSize 错误等级、图片大小
 */
function getQRCodeImg($source, $temp, $errorCorrectionLevel, $matrixPointSize){
	$date = date('Ymd');
	$PNG_WEB_DIR = DIRECTORY_SEPARATOR.$temp.DIRECTORY_SEPARATOR.$date.DIRECTORY_SEPARATOR;
	$PNG_TEMP_DIR = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'webroot'.DIRECTORY_SEPARATOR.$temp.DIRECTORY_SEPARATOR.$date.DIRECTORY_SEPARATOR;
	include_once '../lib/qrcode/qrlib.php';
	if (!file_exists($PNG_TEMP_DIR))
		mkdir($PNG_TEMP_DIR);
 
	if (!in_array($errorCorrectionLevel, array('L','M','Q','H')))
		$errorCorrectionLevel = 'M';    
 
	$matrixPointSize = min(max((int)$matrixPointSize, 1), 10);
 
	$filename = md5($source.'|'.$errorCorrectionLevel.'|'.$matrixPointSize).'.png';
 
	QRcode::png($source, $PNG_TEMP_DIR.$filename, $errorCorrectionLevel, $matrixPointSize, 2); 
 
	return $PNG_WEB_DIR.$filename;
}

页面的ajax相当简单:

//ajax轮询时间间隔,通过服务器配置得到
var ASK_TIME = <?php echo conf('ajax.time');?>;
var i = 0;
$(function(){
	setInterval(function(){
		$.get('page_ask.php?source=<?php echo $source;?>', function(d){
			//登录成功,跳转页面
			if(d && d.login){
				window.location = '';
			}
			//测试信息的显示
			$('div.test').html(i++ + '---' + JSON.stringify(d));
		}, 'json');
	}, ASK_TIME * 1000);
});

page_ask.php的核心代码,服务端存储使用mongodb

//ajax轮询检查是否登录		登录成功返回user,否则为false 
function checkSource($source){
	$collection = getDBCollection(conf('mongo.collection'));
	$sou = $collection->findone(array('_id' => $source));
	if($sou && $sou['login'] === 1){
		return $sou['user'];
	}
	return false;
}

客户端通过访问“/client_login.php?source=xxx&user=张三”实现登录,当然,实际的系统应该是传userId

//取记录  返回  1 登录成功  10 source有误  11 此source已使用过  21 无此user(暂无此项)
function checkSource4Login($source, $user){
	$collection = getDBCollection(conf('mongo.collection'));
	$sou = $collection->findone(array('_id' => $source));
	$r = 10;
	if($sou && isset($sou['login'])){
		if($sou['login'] === 0){
			$r = 1;//校验正确
		}else{
			$r = 11;
		}
	}
	//保存登录状态
	if($r == 1){
		//保存登录状态
		$collection->update(
			array('_id' => $source), 
			array('$set' => array('login' => 1, 'user' => $user)), 
			array('safe'=>true)
		);
	}
	return $r;
}

下面是客户端的几个片段,这里使用的是ios
二维码扫描使用的库是ZBar,注意,需要添加一坨frameworks

-(IBAction)startScan:(id)sender
{
    ZBarReaderViewController *reader = [ZBarReaderViewController new];
    reader.readerDelegate = self;
    ZBarImageScanner *scanner = reader.scanner;
    [scanner setSymbology:ZBAR_I25 config:ZBAR_CFG_ENABLE to:0];
    [self presentModalViewController:reader animated:YES];
    [reader release];
}
//将上面这个方法注册给“开始扫描”按钮:
[QRBut addTarget:self action:@selector(startScan:) forControlEvents:UIControlEventTouchUpInside];
//扫描成功后,会自动调用此方法
- (void) imagePickerController: (UIImagePickerController*) picker didFinishPickingMediaWithInfo: (NSDictionary*) info
{
    UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage];    
    id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults];
    ZBarSymbol *symbol = nil;    
    for(symbol in results){
        break;
    }    
    if(!symbol || !image){
        return;
    }    
    NSLog(@"symbol.data = %@", symbol.data);  
    [self sendRQcode:symbol.data];  
    [picker dismissModalViewControllerAnimated: YES];
} //imagePickerController

//取得二维码后,通过http方式发送,然后解析返回的json数据

- (void) sendRQcode:(NSString*)code
{
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0];
    NSTimeInterval a=[dat timeIntervalSince1970];
    NSString *timeString = [NSString stringWithFormat:@"%f", a];
    NSHTTPURLResponse* urlResponse = nil;
    NSError *error = [[NSError alloc] init];
    NSLog(@"-------loginClick------user:%@", timeString);
    NSString *url = [[[[[@"http://xxxxxx/client_login.php?user=" stringByAppendingString:user]                     
            stringByAppendingString:@"&source="] stringByAppendingString:code]
            stringByAppendingString:@"&time="] stringByAppendingString:timeString];
    [request setURL:[NSURL URLWithString:url]];
    [request setHTTPMethod:@"GET"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];  
    //发送请求  
    NSData *returnData = [NSURLConnection sendSynchronousRequest:request 
            returningResponse:&urlResponse error:&error];
    NSLog(@"-------sendRQcode--status:------%d", [urlResponse statusCode]);
    NSString *login = @"0";
    if(returnData != NULL && [urlResponse statusCode] == 200) {
        //解析json
        NSDictionary *data = [returnData objectFromJSONData];
        NSLog(@"-------sendRQcode----returnData:------%@", data);
        login = [data objectForKey:@"login"];
    }
    NSLog(@"-------sendRQcode----login:------%@", login);
    msg = @"";
    if([login isEqual: @"1"]){
        msg = @"登录成功!";
    }else if([login isEqual:@"0"]){
        msg = @"网络连接错误,请检查网络设置后重试!";
    }else{
        msg = [[@"未知错误(login:" stringByAppendingString:login] stringByAppendingString:@"),请检查网络设置后重试!"];
    }
    NSLog(@"-------sendRQcode----msg:------%@", msg);
    [self alertWithMessage:msg];
}//sendRQcode

———–
代码就展示这么多,具体的,请访问github:https://github.com/jklgithub/qrcode_login
本文为本人原创,转载请注明地址:http://www.jiangkl.com/2012/10/%E4%BA%8C%E7%BB%B4%E7%A0%81%E7%99%BB%E5%BD%95_qrcode_zbar/
演示地址:dev.jiangkl.com (mongo不太稳定,测试不通的话可以给我留言)

业界杂谈 他山石

[转]提升CakePHP网页应用速度的八大方法

原文标题:8 Ways to Speed Up the Performance of CakePHP Apps

引言

CakePHP 有些慢已经不是什么公开的秘密了。但不为众人所知的是这是一个大大的阴谋。而我会因揭露这个阴谋惹上不少麻烦。不过我愿意冒险一搏。我手头有大量的记录和文档线索表明CakePHP开发团队的成员手头持有戴尔,IBM,思科以及其他服务器厂商的公司股票。想必你已经听说过这类的措辞像“硬件白菜价,而程序员太金贵了。”Cake的开发团队从中嗅到了商机,他们便制作出了这款易于上手和开发,但是却跑得很慢的框架来。他们希望你在硬件方面多砸点钱来。这点子太有才了吧,对不?好了,闲扯就此结束。作为一个CakePHP的开发者,每当你学会使用本文中的一个技巧,套在你脖子上的这枷锁就少了一层。

注意:

本文认为你已经在使用ContainableBehavior并且已经对你的SQL查询进行优化和索引了
我使用ab(ApacheBench是Apache自带的HTTP性能测试工具)对所有改动后的性能进行了测试,并与基准值做了对比。这里的基准值是 debug级别为0时的简单应用程序的性能。我并没有把实际基准数字贴出来。因为这类数字会因应用程序和机器性能而波动。不过我将以百分比的形式将大概的 性能提升数据附在了文中。
另外,你想看看我测试所用的应用程序内容?门儿都没有。

一、 将debug级别设置为0

这技巧很没技术含量,是吧?在Google小组中有 大量的帖子在扯其他方法。想都不用想,先把你CakePHP应用程序的debug级别设置为0在说。

将此值设0和不设0是有区别的,原因就在Cake引 擎生成两部分缓存内容

第一部分是/tmp /cache/models中的缓存内容。在这里,你会看到每个模型都有一个对应的文件,这些文件中包含的是数据库的表模式(table schema)。想必你已经看到过数据库查询记录中包含一些”DESC table;” 这类的查询内容,这些缓存文件就是做着这个用的。当debug级别设置为0时,这些数据库查询就不会进行。

第二部分的缓存是在/tmp /cache/persistent中。当你运行你的Cake网页应用时,CakePHP会用到其中几种不同的文件。而通常拖慢CakePHP运行速度最 厉害的当属cake_core_file_map。这个文件保存了到各个不同类的路径信息。为了创建这个文件,CakePHP会做一个必须但是也是非常耗 费时间的搜索,以便在你的目录结构中找到对应的文件。

那么,debug级别是0和debug级别大于0时有什么区别呢?呵,他们两者之间相差的是2.73517104年。当debug级别大于0时,这些缓存文件的时间周期是10秒钟。而把debug级别设置为0时,这些文件的缓存时间周期则是999 天。(译者:这就是前面 2.73517104年差距的来历)

实 际上我这里有这样一个疑问:既然这种事情是非常“脑残”的技巧,那么连这种“脑残”的技巧都不采用的人是不是比“脑残”好“脑残”呢?如果连“脑残”的人 都能弄明白这种技巧,那么那些不会的人的脑子是不是被狗吃了。既然这些人的脑子被狗吃了,那么我对这钟人讲这种技巧岂不是肉包子打狗——有去无回?(译 者:囧)

性能大致提升数字为:80%到100%左右

二、缓存拖慢速度的查询/网络服务请求等等

对于缓存你应用程序中的单个部件而 言,CakePHP的缓存库(cache lib)是一个非常不错的工具。它会处理所有“脏重累的活儿”,比如向缓存引擎中的文件或者内存中写入内容。你所需要做的就是想清楚哪些东西需要缓存。

打个比方说,你有一个已经索引并已经优化了的数据库 查询,但是查询速度还是比较慢。CakePHP的烹饪手册上(link)就提供了一个例子,教你 如何将缓存库应用起来,缓存后,CakePHP就不会对每一个请求都进行这种拖慢速度的数据库查询。

如 果你网站的部分内容是来自其他网络服务的的,比如一个显示最新推文的区域(这个例子并不恰当,因为大多数Twitter挂件都是用JavaScript写 成的,这里读者就将就一下吧。)我们完全没必要对每个发来的请求都调用这个网络服务。只需像上面一样,用缓存功能将其缓存起来即可

性能提升:+0% 到 1000000%,这取决于你的应用程序,以及你所缓存的内容。

三、缓存视图

你可将这视作整个页面的缓存。CakePHP的烹饪手册提供了一些基本的知识(link),因为页面创建依然是通过PHP进 行,所以对于保持页面的部分动态内容还是有些灵活方法的。比如,如果你运营着一个网店时,你可以将产品页面进行缓存,但是页面上还是可以有一块区域来显示 用户的购物车内容,

注意:在CakePHP的烹饪手册中有一个专门的小节来介绍CakePHP支持的各种缓存引擎(link)。 但是,目前的版本(1.2.1.8004),视图缓存所使用的是基于文件的缓存引擎,并不依赖于上面第二部分所说的缓存库( cache library)
速度大致提升:+130% 到 160%

四、HTML网页页面缓存

这个是我们自己的想出来的(link)。 它的原理跟WordPress上的Super Cache插件一样。它会将CakePHP的 页面以HTML文档的形式直接写入网站的根目录。下次,当用户点击这个页面时,你的服务器会直接将这个HTML文件传输给用户,而无需运行PHP。

这种方法也有其明显的局限性,比如缓存的页面上完全 没有动态内容,缓存的内容也不会自动清除。但是,对于RSS的订阅源或者其他像popurls 这种所有浏览者所看到的都是同样内容的页面而言,这种方法就非常不错。

速度提升:~60000%——这绝非夸张,这才是真正的速度提 升

五、 APC

维基百科是这样描述APC的,“它是一种自由的开源框架,它可以优化PHP的中间 代码,并在共享内存中缓存PHP字节码编译器(Bytecode compiler)中的数据和编译好的代码。”(维基百科链接)。总的来说,速度真TMD的 快,并且你还无需改动你的代码。很爽,是吧?

速度提升:+25% 到 100%

六、持久性模型

CakePHP的烹饪手册并没有提及这个技巧,这个 功能的开启很简单,你只需在你的控制器(或者父控制器中)中添加如下属性即可

var $persistModel = true;

在页面刷新后,你会注意到 /tmp/cache/persistent 目录下会出现两个新的文件,它对应控制器所包含的每个模型。一个文件是模型的缓存,另一个缓存的是类注册器(ClassRegistry)中的对象。跟上 面所提到的视图的缓存一样,这种缓存只会保存在文件系统中。

速度提升:+0%到200%

这种方法效果怎么样取决于你的应用程序。如果你的控制器只有一个模型,并且并没有与其他任何模型关联,那么你不会看到有多大的速度提升。在我们的测试应用程 序中,它带来的速度提升大约在100%左右。其中测试用的控制器中有一个模型,该模型关联其他三个模型,这些关联的模型又有各自关联的模型。

七、在APC中存储持久性缓存内容

要使用这个,你需要开启APC,将CakePHP的 核心缓存模式设置为APC。在core.php文件中,添加如下代码。

Cache::config(‘_cake_core_’, array(‘engine’ => ‘Apc’,
‘duration’=> 3600,
‘probability’=> 100,
));
这会将通常缓存在/tmp/cache/persistent 中的缓存文件(不包括持久性模型)保存到内存中去。

速度提升:~25%

这带来的速度提升很难衡量。我试着启用不带指令缓存 (opcode caching)的APC,以便衡量所带来的速度提升,但是却找不到如何设置。

八、加速反向网址路径的寻找

目前有两种方法可以达到此目的。第一种是Debuggable.com网站上Tim同学在一篇文章中所提及的。Tim的方法只对某种链接类型有效,而且还会破坏反向网址路径的寻找功能,而我的方法使用的是缓存功能(link) (译者:具体内容见这个链接。),CakePHP开发团队的Nate同学也称这种方法非常“高明”(link)

速度提升:~50%

正如这里的所有技巧一样,实际的速度提升取决于你的 应用程序。如果你并不使用大量的自定义网址路径,而且页面的链接也不多,那么这个技巧不会带来多大的速度提升

——
转自:http://21haolou.com/articles/show/46

业界杂谈 日志

一年又一年

间总是过的很快,前几天忙着“优化“自己一年前写做的东西~~忍不住有点感慨。

每看到一段写的很烂的代码,或者找不到要改的逻辑在哪儿时,总会怀疑:这是我写的吗?真的是我写的吗~~(不是你还有谁!!)如果时间允许,真想再重构一次~~可惜时间不允许呀。

去年做这一版时,正是刚开始专js的时候,当时一知半解,又自以为是,很多做法现在看来不仅效率低下,开发麻烦,而且用户体验也不好~~如果当时身边有个js牛人指点指点可能会好很多。

而这一版之前的一版,大约也是在再上一年写的。那时对js的理解还仅停留在jquery和最初级的面向对象的层次,所以那一版虽然功能比较简单,不过两三千行代码,但页面执行已经相当之慢,逻辑乱的改一个小地方也要找半小时。

看过一个微薄说人身体的细胞每7年就会更换一遍,从这层意义上说,7年以后的你就是另外一个人了。可是看自己的代码,似乎一年以后就是另外一个自己了,作为每时每刻都会诞生新东西的行业,程序员需要不间断的学习充实自己,然后不停的去否定,去更新以前的自己,然后就慢慢变成更好的另一个自己。

希望自己可以克服惰性,继续保持谦卑的学习心态~~~~

Hello World 业界杂谈

IE出没,当心!!

有多少前端攻城湿,就有多少IE受害者~~~ →_→

    这段时间开始搞webview,html5~~~~本来以为总算可以暂时逃离IE的魔咒了,可是没想到的是,除了iphone和android,还有个wp7,wp7里还有个IE9,之前听说IE9支持html5,所以开始并没有把它放在心上,可是真正要用它的时候,问题来了

    开始的时候是习惯的拿alert测试,没反应,把页面内容删光,只剩一个alert也不行,google一下,以为是客户端对webview功能做了限制,去找客户端同事,人家很委屈的说,没做任何限制~~~好吧,不能alert,换其他方法测试

    然后是发觉click绑定有时不成功,开始以为绑定方法不对,试遍了jquery的的live、click、bind,和DOM的onclick,都不行,将同样的页面直接用wp7的IE9打开,于是又去找客户端,这次同事认真查了wp7的手册,回复给我一段文档,大意是说:wp7的webview只支持简单的html显示。可是其他很多js功能是可以用的~~~嗯,也就是说具体支持哪些js功能~~没准~~~~这便是悲剧之源

    后来,死马当活马医,将链接的“href=javascript:void(0)”,改成“href=#”,click绑定居然起效了~~~~尼玛的微软,连最屎的IE6都支持“void(0)”

    做技术的有句话:发现了问题就等于解决了问题~~~~可是对于眼下这种薛定谔的猫的问题,真的没办法了,除非有时间一个一个试去~~~~可惜没时间~~~~无奈,降格,把你当wap用总可以吧!!!

蒙娜丽莎

业界杂谈 日志

LAMP人分享交流会

昨天长途跋涉去中关村参加了“第13期#LAMP人#主题分享交流会”,感觉很不错,对得起一天的时间。

这次是木瓜移动专场,第一场是关于移动游戏市场的,与技术关系不大;第二场“社交网络动态框架”,内容实际是web开发在手机客户端的应用;第三场是在讲使用木瓜移动平台做游戏,讲游戏平台前,有一大半时间是在将计算机视频处理~~~汗,技术含量太高了,听了个热闹;最后还有个Any Share环节,分别是phonegap介绍和svn的使用小技巧

收获最大的是第二场,由木瓜移动的研发总监李章晶讲的手机客户端如何与web开发相结合。不同于简单的webview,他所说的方法,将手机客户端与webview结合的更紧密,使用高度定制化的浏览器:1. 客户端会保存web页面的html等资源,并结合svn自动更新  2. webview的ajax请求实际是走的客户端的tcp,节约了http的连接时间  3. 客户端和web页面通过js交互,使更新页面内容变得很简单~~~不懂客户端开发,具体细节记不清了

另外,后面的PhoneGap介绍,也是蛮有吸引力的的,可以使用html/js/css开发跨平台的手机应用,有时间的时候会去做些尝试

整体感觉,昨天下午比之前参加两次百度技术沙龙的收获更多。后者的分享基本是分享者在讲自己的东西,听者只能是借鉴一些共通的经验;而昨天下午的技术分享,基本都是就技术论技术,特别是第二场李章晶的分享,确实是在给听众介绍实际的方法~~~还有最重要的一点,前者是有小礼品的 ; )  ~~~当然,这里没有贬低百度技术沙龙的意思呀~~~分享思路不同而已

Hello World 业界杂谈

书签里的js

如要你要去现在很流行的花瓣网去注册,会发现其中有一步,是让你将它的一个“采集到花瓣”的链接添加到你浏览器的书签里。当你使用这个浏览器再浏览其他网页时,点这个书签,就会弹出花瓣网采集该网页图片的界面~~
这是一个很nice的功能,已经有点类似客户端了,把这个书签里的“链接”拿出来,整理一下:

function(d,a,c,b){
	//首先检查document里面是否有'__huaban',如果没有,就去加载一段自己的js
	if(a[c] && typeof a[c].showValidImages=='function'){
		a[c].showValidImages()
	}else{
		b=a.createElement('script');
		b.id='huaban_script';
		b.setAttribute('charset','utf-8');
		b.src='http://huaban.com/js/pinmarklet.js?'+Math.floor(new Date());
		a.body.appendChild(b);
	}
}(window,document,'__huaban'));

对,这段代码就是取加载一段花瓣网自己的js~~~~~~ 😈 😈 在别人的页面上注入一段自己的代码,然后它就可以为所欲为了:除了抓图片,抓视频,还可以取cookie,修改页面css等等
除了书签外,还可以直接在地址栏执行js的逻辑,比如:javascript:alert(document.title);
其实,把js放在书签里,只是浏览器地址栏执行js的一种表现而已

Hello World 业界杂谈

整站ajax中url的处理

早在两年前,互联网中的网页数量就已超过了1万亿,并且,每天还会增加数十亿。虽然是如此浩瀚的世界,我们却可以通过url轻而易举的找到每一个页面,可见url对于网络世界时多么的重要。

随着浏览器的进步和对用户体验要求的提升,整站ajax之类的一站式前端应用正在逐步取代传统的一个url对一个页面的展示方式。在整站ajax中,数个模块在一个url下,采用无刷新的方式切换,通过减少页面刷新有效的提升了用户体验,但也带来了一些问题:

1. 某些用户习惯根据url判断自己所在的位置,如果明明点击了链接,页面变化了,url却没有变化,用户会比较晕

2. 用户保存书签后,在打开这个书签,得到的可能并不是自己想要的内容

3. 模块间切换后,不能使用浏览器的回退键退回去

针对上述问题,比较简单的方式是使用锚点做标记:每个模块切换时,锚点都会有所变化,比如:/index#list,/index#edit,这样,可以有效的解决上面提到的第1个问题;对第2个问题,前端代码初始化时根据当前页面的hash做些处理也就行了;第3个问题稍微麻烦一些,需要监控页面url的变化。

浏览器不可以给地址栏添加监听时间,只能通过定时读取location.hash/href来判断锚点是否发生了变化。定时长度不能太长,否则影响用户体验,也不能太频,会耗太多资源。这种定时的方式虽然很土,但出了“坚挺”浏览器是否回退、前进以外,却还可以减少很多绑定链接click的操作:只需要给按钮链接的href加上锚点,将参数写在锚点里,用户点击这个url时,锚点变化被定时程序“监听”到,就可以执行相应的逻辑(还顺道解决了按钮重复点击的问题,一举多得呀 :-))。关于如何把button按钮转变为锚点链接的点击可以参考本人以前的博文:js触发链接

不过使用定时方式“监听”浏览器回退,还有一个无法解决的缺陷(只是本人还未发现解决方法),IE6/7的锚点跳转记录不会进入history对象,也就是在IE6/7里,有A页面进入B页面后,B页面内由/B跳到/B#1,再跳到/B#2,再“回退”,也还是会回到A页面,无法回到中间的/B跳和/B#1…….IE而已,可以暂时不去理它。

前面说,使用方式监听地址栏变化有些土,那么,有跟好的办法吗?

之前转发的一篇博文(使用ajax和history.pushState无刷新改变页面URL)里提到了html5的一个新特性:可以注册“popstate”事件监听url的变化,我们可把前面的定时“监听”稍作改进,如果浏览器支持popstate,就使用popstate,如果不支持,在使用定时。本人测试表明,firefox、chrome、safari支持popstate事件,IE系、以及opera不支持。

———-End

业界杂谈 他山石

[转]抓大放小,从粗到精

作为产品经理的你,洋洋洒洒的整理完产品的功能列表,长舒一口气,看,爷的产品多强大,规划了这么多牛掰闪闪的功能;作为交互设计师的你,头晕眼花的画完产品的原型和流程,暗自窃喜道,看,咱的流程多细致,梳理了那么多特殊情况和可能性;作为视觉设计师的你,设计了好多个华丽丽的界面,你为每一个页面进行了精心的雕琢,期待用户见到每一个页面都竖起大拇指说这个界面真漂亮;作为开发人员的你,搭建完框架之后,发现每个模块都有评审时漏掉的细节,一个模块一个模块的赶进度,导致你精疲力尽力不从心。

其实,在一个产品里,并不是所有的功能都那么重要的,如果产品经理只是交付一个功能列表,而不做需求优先级设定的话,产品可能沦为没有主心骨的产品,交互设计师设计的时候,不确定主要任务是什么;视觉设计师设计的的时候,不确定哪些界面和模块要提供更精致的设计;开发人员开发的时候,不知道哪些功能该设定更高的开发优先级,于是就会形成大家都在搅浆糊的状态,可能因为某些弱弱的分支流程的复杂性,导致开发人员花了大量的时间去攻克难题;可能因为某个不那么重要的设置界面中,UI增加了复杂的转场效果,导致开发人员搞到头破血流;可能因为开发人员捡着简单的功能先做,导致复杂的重要功能到最后才被草草攻克,一堆bug。别在抱怨各个角色不给力什么的了,想想自己的需求的分析是否到位,是否给你的需求排排等级,哪些对解决产品的商业价值+用户价值有最大的帮助,优先实现它,不重要的,不着急的,可以次优先级实现。

产品功能就像镜头里的花,如果你的镜头里,全是花,会因为全是亮点而变得没有亮点;如果你的镜头里,聚焦于一朵最娇艳的话,亮点就轻松浮现了。但有时候,说不做什么比说做什么还要难。必须每次改版,都上一些新功能,才能让用户知道我们在持续改进,才能让老板知道我们的团队充满战斗力。针对这些要上的功能(或来源于客户,或来源于老板,或来源于自己突发奇想),如果你经过了反复的调研,结论是做的意义不大,你甚至都不敢汇报,还是要硬着头皮上新功能。直到程序底层架构出了大问题,才不得不停下来做代码重构。

“People think focus means saying yes to the thing you’ve got to focus on. But that’s not what it means at all. It means saying no to the hundred other good ideas that there are. You have to pick carefully. I’m actually as proud of the things we haven’t done as the things I have done. Innovation is saying ‘no’ to 1,000 things. ——Steve Jobs

是的,不得不说,乔布斯是一个睿智的产品经理,他眼光独到,敢于取舍,会为了一个产品秘密研发几年,终于厚积薄发。国内环境不太一样,是非成败,瞬息万变,没有一个老板乐意让团队研发那么久,下那么大赌注在一款产品上。他们希望马上看到成绩,逼得团队甚至没有太多时间去深入调研,只能找到一些已经被证明可用的模式,Copy to China,虽说有些变态,可更多的是无奈。

即便如此,产品经理仍然可以讲清楚,产品的核心模块、核心功能是什么。就像这样一个金字塔,塔尖上的是最重要的需求,没有这些,就没有产品价值。

而一个交互设计师,则需要在了解清楚主要需求之后,对应分解到主要任务流程,你需要花80%的精力去设计那20%的重要任务流程,而这20%的重要任务流程,又足以解决80%的用户的核心需求。

有两个技巧让你不会劳心劳力又没成绩——1.抓大放小,要精心雕琢主要任务快速完成次要任务;2.从粗到精,不要上来就陷入到细节中去

其实花了这么多笔墨,只是讲了一个简单的道理,学会做减法,学会排优先级,学会抓主要矛盾。但这本身就是一件知易行难的事,还需要在实践中不断磨练。
———-
转自:http://ucdchina.com/snap/10368

Hello World 业界杂谈

百度技术沙龙

昨天下午跑去参加的百度技术沙龙,之所以回去,是因为这一期的主题是“全站式ajax应用”,正是自己近半年一直在做的东西—-闭门造车这么久,终于有机会看看别人是怎么做的了。
两场讲座,一个是百度的人在讲自己的一个前端框架,领一个是IBM的的人在讲Dojo。
百度董睿所讲的ER框架(这里),用来做所谓整站式ajax应用,在前端实现了MVC的分离、url的前进后退、参数的传递、html模版等等,以及DOM操作~~~听他刚开始讲ER框架用途和亮点时,自己突然,第一次有了一种站在了技术最前沿的感觉—-几个月前为空间通讯录做的前端框架,实现的正是这套东西,MVC/url参数传递等的思想方式与ER很类似,不同的是DOM操作是基于jquery的,另外,为了应对页面灵活多变的显示方式,没有使用html模版。
不过听了ER的一些做法,自己还是受了一些启发的,比如url参数传递方式可以更精简,MVC分层可以更清晰,引入html模版降低使用难度和维护成本等等~~~四个小时的公车算是没白坐吧。
另一场关于Dojo的讲座(这里)—-本来主要式冲着这一场去的—-讲的有些泛泛,基本上就是在推广Dojo,感觉没有多少实质性的内容。
第一次参加这种行业内的技术讲座,本来以为会有更权威的人讲解怎么做 “全站式ajax应用”,但实际两位都只是在介绍、推广自己的东西,所以,刚听完的时候感觉有些失望。但后来想了想,这种技术分享,也只能做到什么程度~~~大家只是将自己的实践经验推广给别人,没有所谓的权威,没有绝对的对与错,大家都是探路者。。。