风为人世在,在世人为风。

如何减少前端异步请求的数量

开发模式

在前端开发中,一条很重要的优化原则就是减少HTTP请求数。但在实际开发中,有时候不得不用大量的异步请求。这里的异步请求所指的都是页面加载时的,之后用户操作过程中所发生的异步请求并不影响加载的速度。

在百度这边,由于FE需要写模板。所以很多时候有些功能如果有接口了,RD同学就要你使用异步接口。但异步接口使用多了就会影响页面的性能。比如说百度空间个人中心在页面加载时差不多要处理10个异步请求。这个数量是非常大的。如果减少这些异步请求的数量成为了优化的重点之重。

实现方案

目前想到一条比较可行的实现方案是通过一个公用的代理页面,前端在处理时将这些异步请求分组去处理。代理页面获取到打包的URL,然后服务器端去请求,最后将合并后的数据输出。

在服务器端去请求,由于数据是服务器对服务器的,数据网络传输时间可以忽略不计。

实现代码

  1. if(typeof Space == 'undefined') window.Space = {};
  2. Space.asynJs = function(){
  3.     var _isStart = false,
  4.         stack = [];
  5.     function start(){
  6.         if(_isStart){
  7.             throw new Error('asynJs request is start.');
  8.             return false;
  9.         }
  10.         _isStart = true;
  11.         for(var i = 0, len = stack.length; i < len; i++){
  12.             if(!stack[i]) continue;
  13.             setTimeout(function(){
  14.                 //如果该组只有一条记录,就不用走通用代理了。
  15.                 if(stack[i].length == 1){
  16.                     baidu.sio.callByServer(decodeURIComponent(url));
  17.                 }else{
  18.                     var url = 'url[]=' + stack[i].join('&url[]=');
  19.                     baidu.page.loadJsFile('http://hi.baidu.com/st/asynproxy.php?' + url);
  20.                 }
  21.             },0)
  22.         }
  23.     }
  24.     function load(url, group){
  25.         group = group | 0;
  26.         url = encodeURIComponent(url);
  27.         if(!stack[group]){
  28.             stack[group] = [url];
  29.         }else{
  30.             stack[group].push(url);
  31.         }
  32.     }
  33.     return {
  34.         load:load,
  35.         start:start
  36.     }
  37. }()

该实现方式就是提供了2个方法load和start,load用于填充要请求的异步接口,start在页面底部启动请求。因为页面一般都分成头,中,尾3个部分,所以start方法可以在尾部文件里直接调用就可以了。

  1. Space.asynJs.load('http://pageurl1', 1)
  2. Space.asynJs.load('http://pageurl2', 1)
  3. Space.asynJs.load('http://pageurl3', 2)
  4. //在页面最底部启动请求
  5. Space.asynJs.start();

PHP端的实现

  1. <?php
  2. //url is array
  3. $urls = $_GET['url'];
  4. //经过白名单过滤后的URL
  5. $urls = apply_domain_filter($urls);
  6. //实例化Curl类
  7. $curl = new Curl();
  8. $contents = array();
  9. foreach($urls as $url){
  10.     //需要配置对应URL的transmit
  11.     //请求时带上传递过来的Cookie
  12.     $contents[] = $curl->get($url, $_COOKIE);
  13. }
  14. echo join(';', $contents);
  15. ?>

额外的收益

目前一些框架(tangram)里domready时执行一些函数时并没有提供函数排序的功能,这样就不能实现重要的异步请求先加载不重要的请求后加载的功能。目前百度空间app平台这边实现方式是通过setTimeout延时来执行。如果设置的时间较短,各个浏览器表现的不一样。如果设置的时间比较长,有会影响页面加载的时间。

通过现在的这种方式后,不光实现了异步接口分组的功能,同时实现了重要的先加载,不重要的后加载。
先加载的东西只要第二个参数传值小一些就可以了。

风险

由于通用代理管理的是获取URL然后请求的工作,所以如果在后端不做一些判断的话可能引发一些风险,甚至XSS漏洞。目前想到的主要是2点。

1、通用代理要判断域,非白名单制定域不予请求
2、返回时必须设置header为application/javascript来阻止直接请求的XSS漏洞

MBTI在线职业测试

MBTI是什么?

MBTI 人格理论的基础是著名心理学家卡尔·荣格先生关于心理类型的划分,后经一对母女Katharine Cook Briggs 与Isabel Briggs Myers 研究并加以发展。

四个维度

共有四个维度:

MBTI 人格共有四个维度,每个维度有两个方向,共计八个方面。

分别是:

外向(E) 和 内向(I)

感觉(S) 和 直觉(N)

思考(T) 和 情感(F)

判断(J) 和 知觉(P)

十六种类型

