主题名称: wSpring 系列 Hoar-frost Falls / 霜降
主题链接: https://yimity.com/2013/03/20/wordpress-free-theme-hoar-frost-falls.html
主题描述: Hoar-frost Falls. 单栏主题,支持后台选项,包括自定义站点统计,支持评论框右侧 125*125 像素的广告,jiathis 分享功能,SEO 友好,支持自定义菜单,支持IE8以上,及 Chrome,Firefox,Opera等现代浏览器。不需要任何插件支持。
主题版本: 1.1.1
主题作者: 一米
发布日期:03月20日。
主题预览:https://yimity.com/
介绍:
iSimple. 单栏主题,支持后台选项,包括自定义站点统计,支持评论框右侧 125*125 像素的广告,jiathis 分享功能,SEO 友好,支持自定义菜单,支持IE8以上,及 Chrome,Firefox,Opera等现代浏览器。不需要任何插件支持。即使 JS 加载失败,功能同样正常可用。文章样式已经定义好,几乎所有的文章发表即可正常显示。支持 10 种文章格式。全站 placeholder 支持。极度个性化的 404 页面。

后续升级计划:
优化响应式设计。
优化代码。

主题演示:貌似没有哦,先看图片吧。
阅读更多...

回调与作用域

在前面的例子中,执行回调的函数一般都如下所示:

1
    a(b);

而回调一般都如下所示:

1
    b(parameters);

这在一般情况下都可以正常的工作,但在某些时候,回调并不仅仅是单一的函数,而是某个对象的方法,并且如果该回调使用 this 来引用其所属的对象的话,就会导致错误发生。

假如有一个 add 的回调函数,它是 app 对象的一个方法:

1
2
3
4
5
	var app = {};  
	app.number = 1;  
	app.add = function (n) {  
		n = n + this.number;  
	};

makeArray 函数做了类似下面的事:

1
2
3
4
5
6
7
var makeArray = function (callback) {  
	// ...  
	if (typeof callback === "function") {  
		callback(i);  
	}  
	// ...  
};

调用

1
makeArray(app.add);

当调用的时候,它并不能按照预期的那样工作,因为 this.number 将会是 undefined。这里 this 将会指向全局对象,因为 makeArray 是一个全局函数。

如果 makeArray 是某个对象的方法,那么在回调函数中的 this 将会指向这个对象而不是期望的 app。(详情请了解下 javascript 中的 this)

解决这个问题的方法就是传递一个回调函数,此外再传递这个回调函数属于的对象作为一个参数:

1
makeArray(app.add, app);

紧跟着,我们需要去修改 makeArray 去绑定(bind)传递进来的对象:

1
2
3
4
5
6
7
var makeArray = function (callback, callback_obj) {  
	//...  
	if (typeof callback === "function") {  
		callback.call(callback_obj, i);  
	}  
	// ...  
};

对于传递一个对象和一个被用来回调的方法,另一个可选的方法就是将方法作为字符串传递,那么你就不会重复对象两次。换言之:

1
makeArray(app.add, app);

会变成:

1
makeArray("add", app);

那么 makeArray 将会变为如下面几行所示:

1
2
3
4
5
6
7
8
9
10
var makeArray = function (callback, callback_obj) {  
	if (typeof callback === "string") {  
		callback = callback_obj[callback];  
	}  
	//...  
	if (typeof callback === "function") {  
		callback.call(callback_obj, i);  
	}  
	// ...  
};

匿名的事件监听器

回调模式在日常中被经常使用,比如,当你附加一个事件监听器给页面上的某个元素时,你实际上提供了一个指向了回调函数的引用,并且在事件发生时被调用。

这里有个例子,怎么将 console.log() 作为一个回调函数监听文档的 click 事件:

1
	document.addEventListener("click", console.log, false);

绝大部分客户端浏览器都是事件驱动的(event-driven),当一个页面加载完成,会触发load事件,然后用户可以通过和页面交互触发各种各样的事件,比如:click, keypress, mouseover, mousemove等等,因为回调模式,JavaScript特别适合事件驱动编程,能让你的程序异步的工作,换言之,就是不受顺序限制。

