NodeJS探索
一、什么是NodeJS?
第一眼看到这个词,估计你和我一样,以为它不过是一个JavaScript框架吧?如果是,那么首先恭喜你,说明你的思维是正常的。其次鄙视你:凭什么带JS的就一定是JavaScript框架?你丫做技术的吧?最看不起你们这帮做技术的了,跟姓韩的那谁似的,没一点文化。
生活经验告诉我们,牛人往往都不怎么正常,正常人往往都成不了牛人。比如说NodeJS的发明者,这家伙就曾叫嚣说:其实JavaScript更适合做服务器端的并发编程。估计很多人听到这话都笑了,嗯,原谅我的可耻,我也笑了。令人欣慰的是上帝没有笑。当年上帝说要光,于是就有人脱了个精光。今天,上帝说我相信这个人能成,于是这个人还真成了,他弄了这么个东西:NodeJS--基于V8引擎的,使用事件驱动模型而不是复杂的多线程来获得可伸缩性,类似Ruby的EventMachine和Python的Twisted的轻量级WEBServer。
是不是很有意思?这意味着什么我想大伙都清楚吧?嗯,如果这东东真的可用,那就是说,以后前段后端都可以都用JavaScript来编程了,这对于JS程序员来说,真是一个莫大的福音,因为你们再也不用看着那帮做后台的孙子的脸色干活了,可以翻身做后端了。
好,废话到此,那接下来就请跟着我,开始NodeJS的探索之旅。
二、安装!
安装前先从NodeJS的官方网站下载其源代码:
- [root@www source]# wget http://s3.amazonaws.com/four.livejournal/20091028/node-v0.1.15.tar.gz
- [root@www source]# tar xzvf node-v0.1.15.tar.gz
- [root@www source]# cd node-v0.1.15
安装NodeJS很简单,一般情况下,Linux下常用的三步走即可。但需要注意的是:NodeJS需要2.3以上版本Python的支持,否则安装无法继续。
打开tools/waf-light,看到如下代码:
- #!/usr/bin/env python
- # encoding: utf-8
- # Thomas Nagy, 2005-2009
- # ...
- import os, sys
- if sys.hexversion<0x203000f: raise ImportError("Waf requires Python >= 2.3")
从以上代码我们知道,在安装脚本中,在Python脚本的头部指定了#!/usr/bin/env python,这就使得如果你的系统安装了自带的Python程序,则有可能由于版本不够高而无法安装。比如我的系统,是把Python2.5安装在了/usr/local/python252,同时未删除系统自带的低版本Python,此时如果configure,则会出错。
好吧,那我们建一个软连接:
- [root@www source]# sh
- [root@www source]# cd /usr/local/sbin/
- [root@www source]# ln -s /usr/local/python252/bin/python2.5 python
- [root@www source]# python
- [root@www source]# exit
此时执行的/usr/bin/env python,即可使用我们自己编译的Python。
PS.直接建软连接就可以了,这里之所以到sh环境下,是为了看一下sh环境。
执行configure,安装nodejs:
- [root@www source]# ./configure --prefix=/var/iapps/nodejs
- [root@www source]# make && make install
三、初次试用:
一切顺利,安装完成,写个脚本试试:
- #!/var/iapps/nodejs/bin/node
- var sys = require("/sys.js"), http = require("/http.js");
- http.createServer(function (req, res) {
- setTimeout(function () {
- res.sendHeader(200, {"Content-Type": "text/plain"});
- res.sendBody("Hello,This is Laohan(www.handaoliang.com)!");
- res.finish();
- }, 2000);
- }).listen(8000);
- sys.puts("Server running at http://127.0.0.1:8000/");
我相信,对于JavaScript程序员来说,这语法真是太熟悉了,除了第一、第二行之外。无论如何,我们要开始我们的梦想之旅了,那么接下来我们将example.js设置成可执行并运行它:
- [root@www source]# chmod +x example.js
- [root@www source]# ./example.js
此时屏幕显示:Server running at http://127.0.0.1:8000/
访问:http://127.0.0.1:8000/,如果看到页面输出:Hello,This is Laohan(www.handaoliang.com)!,那么恭喜你,安装成功了,接下来就需要发挥你自己的聪明才智,去实现各种强大的功能了。很激动人心吧?
后面将为大家深入分析NodeJS,敬请期待。
原创文章,转载请注明出处!
关于JS前端添加RSH会多出一个history的解决方案
在我负责的某一个项目当中,由于使用了大量的Ajax技术,而Ajax技术由于是异步请求,所以使得浏览器无法记录相应的History,这就意味着用户无法通过点击“前进”、“后退”按钮来进行操作,显然,从用户体验的角度来说,由于点击前进后退按钮是用户的习惯性操作,因此这是很不人性化的。
那么,为了规避用户体验不佳的问题,我们使用了Google的一个开源项目RSH来做为最终解决方案。
先介绍一下RSH,RSH是ReallySimpleHistory,是一个用于处理Ajax/DHTML应用程序的书签以及浏览器历史记录的轻量级的JavaScript库(Really Simple History is a lightweight JavaScript library for the management of bookmarking and browser history in Ajax/DHTML applications.)。它的原理是通过在URL上添加一串经过格式化的字符串,以使得每一次Ajax操作看起来都像是点击了不同的URL(实际上就是模拟点击URL的操作)。以此来达到用户可以通过点浏览器按钮来前进或者后退的目的。
RSH目前的最新版本是:0.6Final。项目主页在这里:http://code.google.com/p/reallysimplehistory/。
但是由于RSH在初始化的时候就会往URL后添加一个格式化的字符串,所以这就导致当用户进入到某一个页面时,明明是第一次访问,此时浏览器也会有一个history出现,此时点击“后退”按钮是会退回到一个空白页或者重复刷新一次。为了规避这个问题,通读了RSH的代码之后,在结合前端和后端的基础之上做了一点调整,最后问题完美解决。我这里后端用的是Python+Webpy,所以后端代码以Python代码为例。
现在的解决方案是,后台location到新的URL里,即往header里插入跳转的URL。然后客户端init的时候把这个事件插入hash表,这样就不会多出一个history来,且又能实现插入history的效果。
实现步骤:
1、首先是后台需要location到新的URL。
比如我现在开放给用户的访问链接是:http://www.youcompany.com,在webpy获取到用户请求的时候(GET),判断如果为首页(第一次访问,此时我这边是根据请求特性来判断的,即如果请求为空,则意味着是第一次访问),则跳转:
- def do_request(self,call_name,method):
- if not call_name:
- cur_urls = web.ctx.env.get("HTTP_X_FORWARDED_SERVER",None)
- self.urls = "".join(["http://",cur_urls,"/main/#my-index-page"])
- web.redirect(self.urls)
此时新的URL为:http://www.youcompany.com/main/#my-index-page,之后所有的请求都将发送到:http://www.youcompany.com/main/下面。此是history的记录为0。
2、客户端监听。
由于用户访问首页时,必须要添加一个监听才能使用RSH的方法,而RSH在添加监听函数的时候,相应的也会改变URL,这样还是会产生一个垃圾history,这就需要我们对RSH做一个修改。
在add方法里,加一个形参:is_hidden_location
- //---------------------原来:--------------------
- add: function(newLocation, historyData) {
- //---------------------修改后:--------------------
- add: function(newLocation, historyData, is_hidden_location) {
查找:window.location.hash,加一个判断:
- if(!is_hidden_location){
- window.location.hash = newLocation;
- }
在首页init添加监听的时候,为add方法加上一个参数:True,这样就不会添加路径到URL上,但不会影响RSH的history事件hash表。
自己写的一个JS遮罩类
在一些Ajax的页面中,我们经常能看到一些类似于Windows关机的效果,即是把界面上所有元素都灰度化,同时在最上面弹出一个带有颜色的对话框。貌似很酷的样子。
其实这在页面上很好实现,实际上只要初始化一个浮动层,通过JavaScript把这个浮动层拉到符合页面的宽度和高度,当需要弹出对话框时,只要使用JS去Display这个图层即可以了。当然,如果纯粹都用JavaScript来实现,必然会出现很多冗余的代码,因此在这个我基于Prototype写了一个遮罩类,用它可以有效的减化生成的过程:
- /**
- * Package: resource.web.javascript
- * FileName: cover_effect.js
- * Description: show cover effect.
- * Author: handaoliang<handaoliang@gmail.com>
- * Date: Oct 24,2008
- **/
- var cover_effect_conv_t = {
- version: "cover_effect Ver 1.0",
- create_date: "2008-10-24 15:12:00"
- };
- var readerCoverEffect = new Class.create();
- Object.extend(readerCoverEffect.prototype, {
- initialize: function(cover_element_id, bgcolor, color_deep){
- if (cover_element_id) {
- this.cover_eid = cover_element_id;
- }
- else {
- this.cover_eid = createRandElementID();
- }
- if (typeof bgcolor == "undefined") {
- this.bgcolor = "#333";
- }
- else {
- this.bgcolor = bgcolor;
- }
- if (typeof color_deep == "undefined") {
- this.color_deep = 4;
- }
- else {
- this.color_deep = color_deep;
- }
- },
- createCover: function(){
- //如果存在遮罩层,返回NULL。
- if ($(this.cover_eid)) {
- return;
- }
- //创建一个DIV,即遮罩层。
- this.cover_element = document.createElement("div");
- //为这个DIV设置一个ID。
- this.cover_element.setAttribute("id", this.cover_eid);
- Element.insert(document.body, {
- bottom: this.cover_element
- });
- Element.setStyle(this.cover_element, {
- position: "absolute",
- zIndex: 800,
- // "-moz-opacity":parseInt(this.color_deep)/10,
- // "FILTER": "alpha(opacity=40)",
- filter: "Alpha(opacity=" + parseInt(this.color_deep) * 10 + ")",
- opacity: parseInt(this.color_deep) / 10,
- // "-khtml-opacity": parseInt(this.color_deep)/10,
- // backgroundColor:this.bgcolor,
- backgroundColor: "#333333",
- left: "0px",
- top: "0px"
- });
- this.resizeConver();
- // 注册onresize事件
- Element.observe(window, "resize", this.resizeConver.bindAsEventListener(this));
- },
- destroyCover: function(){
- try {
- //找到元素
- var cover_element_obj = $(this.cover_eid);
- //如果此元素存在,则把它删除。
- if (cover_element_obj) {
- document.body.removeChild(cover_element_obj);
- }
- // 注销onresize事件
- Element.stopObserving(window, "resize", this.resizeConver.bindAsEventListener(this));
- /*
- //如果是IE,则需要恢复select和flash的显示。
- if(myBrowser.is_ie)
- {
- this.displayPeekElement();
- }
- */
- }
- catch (e) {
- dmsg(e);
- }
- },
- resizeConver: function(){
- var widow_size = getWindowDimensions();
- this.win_width = widow_size.win_width;
- this.win_height = widow_size.win_height;
- //设置CSS,使之具有遮罩的样式。
- Element.setStyle(this.cover_element, {
- width: this.win_width + "px",
- height: this.win_height + "px"
- });
- },
- //隐藏掉所有的select框
- hiddenPeekElement: function(){
- selects = document.getElementsByTagName("select");
- for (i = 0; i != selects.length; i++) {
- selects[i].style.display = "none";
- }
- },
- //显示所有的select框
- displayPeekElement: function(){
- //display select element
- selects = document.getElementsByTagName("select");
- for (i = 0; i != selects.length; i++) {
- selects[i].style.display = "inline";
- }
- }
- });
一点经验总结
写这样一段话,首先是因为看了这篇文章:http://eishn.blog.163.com/blog/static/65231820072574234114/。
出于和这篇文章作者相同的心理,本来想找一个zDaemon的替代品(之前大概读了一下zDaemon的代码,读得我实在晕),结果在这篇文章在发现一些值得我们学习的经验,尤其是:
1、Python 的语法决定了 Python 能够编写出目前最复杂的程序。代码量失控是 Python 的最大敌人, 这说明我们可能正在用 Java 写 Python。
2、过度向对象实际上就是完全无法重用, 这是一个典型的例子。
看到这些非常有感触,因为这些问题我都碰到过并且深深为之头疼,不仅仅是Python,任何面向对象的语言都不可避免的有此诟病,比如我之前曾经接手过一个项目,这个项目的极品之处就在于,把所有的操作都封闭在了一个JS文件中,最后导致这个JS文件,无论是代码的深度还是行数,都极为可观。而且之前封装的大部分看起来结构严谨的方法和类,基本上都无法重用,这主要原因就在于:当初在设计这些方法时,只考虑到了部分共性而没有考虑到大部分的无法共通的属性。加上命名空间的杂乱无章、规划无序,都导致维护以及后续开发的困难重重。
严重建议以后写代码(无论是JS还是Python):
1、尽量拆分(尤其是JS。Python还好办,按模块来做,个人觉得尽量不要一个文件里写多个类,行数太多非常不易于阅读理解。)。
2、除非是经验丰富,否则请尽量少用继承,个人觉得继承这个东西,如果没有完美的规划设计,只会添乱,尤其是在快速开发某个项目时,饥不择食的情况下根本没法考虑周全。
3、变量的命名尽量做得可区分。命名混乱会给维护造成很大的困难,经常会让你在读代码的过程当中,不知道这到底是一个字符串变量还是一个对象。