四个维度,两两组合,共有十六种类型。以各个维度的字母表示类型,如下:

ESFP            ISFP               ENFJ              ENFP

ESTP            ISTP               INFJ               INFP

ESFJ            ISFJ                ENTP              INTP

ESTJ            ISTJ                ENTJ              INTJ

四个维度在每个人身上会有不同的比重,不同的比重会导致不同的表现,关键在于各个   维度上的人均指数和相对指数的大小。

更多介绍:http://baike.baidu.com/view/3062806.htm?fr=ala0_1

MBIT在线测试

本来想在网上找个地方测试下,找了好久都没找到在线测试。下了个小决心,决定自己写个。

花了2个小时给整出来了。

在线地址:http://www.welefen.com/lab/mbti/

月陀岛之行

周末space大bui,带了媳妇去月陀岛,由于下雨的关系,玩的地方并不多。发几张照片纪念下。

巧用XSS为实际需求服务

昨天早上和JerryQu骑车上班的途中聊天,他这几天遇到一个特殊的需求。

页面中调用一个通过CMS发布的callback,callback返回一段字符串内容,然后页面拿到内容后直接innerHTML塞到某个ID里面去。本身这是个很简单的需求。

但是现在需求复杂了,希望callback返回内容中有一段脚本,用来操作页面的DOM。不想改变页面的原因是,要走整套上线流程,非常麻烦,你们懂的。

也就是本事返回的内容是“这是返回的内容”,现在变成“这是返回的内容<script>这里增加一些脚本操作页面</script>”,但是这样直接innerHTML进去的话是不被执行的。

一种解决方案是,将script标签变成iframe,嵌套一个新的页面(这个页面也是通过CMS发布,所以也很方便),然后iframe的页面用来操作父页面的DOM(当然是在同一个域下)。

后来想到了一种解决方案。利用XSS经常用的方式,插入一个img标签,设置src为一个不存在的URL,然后再onerror事件里操作页面的DOM,如:

返回内容为“这是返回的内容<img src=”h” onerror=”这里的脚本用来操作页面DOM”>”。

经测试,一切OK。

按字节截取字符串

在JS中,由于中文和英文是同等对待的,但有时候我们希望是一个中文按两个字节算,这就出现了按字节截取字符串的功能。下面列举了 2种实现方式。

循环检测

这种实现方式来自于Tangram,具体实现如下:

  1. baidu.string.getByteLength = function (source) {
  2.    return String(source).replace(/[^\x00-\xff]/g, "ci").length;
  3.    };
  4. /*
  5. * Tangram
  6. * Copyright 2009 Baidu Inc. All rights reserved.
  7. *
  8. * path: baidu/string/subByte.js
  9. * author: dron, erik
  10. * version: 1.1.0
  11. * date: 2009/11/30
  12. */
  13. /**
  14. * 对目标字符串按gbk编码截取字节长度
  15. *
  16. * @param {string} source 目标字符串
  17. * @param {number} length 需要截取的字节长度
  18. * @return {string} 字符串截取结果
  19. */
  20. baidu.string.subByte = function (source, length) {
  21.    source = String(source);
  22.    var getLen = baidu.string.getByteLength, i, len, current, next, currentLen, nextLen;
  23.    if (length < 0 || getLen(source) <= length) {
  24.       return source;
  25.       }
  26.    len = source.length;
  27.    for (i = Math.floor(length / 2) - 1; i < len; i++) {
  28.       current = next || source.substr(0, i);
  29.       currentLen = nextLen || getLen(current);
  30.       if (currentLen == length) {
  31.          return current;
  32.          }
  33.       else {
  34.          next = source.substr(0, i + 1);
  35.          nextLen = getLen(next);
  36.          if (nextLen > length) {
  37.             return current;
  38.             }
  39.          }
  40.       }
  41.    return source;
  42.    };

baidu.string是字符串常见操作的对象。

正则替换

这种方式的实现原理是先将中文替换成中文加个空格,这样变相的将一个中文变成了2个字节,然后在这个基础上截取,截取完成后在将中文加空格变成中文。

  1. baidu.string.subByte1 = function(source, length){
  2.     return (source+'').substr(0,length).replace(/([^\x00-\xff])/g,'$1 ').substr(0,length).replace(/([^\x00-\xff]) /g,'$1');
  3. }

这种实现方式不管是从代码量上还是从效率上都要比第一种高很多,这里有个测试案例

js动态创建类和实例化

在js中,创建一个类和实例化该类一般方式是:

  1. var cls = function(){}
  2. cls.prototype = {
  3. attr:'',
  4. method:function(){}
  5. }
  6. var clsInstance = new cls;

这种方式简单明了,但如果类很多的话就比较痛苦了,并且代码看起来不够优化。