在异步的事件驱动的JavaScript,可以提供一个回调函数用于在正确的时候被调用;有时候甚至可能提供比实际请求还要多的回调函数,因为某些事件可能不会发生,比如:如果用户不点击“购买”按钮,那么你用于验证表单格式的函数永远不会被调用。

定时

另一个使用回调模式的例子就是使用浏览器的window对象的setTimeout()和setInterval()方法,这些方法也可以接受和执行回调函数:

1
2
3
4
	var thePlotThickens = function () {  
		console.log('500ms later...');  
	};  
	setTimeout(thePlotThickens, 500);

再次注意一下,thePlotThickens 是如何被作为一个参数传递的,注意这里仍旧没有使用括号,因为你不想它立即执行,传递字符串 "thePlotThickens()" 取代函数的引用和eval()类似,是不好的模式。

当然可以提供匿名函数的方式进行调用。

1
	setTimeout(function(){thePlotThickens()}, 500);

回调

在 javascript 中,函数都是对象,这也就表明了他们也可以被作为参数,从而传递给其他函数。例如,将函数 a() 作为参数传递给 b() 时,那么,在某一时刻 b() 可能会执行或者说调用 a(); 在这种情况下,a() 就被称作毁掉函数(callback function),也可简称为回调(callback)。

代码示例:

1
2
3
4
5
6
7
8
9
10
    function b(callback) {
        // do something
        callback();
    }
 
    function a() {
        // do something
    }
 
    b(a);

这里需要注意的是,在 b(a) 中,作为参数的 a 函数,是不带括号的,括号表示要执行(此函数),而此处只需要将 a 函数传递给 b 函数,让 b 函数在适当的时候,加上括号调用它即可。

注:可以使用 火狐,或者谷歌的 javascript 控制台,如果调用 console.log(a) 的话,就会得到 a 的函数的定义代码,而前面说了括号表示要执行,所以加上括号之后,表示要执行 a 函数的代码,a 其实只是一个名字而已。

代码改写示例

首先不使用回调的方式来定义两个函数,然后使用回调的方式重构这两个函数。

使函数具有通用的,单一功能的行为,这是非常好的思想,可以对函数的功能一目了然,阅读和修改也更方便。下面两个函数都是用这种思想来定义。

makeArray 函数用来生成一个大数组,并且返回这个大数组。

1
2
3
4
5
6
7
8
9
    var makeArray = function () {
        var i     = 100000,
            array = [];
        while (i) {
            i --;
            array.push(i);
        } 
        return array;
    }

arrayPlus 函数的功能只对数组的元素进行处理。

1
2
3
4
5
6
7
    var arrayPlus = function (array) {
        var i   = 0,
            max = array.length;
        for(; i < max; i++) {
            console.log(array[i] + 1);
        }
    }

执行这两个函数

1
    arrayPlus(makeArray());  // 生成数组,并给所有的数字元素 +1 再返回此数组

函数达到了我们的目的,但是对于客户端 javascript 来说,性能在一定程度上也是至关重要的,但是上面的实现是比较低效率的,因为生成数组的 makeArray 有一遍大循环,而处理数组元素的 arrayPlus 函数,做了同样的一遍大循环。如果将 makeArray 中的 array.push(i); 改为 array.push(i+1); 即可,这样是高效的,但是这样生成数组和处理数组高度耦合,其并不再是一个通用的具有单一功能的函数,不符合我们的思想。

那么我们尝试使用回调的方式来重构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    var makeArray = function (callback) {
        var i     = 100000,
            array = [];
        while (i) {
            i --;
            if(typeof callback === 'function'){
                callback(i);
            }
            array.push(i);
        } 
        return array;
    }
 
    var arrayPlus = function (n) {
        console.log(++n);
    }

重构之后的代码,makeArray 唯一额外的任务是要检查是否有提供回调函数,如果有则执行,如果没有则不执行,并没有破坏原来 makeArray 函数的功能,makeArray 仍然可以像以前一样使用。而 arrayPlus 则变得更加简单。

调用,执行即可

1
    makeArray(arrayPlus);  // 注意和重构前的调用对比

对于回调函数来说,可以是已经定义好的函数,也可以是匿名函数,在调用主函数时创建回调函数,例如:

