开发模式
在前端开发中,一条很重要的优化原则就是减少HTTP请求数。但在实际开发中,有时候不得不用大量的异步请求。这里的异步请求所指的都是页面加载时的,之后用户操作过程中所发生的异步请求并不影响加载的速度。
在百度这边,由于FE需要写模板。所以很多时候有些功能如果有接口了,RD同学就要你使用异步接口。但异步接口使用多了就会影响页面的性能。比如说百度空间个人中心在页面加载时差不多要处理10个异步请求。这个数量是非常大的。如果减少这些异步请求的数量成为了优化的重点之重。
实现方案
目前想到一条比较可行的实现方案是通过一个公用的代理页面,前端在处理时将这些异步请求分组去处理。代理页面获取到打包的URL,然后服务器端去请求,最后将合并后的数据输出。
在服务器端去请求,由于数据是服务器对服务器的,数据网络传输时间可以忽略不计。

实现代码
- if(typeof Space == 'undefined') window.Space = {};
- Space.asynJs = function(){
- var _isStart = false,
- stack = [];
- function start(){
- if(_isStart){
- throw new Error('asynJs request is start.');
- return false;
- }
- _isStart = true;
- for(var i = 0, len = stack.length; i < len; i++){
- if(!stack[i]) continue;
- setTimeout(function(){
- //如果该组只有一条记录,就不用走通用代理了。
- if(stack[i].length == 1){
- baidu.sio.callByServer(decodeURIComponent(url));
- }else{
- var url = 'url[]=' + stack[i].join('&url[]=');
- baidu.page.loadJsFile('http://hi.baidu.com/st/asynproxy.php?' + url);
- }
- },0)
- }
- }
- function load(url, group){
- group = group | 0;
- url = encodeURIComponent(url);
- if(!stack[group]){
- stack[group] = [url];
- }else{
- stack[group].push(url);
- }
- }
- return {
- load:load,
- start:start
- }
- }()
该实现方式就是提供了2个方法load和start,load用于填充要请求的异步接口,start在页面底部启动请求。因为页面一般都分成头,中,尾3个部分,所以start方法可以在尾部文件里直接调用就可以了。
- Space.asynJs.load('http://pageurl1', 1)
- Space.asynJs.load('http://pageurl2', 1)
- Space.asynJs.load('http://pageurl3', 2)
- //在页面最底部启动请求
- Space.asynJs.start();
PHP端的实现
- <?php
- //url is array
- $urls = $_GET['url'];
- //经过白名单过滤后的URL
- $urls = apply_domain_filter($urls);
- //实例化Curl类
- $curl = new Curl();
- $contents = array();
- foreach($urls as $url){
- //需要配置对应URL的transmit
- //请求时带上传递过来的Cookie
- $contents[] = $curl->get($url, $_COOKIE);
- }
- echo join(';', $contents);
- ?>
额外的收益
目前一些框架(tangram)里domready时执行一些函数时并没有提供函数排序的功能,这样就不能实现重要的异步请求先加载不重要的请求后加载的功能。目前百度空间app平台这边实现方式是通过setTimeout延时来执行。如果设置的时间较短,各个浏览器表现的不一样。如果设置的时间比较长,有会影响页面加载的时间。
通过现在的这种方式后,不光实现了异步接口分组的功能,同时实现了重要的先加载,不重要的后加载。
先加载的东西只要第二个参数传值小一些就可以了。
风险
由于通用代理管理的是获取URL然后请求的工作,所以如果在后端不做一些判断的话可能引发一些风险,甚至XSS漏洞。目前想到的主要是2点。
1、通用代理要判断域,非白名单制定域不予请求
2、返回时必须设置header为application/javascript来阻止直接请求的XSS漏洞
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,带了媳妇去月陀岛,由于下雨的关系,玩的地方并不多。发几张照片纪念下。
昨天早上和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,具体实现如下:
- baidu.string.getByteLength = function (source) {
- return String(source).replace(/[^\x00-\xff]/g, "ci").length;
- };
- /*
- * Tangram
- * Copyright 2009 Baidu Inc. All rights reserved.
- *
- * path: baidu/string/subByte.js
- * author: dron, erik
- * version: 1.1.0
- * date: 2009/11/30
- */
- /**
- * 对目标字符串按gbk编码截取字节长度
- *
- * @param {string} source 目标字符串
- * @param {number} length 需要截取的字节长度
- * @return {string} 字符串截取结果
- */
- baidu.string.subByte = function (source, length) {
- source = String(source);
- var getLen = baidu.string.getByteLength, i, len, current, next, currentLen, nextLen;
- if (length < 0 || getLen(source) <= length) {
- return source;
- }
- len = source.length;
- for (i = Math.floor(length / 2) - 1; i < len; i++) {
- current = next || source.substr(0, i);
- currentLen = nextLen || getLen(current);
- if (currentLen == length) {
- return current;
- }
- else {
- next = source.substr(0, i + 1);
- nextLen = getLen(next);
- if (nextLen > length) {
- return current;
- }
- }
- }
- return source;
- };
baidu.string是字符串常见操作的对象。
正则替换
这种方式的实现原理是先将中文替换成中文加个空格,这样变相的将一个中文变成了2个字节,然后在这个基础上截取,截取完成后在将中文加空格变成中文。
- baidu.string.subByte1 = function(source, length){
- return (source+'').substr(0,length).replace(/([^\x00-\xff])/g,'$1 ').substr(0,length).replace(/([^\x00-\xff]) /g,'$1');
- }
这种实现方式不管是从代码量上还是从效率上都要比第一种高很多,这里有个测试案例。
在js中,创建一个类和实例化该类一般方式是:
- var cls = function(){}
- cls.prototype = {
- attr:'',
- method:function(){}
- }
- var clsInstance = new cls;
这种方式简单明了,但如果类很多的话就比较痛苦了,并且代码看起来不够优化。
动态创建类
动态创建类实际上类似于一种代理的模式,代码如下:
- var Fath = function(methods){
- var cls = function(){
- return new fn(arguments);
- },
- fn = function(args){
- return this.init && this.init.apply && this.init.apply(this,args);
- };
- fn.prototype = cls.prototype = methods || {};
- try{
- return cls;
- }finally{
- cls = null;
- }
- }
创建一个类:
var TestClass = Fath(methods) //这里的methods是方法或者属性集合,是一个静态对象
实例化该类:
var testInstance = TestClass(params) //这里的params是传进去的形参
通过这种方式后,一个好处就是实例化类的时候不用再使用new了,当然使用new也是可以的,作用的等价的。避免了到处是new和prototype的好处。
性能上在1K数量级上没有很大的区别。
在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马上就要过去,2010马上就要开始。在展望新的一年的同时,回顾下我在2009年的变化。
一切从年初开始说起。。。
杭州的故事
继去年的惨淡经历后,混迹于杭州,继续在阿里巴巴做前端开发外包工作。
但心里暗下决心,2009我一定要改变什么,不然就废弃了,到了2010估计就没奋斗的决心了。
但由于某些方面的特殊性,又不能太果断的换工作,对我来说找一份新的工作太难了。
虽然每天上下班要在公交车了呆4个小时,但始终疲惫的坚持着。
6月份获知阿里巴巴要在8月初搬到滨江的新大楼。
这样我上班就更远了,给我一种换工作的准备。
正好在blueidea看到上海的一个外企在招前端开发工程师的职位,想到外企应该在某些方面不是很严,于是乎就投了简历。
今天下来就收到了offer,一个月后告别杭州,去了上海。
这样终于告别了再阿里巴巴的外包工作,感叹他们在某些方面太严。
自己虽然不是个特别优秀的人才,但比他们招进来的一些人还是稍微强一些的,一切都是个杯具。。。一个大杯具
在这里要特别感谢冯婷,她给了我很多的帮助。她弟弟也为我做了件特殊的事情。
继续阅读 »