动态创建类

动态创建类实际上类似于一种代理的模式,代码如下:

  1. var Fath = function(methods){
  2.     var cls = function(){
  3.         return new fn(arguments);
  4.     },
  5.     fn = function(args){
  6.         return this.init && this.init.apply && this.init.apply(this,args);
  7.     };
  8.     fn.prototype = cls.prototype = methods || {};
  9.     try{
  10.         return cls;
  11.     }finally{
  12.         cls = null;
  13.     }
  14. }

创建一个类:
var TestClass = Fath(methods) //这里的methods是方法或者属性集合,是一个静态对象
实例化该类:
var testInstance = TestClass(params) //这里的params是传进去的形参
通过这种方式后,一个好处就是实例化类的时候不用再使用new了,当然使用new也是可以的,作用的等价的。避免了到处是new和prototype的好处。
性能上在1K数量级上没有很大的区别。

Smarty中实现模板继承功能

在PHP+Smarty大行其道的今天,怎么样进行快速的模板开发成为了前端开发人员必须思考的问题。不同的页面,相同或者相似的页面结构是否进行了模块化处理,同样的头部或者尾部是否进行了内容分离决定了开发的效率。虽然Smarty本身支持include a file的功能,但这远远不够。我们需要的是不想重写同样的页面结构。

Jinja2的模板继承功能

Jinja是基于python的模板引擎,有个非常实用的功能就是模板继承。不太了解的可以去这里看详细的介绍http://opensource.csdn.net/bbs/thread/3943?lp=1

模板继承的好处就是你不在需要写整个页面,只要写页面的模块,然后用一个布局的页面套一下就可以了。并且页面中的模块书写顺序是无序的。

smarty本身并没有模板继承的功能,但我们可以通过block,capture和方法插件(其实block,capture也是插件)。 继续阅读 »

生日与星座绑定的杯具

这个标题是基于生日是阴历的情况下。在中国,我相信有很多人过的都是阴历,我就是其中一个,并且是在腊月。但在国内很大网站,包括一些大型网站生日都没有阴阳历的选择。这样会导致什么样的后果呢?就拿我自己来说吧,我过的是阴历,本来我很想把真实的生日填上去的,这样在生日的时候可以接受一些生日礼物,这对社区型网站是非常重要的,这样可以增加用户的回访率。但现实很残酷,由于并不是每一年的阴阳历都是对应的(如:我出生是86年12月11日,对应的阳历是87年01月10日。但在2009年这两个日期是不对应的)。曾经出现这样的事情,hi上给我祝贺时,我只能说今天不是我生日。但个人很杯具,但对这个产品更杯具,几次下来这个用户可能就流失了。

现在已经有几个产品已经注意到这个问题了。下面从我知道的意义列举,他们做的确实非常不错。

QQ

不管QQ是如何出生的,但QQ2009做的非常不错,非常好看的UI,也很不错的UE。QQ2009用了一年多了没出现过崩溃和卡死的现象,这是百度Hi和阿里旺旺所不能及的,当然后2款IM产品存在时间确实也QQ要短很多,所以可以原谅。 继续阅读 »

那些伴随成长的野果

那些伴随成长的野果,现在却叫不全名字来,着实是个大杯具。

记忆力下降惊人啊。。。

大茶树上长的果子,未成熟时是青色,待外面的皮脱了才好吃。不过吃的时候要小心掰开,因为有的里面会有“百节虫”。

继续阅读 »

回顾2009

2009马上就要过去,2010马上就要开始。在展望新的一年的同时,回顾下我在2009年的变化。

一切从年初开始说起。。。

杭州的故事

继去年的惨淡经历后,混迹于杭州,继续在阿里巴巴做前端开发外包工作。

但心里暗下决心,2009我一定要改变什么,不然就废弃了,到了2010估计就没奋斗的决心了。

但由于某些方面的特殊性,又不能太果断的换工作,对我来说找一份新的工作太难了。

虽然每天上下班要在公交车了呆4个小时,但始终疲惫的坚持着。

6月份获知阿里巴巴要在8月初搬到滨江的新大楼。

这样我上班就更远了,给我一种换工作的准备。

正好在blueidea看到上海的一个外企在招前端开发工程师的职位,想到外企应该在某些方面不是很严,于是乎就投了简历。

今天下来就收到了offer,一个月后告别杭州,去了上海。

这样终于告别了再阿里巴巴的外包工作,感叹他们在某些方面太严。

自己虽然不是个特别优秀的人才,但比他们招进来的一些人还是稍微强一些的,一切都是个杯具。。。一个大杯具

在这里要特别感谢冯婷,她给了我很多的帮助。她弟弟也为我做了件特殊的事情。

继续阅读 »