1
2
3
    makeArray(function(n){
       console.log(++n); 
    });

主题名称: wSpring 系列 Insects Awaken / 惊蛰
主题链接: https://yimity.com/2013/03/15/wordpress-free-theme-insects-awaken.html
主题描述: Insects Awaken. 单栏主题,支持后台选项,包括自定义站点统计,页面上部背景自主更换,是否显示头像以及阅读模式和浏览模式的切换,SEO 友好,支持自定义菜单,支持IE8以上,及 Chrome,Firefox,Opera等现代浏览器。不需要任何插件支持。
主题版本: 1.0
主题作者: 一米
发布日期:03月15日。
主题预览:https://yimity.com/
灵感来源:http://www.diandian.com/themes/110/show 咖啡因折页,感谢。
介绍:
Insects Awaken. 单栏主题,支持后台选项,包括自定义站点统计,页面上部背景自主更换,是否显示头像以及阅读模式和浏览模式的切换,SEO 友好,支持自定义菜单,支持IE8以上,及 Chrome,Firefox,Opera等现代浏览器。不需要任何插件支持。即使 JS 加载失败,功能同样正常可用。文章样式已经定义好,几乎所有的文章发表即可正常显示。支持 10 种文章格式。只支持多说评论系统。请使用多说的 WordPress 插件,后台有说明。全站 placeholder 支持。极度个性化的 404 页面。

阅读更多...

接上一篇

1. 进入 SVN 的 hooks 目录

1
cd /home/svn/node/hooks

2. 新建post-commit文件

3. 输入以下内容

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
REPOS=$1
REV=$2
time=`date "+%Y-%m-%d %H:%M:%S"`
export LANG=C.UTF-8
 
# 当用户提交的时候注释里包含 auto_deploy 字符串的时候才发布到 web 目录
if (/usr/local/svn/bin/svnlook log -r $REV /home/svn/node/ | grep "auto_deploy")
then
echo "start deploy $time" >> /tmp/svn_autocommit.log
/usr/local/svn/bin/svn export --username yimity --password ******* "svn://127.0.0.1" /home/wwwroot/ajax --force --no-auth-cache
fi

为了不使每次提交之后都发布,所以判断下 只有在提交的注释中包含 auto_deploy 时,才发布。
注意其中的 /usr/local/svn/bin/ 和 /home/svn/node/ 以及 /home/wwwroot/ajax 这三个路径,第一个是安装 svn 的路径,上一篇有讲,第二个是 svn 的版本库目录,第三个是网站服务器目录,至于 svn://127.0.0.1 这个则是 svn 的访问目录了,也可以是外部地址,即:工作电脑获取的地址。
注意 export LANG=C.UTF-8 请看问题 2 字符集。

4. 修改权限

1
chmod +x post-commit

保存,在本地修改保存提交试试看。

出现错误:
1. Store password unencrypted
进入

1
vi /home/root/.subversion/servers

将所有的

1
# store-plaintext-passwords = no

改成

1
# store-plaintext-passwords = yes

即可

2. 字符集
使用

1
locale -a

查看系统字符集,如我的 VPS 只有

1
2
3
C
C.UTF-8
POSIX

所以设置成 zh_CN.UTF-8 就会出错

然后设置 3 中的

1
export LANG=C.UTF-8

修改为

1
export LANG=系统列出来的字符集的名字

3. 权限
如果新建了不同的组和用户(非 root),权限问题的话,肯定应该会有,比较复杂,这里不多说了。

参考:http://blog.51yip.com/server/901.html 以及 http://www.wp1998.net/2012/834.html

此处所说的独立,是指不结合 apache,也就是你的 ubuntu 可以不安装 apache ,然后独立安装这个 Subversion。

虽然说某些事情有前奏非常好,但是咱们这里就不要前奏了,直接进入主题吧。

1. 下载必要的软件
这里以临时目录为基础。
进入临时目录

1
cd /tmp

然后下载需要的软件

1
2
wget http://subversion.tigris.org/downloads/subversion-1.6.1.tar.gz
wget http://subversion.tigris.org/downloads/subversion-deps-1.6.1.tar.gz

(上面二个压缩文件解压后会放到同一个文件下,不要另建文件夹,这一句可忽略)

2. 安装
解压

1
2
tar zxvf subversion-1.6.1.tar.gz
tar zxvf subversion-deps-1.6.1.tar.gz

进入目录,编译安装,视服务器性能需要比较长的时间,可以使用 screen 命令

1
2
3
cd subversion-1.6.1/
./configure --prefix=/usr/local/svn
make && make install

此处将 svn 安装到了 /usr/local/svn 目录。

3. 查看 svn 信息

1
2
3
# /usr/local/svn/bin/svnserve --version
svnserve, version 1.6.1 (r37116)
compiled Jul  7 2010, 23:06:21

如果出现类似 version 1.6.1 的信息,说明安装成功了。

4. 将 svn 的 bin 目录加到环境变量中去

1
2
# PATH=$PATH:/usr/local/svn/bin
# export PATH

通过键入 svn 查看是否成功

1
2
# svn
Type 'svn help' for usage.

出现这样的字样,就说明成功了

5. 建立仓库
5.1 建个 svn 的根目录,因为项目不只一个,默认放到 home 目录中

1
# mkdir -p /home/svn    #-p 的意思是说如果没有父目录建之

5.2 建个仓库

1
2
# mkdir -p /home/svn/node
# svnadmin create /home/svn/node 或者 # svnadmin create /home/svn 记不清楚了

5.3 导入数据

1
2
3
# svn import ./svntest file:///home/svn/node -m "Initial repository test"
Adding         svntest/test.php
Committed revision 1.

./svntest 为要导入的目录,里面还有相应的项目文件此处使用 test.php 测试。如果出现如上例代码中的字样,则表示导入数据成功

6. 配置
6.1 修改 svnserve.conf(/home/svn/node/conf)

1
2
3
4
5
# vi svnserve.conf
[general]
anon-access = none
auth-access = write
password-db = passwd

6.2 修改用户密码文件 passwd

1
2
3
# vi passwd
[users]
yimity = ******(密码)

vi 的使用这里就不说了,i 进入编辑模式,修改完成后按 esc 进入一般模式,然后 :wq 保存退出。

7. 启动

1
# svnserve -d -r /home/svn/node

然后在本地安装 svn 客户端,获取就可以了。

参考:linux svn安装和配置,不结合apache

  在使用 jQuery 开发了很多网站之后,对原生的 JS 是越来与生疏了,但是对于任何一门语言来说,框架只会让事情事半功倍,但归根结底,还是语言原生的特性是根本。
  对于JS来说,基于浏览器这个宿主环境,其有很多尴尬的地方,例如兼容性不好,对于多种浏览器来实现兼容,导致代码很臃肿;原生的很多方法属性使用十分不便捷等。但对于一门语言要想深入,掌握其原生的特性还是十分必要的,毕竟相对于 jQuery 这种类库或者框架来说,要知其然,还要知其所以然。
  来自维基百科的关于AJAX 的解释是:Asynchronous JavaScript and XML(异步的JavaScript和XML技术,但其实AJAX的实现不一定和 XML 有关系,也即AJAX和数据格式无关),是一种创建交互式网页应用的网页开发技术,是多种技术的组合。目的就是让前台的数据交互变得更快捷,不用刷新页面就可以完成数据的更新。
  AJAX 的优点很明显,利于用户体验,在不刷新页面的情况下利用DOM交互可以更新页面内容,不会打断用户的操作,由于其获取和发送的数据量小的特点(获取数据格式可以使XML或者JSON或者其他,不是整个页面),减小服务器压力也是它的一个优点。
  而缺点,就是倍受人们关注的SEO问题(Google使用#!这种规定来支持AJAX的SEO,但是百度貌似还没有支持,所以做互联网的人,如果用百度的话,我劝你趁早转行,不要告诉我不稳定,不会那啥),前进后退问题(可以用其他方法解决HMTL5新增了相应的API),以及必须重视的安全问题等都是它现存的一些缺点。
  而AJAX 的应用范围,这其实这是一个太宽泛的问题,各人及各项目都有不同的用处,只要是能够提升用户体验的地方,都可以使用AJAX,但是对于什么是用户体验,又是一个写几本书都写不完的东西了。举例子来说,对于Gmail这类应用,其实可以看做SPA(单页面应用, Single Page Application)应用,所以整站都不需要刷新,而对于G+ 这种网站来说,虽然可以不整站刷新,但是整站刷新其实并没有什么不妥的地方,而不整站刷新,反而带来了页面假死的现象。据说知乎内部也有类似于G+的项目,但是我觉得这种应用,对于知乎来说,其实并不一定适合,在越来越快的网速这种情况下,其实还是整个页面刷新来的舒服,当然了,这个只是个人观点。而对于小面积更新数据来说,比如对用户名或者邮箱唯一性(输入框失去焦点后)等数据判断、选项卡(选项卡切换AJAX加载)、弹出的登录注册窗口(可以AJAX返回HTML结构)以及用户提交表单信息后的反馈信息等等,这种形式确实非常合适的。
  来了这么多开胃菜,其实很多人已经烦了,那么我们就开始来点干货,下面将通过一个新用户注册的过程来浅尝辄止的讲解下AJAX的基础知识。
  注册表单要求:用户名为邮箱,唯一性,密码必须大于六位,为了简单起见,这里不做过多合法性验证。
  AJAX出现之前,如果要注册新用户的话,用户必须填写完所有必填信息,然后单击提交按钮,将数据提交到服务器之后,才能验证数据的合法性,例如验证邮箱唯一性,提交表单之后,必须经过服务器判断邮箱的唯一性,然后刷新页面在显示此邮箱是否是唯一的,可用来注册的(其实还可以用隐藏iframe来实现)。而有了AJAX之后,当用户将邮箱输入完成,此输入框失去焦点的时候,通过AJAX查询服务器,然后给出邮箱是否是唯一的结果,虽然也经过了服务器的交互,但是页面没有刷新,并且交互的数据量非常小,发送的数据除了必要的头信息就是查询数据,而接受的数据除了头信息之外,甚至可以减少到只用数字0或者1来表示数据是否合法。
  来看HTML结构代码:

1
2
3
4
5
<form id="register" method="POST" action="register.php">
    <label>邮 箱:<input type="text" id="email" placeholder="请输入邮箱地址"></label>
    <label>密 码:<input type="password" id="password" placeholder="请输入密码"></label>
    <input type="submit" value="提交">
</form>

  除了表单页面之外,还需要有一个后端页面register.php来负责验证数据的合法性以及注册过程,这里是后端页面,就不给出具体代码了。
  接下来就交给AJAX来实现了,当邮箱输入框失去焦点时使用 GET 方式来和后台交互以验证此邮箱的唯一性。
  而当点击提交按钮时,使用 POST 方式来和后台交互,以验证是否能够注册成功。
  除了HTML和PHP之外,接下来就是AJAX发挥作用的时候了。
  提到AJAX,就不得不提XMLHttpRequest(XHR) 对象,它是W3C的一个标准,也是AJAX最核心也是最底层的对象,但是悲剧的是IE系列,直到IE7+ 才实现了W3C的标准XHR对象,但是我们还是要支持IE6的啊(万恶的IE6,这里不得不说,其实很多地方都误解了微软,在IE5中微软就实现了XHR对象,后来各个浏览器厂商才逐渐的实现此对象,只不过IE5/IE6是按微软自己的方式来实现的,使用的是ActiveX对象),所以不得不做兼容性处理。
  为了兼容IE6,那我们就写兼容性的代码吧。

1
2
3
4
5
6
7
8
9
function creatXHR(){
    if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
    } else if(window.ActiveXObject) {
        return new ActiveXObject("Microsoft.XMLHTTP");
    } else {
  	alert(‘此浏览器不支持 XHR !’);
    }
}

  使用此代码来创建一个创建兼容XHR对象的函数,第二行先判断window是否有XMLHttpRequest这个属性,如果有则证明是支持W3C标准的XHR对象,否则就是IE6了(已经可以不用考虑其他浏览器了),使用微软的方式来创建XHR对象,所幸的是其他浏览器的实现和IE早期的实现在内部是兼容的,W3C也兼容(其实W3C的很多标准都是采纳于各个浏览器已经实现的事实标准)。
  我们先来想想数据验证的过程,获取数据,创建XHR对象,然后使用XHR对象的方法来与服务器交互。先不管具体的交互过程,不管是数据验证还是注册,都是这么三步,那么接下来就实现另外的函数。
  发送数据并验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function validateData(obj,method,url,data){
    var xhr      = creatXHR(),
        iscancel = setTimeout(function(){xhr.abort();console.log("超时取消!");},5000); // 设定超时取消
 
    xhr.onreadystatechange = function() {  //绑定事件
        if (xhr.readyState == 4 && xhr.status ==200){ //此时数据可用
 
            var res = (window.JSON)?(JSON.parse(xhr.responseText)):(eval("("+xhr.responseText+")"));//解析 json字符串
 
            obj.nextSibling.innerHTML = res.msg;
 
            if (res.code == 0 || res.code == 2) {
	        obj.nextSibling.style.color = 'red';
	    } else {
		obj.nextSibling.style.color = '#5B636A';
	    };
 
	    clearTimeout(iscancel);//如果响应成功,取消定时函数
 
        }
    }
 
    //检测 Method 方法,GET POST
    if (method.toLowerCase() === "get") {
        xhr.open("GET",modiUrl(url,data),true);
	xhr.send(null);
    } else if (method.toLowerCase() === "post"){
	xhr.open("POST",url,true);
	xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
	xhr.send(serialize(data));
    };		
}

  然后来详细阐述一下这个xhr对象的相关内容(获取输入数据就是很正常的获取数据的过程,这里不多说了)。
  对于vliData() 函数中的XHR而言(并不只是此函数中,所有的XHR对象都一样,后面相同,此处只是为了好说明),要使用XHR对象时,必须调用的第一个方法是open(),即:xhr.open();此方法需要接收三个参数:
  第一个参数是要发送的请求的类型(GET POST等),
  第二个参数即请求的 URL,GET请求可以将查询参数追加到URL的末尾,但是每个参数的名值对必须经过encodeURIComponent() 编码才可使用,不然容易发送错误(虽然一般情况下参数的名称都为拼音或者英文,但是还是应该养成每次编码的好习惯)。
  第三个参数即表示是否是异步发送请求,布尔值,true或者false。上例中已有使用,即:

1
xhr.open("GET","register.php?"+encodeURIComponent('email')+"="+ encodeURIComponent(email),true);

  这里使用了 get 方法,异步获取register.php 这个url的数据(url必须同域同端口同协议,否则报安全错误,可以绝对路径或者相对路径)。但是当调用open() 方法的时候并不会真正的发送请求,而是启动一个请求以备发送。而要发送请求,就必须调用send()方法才行。例如上面的代码所示,即:

1
xhr.send(null);

  这里的send()必须接受一个参数,即:要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入 null ,因为不传入的话,有些浏览器会报错。
  好了,数据发送完毕了。
  数据发送完毕,等到服务器处理完毕并返回,浏览器收到响应的时候,相应的数据就会自动填充XHR对象的相应属性。XHR对象的相应属性主要包括以下几种。
  responseText : 作为响应主体被返回的文本。
  responseXML : 如果响应的内容类型是text/xml 或者 application/xml 的时候,这个属性保存这响应数据的XML DOC 文档。
  status : 响应的http状态。
  statusText : http状态的说明
  在接收到响应之后,第一步就是要检查 status 属性,以确定响应已经成功返回,一般来说,可以将http的状态码为200时作为成功的标志(其实还有304等),此时responseText 的内容已经就绪,如果内容类型正确的话,responseXML 也已经可以访问。
  但是我们这里使用的是异步请求,不像同步请求那样会将JS执行阻塞,当收到响应后才会执行后面的代码。因此要通过事件来确定XHR的状态,因为XHR对象的open() 或者 send() 方法被调用之后,并不清楚XHR 到达什么状态了,所以还要判断 XHR 的状态决定之后该如何处理。
  对于 XHR 状态的判断,可以检测 XHR 对象的 readyState 属性,该属性表示 XHR 对象请求/响应过程的当前活动阶段,可取值如下:
  0 : 未初始化,即尚未调用 open() 方法,
  1 : 启动,已经调用open() 方法,但尚未调用 send() 方法,
  2 : 发送,已经调用send() 方法,但尚未接收到响应,
  3 : 接收,已经接收到部分响应数据,
  4 : 完成,已经接收到全部响应数据,即可以在客户端使用数据了。
  同时,XHR 对象还有一个事件readystatechange ,而readyState 属性的值每变化一次,都会触发一次readystatechange 事件,因此可以通过检测此事件每次触发时readyState属性的值,一般情况下,我们只对readyState的值为 4 时的情况感兴趣,因为此时所有的数据已经就绪可用,不过为了确保浏览器的兼容性,必须在调用 open() 之前指定readystatechange事件处理程序。如上代码所示。

1
2
3
4
5
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status ==200){
      alert(xhr.responseText);
  }
}

  在onreadystatechange事件每次触发时,检测readyState的值,若为 4 ,且status 为200(http状态,前面已说明)时才可以使用此数据。
  另外,在接收到响应之前,还可以使用 abort() 来取消异步请求。例如当请求相应时间超长时,可以调用此方法,然后将其异步请求取消。如:

1
2
3
4
5
6
7
var iscancel = setTimeout(function(){xhr.abort();},5000);
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status ==200){
        alert(xhr.responseText);
  	clearTimeout(iscancel);
  }
}

  即可在超过5秒钟未收到响应,由于超时而取消本次请求。
  如果请求发送成功,并且也接收成功的话,responseXML或者responseText也就可用了,此时可以按返回不同的数据来进行处理。目前为了开发简便起见,一般返回的就是html结构或者json数据,关于数据的处理和使用,这里就不涉及了。
  对于 HTTP 来说,每个请求和响应都会包含相应的头部信息,有的有用,有的没用,同时 XHR 也提供了相应的方法来操作这两种头部的信息,因为其用处不是非常大,所以这里就不再展开了(不过有时候可以通过自定义的头部信息加上后端的配合来实现一些额外的功能),只是在使用 POST 方式请求的情况下,需要用到setRequestHeader() 方法。
  以上就是一个完整的利用原生的 JS 来写的GET 方式下的 AJAX请求,而POST 方式下的请求我们还没有接触到。那么我们下面就开始阐述一下POST方式下原生 JS 的AJAX请求的实现方法。
  前面已经说过,XHR对象调用的第一个方法是open(),open() 方法的第一个参数是请求类型,所以POST 方式的 open() 方法即为:

1
xhr.open("POST","register.php,true);

  而第二步就是将发送的数据通过 send() 方法发送出去(前面已经讲过)。但是服务器端对于提交的web表单必须经过处理,因为对于POST提交的非表单数据,其处理方式是不同的,也就是后端程序必须要读取前端发过来的原始表单数据,并从中解析出有用的部分,因此我们必须通过XHR模拟表单数据的提交,这也就用到了自定义头部信息。为了保证兼容性,使自定义头部发送成功,必须在open() 方法之后,在 send() 方法之前,设定自定义头部(如果不设定自定义头部的话后端是否可以获取提交的数据呢,答案是可以,但是就不能使用$_POST这种超级全局变量,而只能通过$HTTP_RAW_POST_DATA来获取了,此处基于PHP而言)。

1
2
3
xhr.open("POST","register.php,true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(data);

  自定义头部的意思就是表单提交时的内容类型。
  然后就是要发送的数据了,因为最初XHR设计主要是为了处理 XML,所以这里可以传入XML DOM文档,传入的文档在经过序列化之后作为请求主体被提交到服务器,当然了,这里也可以传入任何想发送到服务器的字符串。我们这里使用提交字符串方式。
  由于 POST 数据的格式与查询时(GET)字符串格式相同,所以只需要将表单中的数据进行序列化,然后通过XHR发送到服务器即可。

1
2
3
4
5
6
7
8
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status ==200){
        alert(xhr.responseText);
    }
}
xhr.open("POST","register.php,true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(data);

  然后就是对表单数据的序列化了。其实和GET数据格式相同,即email=a@b.com&site=yimity.com,但是要编码。这里就不多做讨论了。
  最终的代码为:

1
2
3
4
5
6
7
8
9
var data = "email=yimity%40gmail.com&password=123456";
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status ==200){
        alert(xhr.responseText);
    }
}
xhr.open("POST","register.php,true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(data);

  
  通过一个例子,将AJAX最基本的 GET 和POST 讲清楚了,其实AJAX还可以用来上传文件,不过这都需要支持HTML5的浏览器才可实现(隐藏的iframe也可以实现,但不属于纯AJAX了),HTML5 中的 XMLHttpRequest 2 级实现了很多XMLHttpRequest 1级中的细节,并且实现了很多非常有用的API,例如文件上传, FormData 等,但是这些不属于基础的AJAX交互范畴,所以这里就不涉及了。
  
  打完收工,其实代码这东西,还是例子最重要,不过我的代码很丑。点此查看

最近开始学习 Nodejs ,但是机子上已经有了 apache ,所以为了跑 Node ,要不就是停用 Apache 以使 Node 可以使用 80 端口,要不就是给 Node 使用另外的端口号,例如:8080,虽然,在本机上可以这么访问,但是如果放在公网上,让用户可以访问的话,如果不使用 80 端口,看起来总是不像一个可以实际使用的网站。可是如果 Node 使用 80 端口的话,就只能停掉 Apache 的网站,两者不能兼顾。那么有没有方法可以使 Node 和 Apache 在一个服务器上同时提供服务,并且都只使用域名而不用加端口号访问呢?答案是肯定的!

那么就来看,如何实现:

1. 首先,在 Apache 的配置文件中,打开 mod_proxy 和 mod_proxy_http 至于其他的自己看着打开就好,例如需要 FTP 的话就打开相应的 FTP 选项。即:去掉 httpd.conf 中的

1
2
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

这两行前面的 # 号即可。

2. 配置虚拟主机,增加下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<VirtualHost *:80>
  ServerName node.js
  ServerAlias www.node.js
 
  ProxyRequests off
 
  <Proxy *>
    Order deny,allow
    Allow from all
  </Proxy>
 
  <Location />
    ProxyPass http://localhost:8080/
    ProxyPassReverse http://localhost:8080/
  </Location>
</VirtualHost>

此处 node.js 和 www.node.js 为绑定的要访问 Node 服务的域名。http://localhost:8080/ 为服务器上访问 Node 的网址和端口号,这里以 8080 为例。

3. 打开你的 Node 服务,然后重启 Apache ,访问 node.js 这时候你就可以看到用 Node 提供服务的网站了。

其实原理就是使用 Apache 做代理服务器而已。

如果使用的是 Nignx 的话,也可以用同样的原理来实现,但是我只用了 Apache ,就没有尝试着去做 Nignx。当然了,其他服务器也一样。

对于大名鼎鼎的 ZenCoding 来说,做前端的同学没有没听过,没用过的,如果你没用过的话,那么你就太 Out 了,不过以后如果没有用过的话,那也不算什么。

以为 ZenCoding 宣布停止更新了。

话说 ZenCoding 确实挺好用的,自从换了 SubLime Text 2 之后,加上这个 ZenCoding 写前端代码如飞一般的流畅,但是今天像往常一样打开 S 之后,却多出了一个关于 Package 的标签页,说的就是 ZenCoding 停止更新的事情。

不过不用担心,在 ZenCoding 停止更新之后,还有另一个项目上线了,貌似其实只是 ZenCoding 改了个名字的项目,那就是 Emmet。

在这个标签页中,ZenCoding 说了不但换了名字,而且功能有了一定的增强。反正就是比之前的强大了不少。

对于 Sublime Text 2 来说,直接在安装包管理器中安装就可以了。不过要先卸载掉 ZenCoding。

1. Ctrl + Shift + P

2. Remove Package

3. ZenCoding

4. 重复 1

5. Install Package

6. Emmet

回车,就会自动安装好的。使用方式我还没有试过呢,应该和 ZenCoding 一样一样的。

对了,新项目主页:http://docs.emmet.io

貌似我也应该弄一个这个个性的域名?!