“Nodejs”的版本间的差异

来自tomtalk
跳转至: 导航搜索
怎样让express自动重新加载修改过的js文件?
Tom讨论 | 贡献
库、框架
 
第2行: 第2行:
 
==库、框架==
 
==库、框架==
 
<div id="indexContent">
 
<div id="indexContent">
[[express.js]]
+
[[express.js]] [[ECMAScript]]
 
</div>
 
</div>
  

2019年8月12日 (一) 12:05的最后版本

库、框架

How to update node.js?

#Clear NPM's cache:
sudo npm cache clean -f
#Install a little helper called 'n'
sudo npm install -g n
#Install latest stable NodeJS version
sudo n stable

node.js介绍

相关资源

NodeJs中的非阻塞方法

via 博客园_首页 by rainsilence无痕 on 6/5/12

首先我们利用NodeJs先构建一个基本的服务器。

//index.js
var requestHandler = require("./requestHandler");
var server = require("./server");
 
var route = {
  "/hello": requestHandler.hello,
  "/upload": requestHandler.upload
};
 
server.start(route);
server.js
 
var http = require("http");
var url = require("url");
 
exports.start = function (route) {
  var server = http.createServer(function (req, res) {
 
    var pathName = url.parse(req.url).pathname;
 
    var handler = route[pathName];
 
    if (handler) {
 
      console.log("Through path:" + pathName + ":" + new Date().getTime());
 
      handler(res);
 
    } else {
      res.writeHead(404, {
        "Content-Type": "text/plain"
      });
      res.end();
    }
  });
 
  server.listen(8088);
};
 
 
//requestHandler.js
 
exports.hello = function (res) {
 
  res.writeHead(200, {
    "Content-Type": "text/plain"
  });
 
  res.write("say hello.");
 
  res.end();
};
 
exports.upload = function (res) {
  res.writeHead(200, {
    "Content-Type": "text/plain"
  });
 
  res.write("upload");
 
  res.end();
};

在控制台键入node index.js即可启动。

但是,上面的代码是阻塞的。如果在createServer的回调函数中,有花费长时间的计算。那么会阻塞node.js的事件轮询。

NodeJS中,他的高效,关键在于快速的返回事件循环。

我们将requestHandler.js改造如下,在这个例子中,由于事件循环一直被sleep函数阻塞着,导致createServer的callback无法及时返回。

function sleep(milliSecond) {

var startTime = new Date().getTime();

console.log(startTime);

while(new Date().getTime() <= milliSecond + startTime) {

}

console.log(new Date().getTime()); } exports.hello = function(res) { sleep(20000); res.writeHead(200, {"Content-Type": "text/plain"});

res.write("say hello.");

res.end(); };

exports.upload = function(res) { res.writeHead(200, {"Content-Type": "text/plain"});

res.write("upload");

res.end(); };


那么先键入http://localhost:8088/hello,后键入http://localhost:8088/upload。你会发现,upload虽然不需要花费太多时间,但是却要等到hello完成。

我们试图找寻异步调用的方法。比如formidable中的上传,经测试是非阻塞的。查看formidable的源码,发现最关键的是下面的代码:

IncomingForm.prototype.parse = function(req, cb) { this.pause = function() { try { req.pause(); } catch (err) { // the stream was destroyed if (!this.ended) { // before it was completed, crash & burn this._error(err); } return false; } return true; };

this.resume = function() { try { req.resume(); } catch (err) { // the stream was destroyed if (!this.ended) { // before it was completed, crash & burn this._error(err); } return false; }

return true; };

this.writeHeaders(req.headers);

var self = this; req .on('error', function(err) { self._error(err); }) .on('aborted', function() { self.emit('aborted'); }) .on('data', function(buffer) { self.write(buffer); }) .on('end', function() { if (self.error) { return; }

var err = self._parser.end(); if (err) { self._error(err); } });

if (cb) { var fields = {}, files = {}; this .on('field', function(name, value) { fields[name] = value; }) .on('file', function(name, file) { files[name] = file; }) .on('error', function(err) { cb(err, fields, files); }) .on('end', function() { cb(null, fields, files); }); }

return this; };


在parse中,将head信息解析出来这段是阻塞的。但是真正上传文件却是在req.on(data)中,是利用了事件驱动,是非阻塞的。也就是说,他的非阻塞模型依赖整个nodeJS事件分派架构。

那么像sleep那样消耗大量计算,但是又不能依赖nodeJS分派架构的时候怎么办?

现在介绍一种,类似于html5 WebWorker的方法。

将requestHandler.js改造如下:

var childProcess = require("child_process");

exports.hello = function(res) {

var n = childProcess.fork(__dirname + "/subProcess.js");

n.on('message', function() {

res.writeHead(200, {"Content-Type": "text/plain"});

res.write("say hello.");

res.end(); });

n.send({}); };

exports.upload = function(res) { res.writeHead(200, {"Content-Type": "text/plain"});

res.write("upload");

res.end(); }; 并加入subProcess.js

function sleep(milliSecond) {

var startTime = new Date().getTime();

console.log(startTime);

while(new Date().getTime() <= milliSecond + startTime) {

}

console.log(new Date().getTime()); }

process.on('message', function() { sleep(20000); process.send({}); });

测试,当hello还在等待时,upload已经返回。

为什么要使用NodeJS

我们的问题 - Server端阻塞

通过一段简单的代码解释何为阻塞:

var name = db.query("select name from persons where id=1");  
output("name")      //进程等待数据查询完毕,然后使用查询结。

在上面两个语句之间,在整个数据查询的过程,当前进程往往只是在等待结果的返。这就造成了进程的阻塞。

如何解决阻塞问题

解决这个问题的办法是,建立一种事件机制,发起查询请求之后,立即将进程交出,当数据返回后触发事件,再继续处理数据:

//定义如何后续数据处理函数  
function onDataLoad(name) {  
    output("name");  
}  
//发起数据请求,同时指定数据返回后的回调函数  
db.query("select name from persons where id=1", onDataLoad);
为什么JS适合解决阻塞问题

首先JavaScript是一种函数式编程语言,函数编程语言最重要的数学基础是λ演算(lambda calculus) - 即函数可以接受函数当作输入(参数)和输出(返回值). 这个特性使得为事件指定回调函数变得很容易.特别是JavaScript还支持匿名函数.通过匿名函数的辅助,之前的代码可以进行简写如下.

db.query("select name from persons where id=1", function (name) {
    output(name);
});

还有一个关键问题是,异步回调的运行上下文保持(称状态保持),我看一段代码来说明何为状态保持.

//传统同步写法
function main(){  
    var id = "1";  
    var name = db.query("select name from persons where id=" + id);  
    output("person id:" + id + ", name:" + name);  
}  
 
main();

前面的写法在传统的阻塞是编程中非常常见,但接下来进行异步改写时会遇到一些困扰.

//异步写法
function main() {
    var id = "1";
    db.query("select name from persons where id=" + id, function (name) {
        output("person id:" + id + ", name:" + name);      //n秒后数据返回后执行回调  
    });
} 
 
main();

细心的同学可以注意到,当等待了n秒数据查询结果返回后执行回调时.回调函数中却仍然使用了main函数的局部变量"id",而"id"已经在n秒前走出了其作用域,这是为什么呢?熟悉JavaScript的同学会淡然告诉您:"这是闭包(closures)".

其实在复杂的应用中,我们一定会遇到这类场景.即在函数运行时需要访问函数定义时的上下文数据(注意:一定要区分函数定义时和函数运行时这样的字眼和其代表的意义,不然很快就会糊涂).而在异步编程中,函数的定义和运行又分处不同的时间段,那么保持上下文的问题变得更加突出了.

在这个例子中,db.query作为一个公共的数据库查询方法,把"id"这个业务数据传入给db.query,交由其保存是不太合适的.但聪明的同学们可以抽象一下,让db.query再支持一个需要保持状态的数据对象传入,当数据查询完毕后可以把这些状态数据原封不动的回传.如下:

function main() {
    var id = "1";
    var currentState = new Object();
    currentState.person_id = id;
    db.query("select name from persons where id=" + id, function (name, state) {
        output("person id:" + state.person_id + ", name:" + name);
    }, currentState); //注意currentState是db.query的第三个参数  
} 
main();

记住这种重要的思路,我们再看看是否还能进一步的抽象?可以的,不过接下的动作之前,我们先要了解在JavaScript中一个函数也是一个对象.一个函数实例fn除了具备可函数体的定义之外,仍然可以在这个函数对象实例之上扩展属性,如fn.a=1;受到这个启发我们尝试把需要保持的状态直接绑定到函数实例上.

function main(){  
    var id = "1";  
    var currentState = new Object();  
    currentState.person_id = id;  
    function onDataLoad(name){  
        output("person id:" + onDataLoad.state.person_id + ", name:" + name);  
    }  
    onDataLoad.state = currentState ;//为函数指定state属性,用于保持状态  
    db.query("select name from persons where id=" + id, onDataLoad);  
}

我们做了什么?生成了currentState对象,然后在函数onDataLoad定义时,将currentState绑定给onDataLoad这个函数实例.那么在onDataLoad运行时,就可以拿到定义时的state对象了.而闭包就是内置了这个过程而已.

在每个函数运行时,都有一个运行时对象称为Execution context,它包含如下variable object(VO,变量对象),scope chain(作用域链)和thisValue三部分.详见ECMA-262 JavaScript. The Core

其中变量对象VO,包含了所有局部变量的引用.对于main函数,局部变量"id"存储在VO.id内.看起来用VO来代替我们的currentSate最合适了.但main函数还可能嵌套在其他函数之内,所以我们需要ScopeChain,它是一个包含当前运行函数VO和其所有父函数scope的数组.

所以在这个例子中,在onDataLoad函数定义时,就为默认为其绑定了一个scope属性指向其父函数的ExecutionContext的ScopeChain.而当函数onDataLoad执行时,就可以通过scope属性来访问父函数的VO对象来找到id,如果父函数的VO中没有id这个属性,就再继续向上查找其祖先的VO对象,直到找到id这个属性或到达最外层返回undefined.也正是因为这个引用,造成VO的引用计数不为0,在走出作用域时,才不会被垃圾回收.

很多人觉得闭包很难理解,其实我们只要能明确需要区分函数定义和函数运行这两个时机,记住闭包让函数在运行时能够访问到函数定义时的所处作用域内的所有变量.或者说函数定义时能访问到什么变量,那么在函数运行时通过相同的变量名一样能访问到.

关于状态保持是本文的重点,在我看到的多数NodeJS的介绍文章,并没有详解这里,我们只是知道了要解决阻塞问题,但是JavaScript解决阻塞问题的优势在哪里,作为一个前端开发,我想有必要详细解释一下.

其实说到状态保持还有一个类似的场景,比如用户从A页面提交表单到B页面,如果提交数据校验不通过,则需要返回A页面,同时保持用户在A页面填写的内容并提示用户修改不对的地方.从提交到返回显示这也是一个包含网络交互的异步过程.传统网页,用户的状态通过请求传递到服务端,交由后端状态保持(类似交给db.query的currentSate).而使用Ajax的网页,因为并未离开原页面,那么服务端只要负责校验用户提交的数据是否正确即可,发送错误,返回错误处相关信息即可,这就是所谓前端状态保持.可以看到这个场景里边服务端做的事情变少了,变纯粹了.正如我们的例子中db.query不再存储转发第三个state参数,变得更轻量.

我们看到通过JavaScript函数式语言特性,匿名函数支持和闭包很漂亮的解决了同步编程到异步编程转化过程中遇到的一系列最重要的问题.但JavaScript是否就是最好的?这就要回答我们引用新技术时需要考虑的最后一个问题了

使用NodeJS是否带来额外的困扰,如何解决

性能真的是最好么?不用比较我们也可以得到结论NodeJS,做无阻塞编程性能较难做到极致.何为极致,处理一个请求需要占用多少内存,多少cpu资源,多少带宽,如果有浪费就不是极致.阻塞式编程浪费了大量进程资源只是在等待,导致大量内存和cpu的浪费.NodeJs好很多,但也正是因为一些闭包等JS内建机制也会导致资源的浪费,看下面的代码

function main() {
    var id = "1";
    var str = "..."; //这里存储一个2M的字符串  
    db.query("select name from persons where id=" + id, function (name) {
        output("person id:" + id + ", name:" + name); //n秒后数据返回后执行回调  
    });
} 
main();

直到数据查询完成,变量str所使用的2M内存不会被释放,而str保持下去可能并没有意义.前面已经解释过闭包的原理,闭包并没有智能到只包起来今后可能被访问到的对象.即使不了解闭包的原理,也可以通过一段简单脚本验证这点:

function main() {
    var id = "1";
    var str = "..."; //这里存储一个2M的字符串  
    window.setTimeout(function () {
        debugger; //我们在这里设置断点  
    }, 10000)
} 
main();

我们在回调函数当中只设置一个断点,并不指明我们要访问哪个变量.然后我们在控制台监视一下,id和str都是可以拿到的.

所以我来不负责任的预测一下,性能极端苛刻的场景,无阻塞是未来,但无阻塞发展下去,或者有更轻量的脚本引擎产生(lua?),或者V8JS引擎可能要调整可以disable闭包,或者我们可以通过给JS开发静态编译器在代码发布前优化我们的代码.

我之前谈到过JS静态编译器:"如果给JS代码发布正式使用前增加一个编译步骤,我们能做些什么",动态语言的实时编译系统只完成了静态语言编译中的将代码转化为字节码的过程,而静态语言编译器的额外工作,如接口校验,全局性能优化等待.所以JS也需要一个静态的编译器来完成这些功能,Google利用ClouserComplier提供了系列编译指令,让JS更好的实现OO编程,我来利用静态编译器解决一些JS做细粒度模块化引入的性能方面的问题.而老赵最近的项目JSCEX,则也是利用JS发布前的编译环节重点解决异步编程的代码复杂度问题.

我们习惯于阻塞式编程的写法,切换到异步模式编程,往往对于太多多层次的callback嵌套弄得不知所措.所以老赵开发的JS静态编译器,借鉴F#的Computation Expressions,让大家遵守一些小的约定后,能够仍然保持同步编程的写法,写完的代码通过JSCEX编译为异步回调式的代码再交给JS引擎执行.

如果这个项目足够好用,那就也解决了一个使用NodeJS这种新技术,却加大编程复杂度这个额外引入的困扰.甚至可以沿着这个思路,在静态编译阶段优化内存使用.

centos安装Node.js 0.8.5

node.js 0.8.5的安装,需要python 2.7,大部分安装失败,都是因为python版本过低所致。安装之前,请升级python版。升级步骤 http://www.tomtalk.net/wiki/Python

[tom@MyVPS1974 ~]$ python -V
Python 2.7.3

开始安装:

yum install gcc-c++ openssl-devel
wget http://nodejs.org/dist/v0.8.5/node-v0.8.5.tar.gz
tar zvxf node-v0.8.5.tar.gz
cd node-v0.8.5
./configure
make && make install

写一段小程序例如hello_node.js来验证安装是否正确:

var http = require('http');
 
http.createServer(function (req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/plain'
  });
  res.end('Hello Node.js');
}).listen(8124, "127.0.0.1");
 
console.log('Server running at http://127.0.0.1:8124/');

用node来运行这段代码

[tom@MyVPS1974 ~]$ node hello_node.js
Server running at http://127.0.0.1:8124/

现在,用浏览器打开 http://127.0.0.1:8124/ , 应该能够看到一条消息。


在mac上安装grunt

ruby -e "$(curl -fsSkL https://raw.github.com/Homebrew/homebrew/go/install)"
brew doctor
brew update
brew install node
npm install -g grunt-cli

NPM模块管理工具

npm 全名为Node Package Manager,是Node.js 的套件(package)管理工具,类似Perl 的ppm 或PHP 的PEAR 等。安装npm 后,使用npm install module name 指令即可安装新套件,维护管理套件的工作会更加轻松。

npm 可以让Node.js 的开发者,直接利用、扩充在线的套件库(packages registry),加速软件项目的开发。npm 提供很友善的搜寻功能,可以快速找到、安装需要的套件,当这些套件发行新版本时,npm 也可以协助开发者自动更新这些套件。

npm 不仅可用于安装新的套件,它也支持搜寻、列出已安装模块及更新的功能。

Node.js 在0.6.3 版本开始内建npm,读者安装的版本若是此版本或更新的版本,否则需要单独安装。

npm 目前拥有超过6000 种套件(packages),可以在npm registry 使用关键词搜寻套件。http://search.npmjs.org/

npm常用命令

npm -v                        #显示版本,检查npm 是否正确安装。
npm install express           #安装express模块
npm install -g express        #加上 -g 启用global安装模式
npm list                      #列出已安装模块
npm show express              #显示模块详情
npm update                    #升级所有套件
npm update express            #升级指定的模块
npm uninstall express         #删除指定的模块
 
npm i --dev unused-webpack-plugin       #安装开发模块
npm i --only=dev unused-webpack-plugin  #安装开发模块(新语法)
 
npm outdated -l                         #检查模块是否过期
# Red means there’s a newer version matching your semver requirements, so you should update now.
# Yellow indicates that there’s a newer version above your semver requirements (usually new major, or new 0.x minor) so proceed with caution.
 
 
npm view jquery                         #查看包信息 
npm ls jquery                           #查看本地包信息
npm ll jquery                           #查看本地包信息,显示详细信息。
npm ls jquery -g                        #查看本地包全局信息
"@angular/core": "2.0.1"                #只有版号(安装指定版本)
"@angular/core": "~2.0.1"               #符号~(2.1.0不可以)
"@angular/core:: "^2.0.1"               #符号^(3开头的不可以)
"@angular/core": ">=2.0.1<=3.0.0"       #符号>=、<=

TypeError: React.createContext is not a function

#在项目根目录执行下面两条命令就好了。
rd /s/q node_modules
cnpm install

删除文件用的是del命令,但del不能删除文件夹,删除文件夹必须使用rd命令。rd的另外一个写法是rmdir,源自ReMakeDirectory。

删除test文件夹,但目录不是空的,这时要加个 /s 参数,它才会正常删除。

但仅仅使用 rd /s test ,CMD总是停下来并询问:是否确认(Y/N),为了避免输入,增加/q (它取自quiet,官方解释为:安静模式)

What does 'invalid' mean when using npm list?

**npm ERR! invalid: chalk@0.5.1 /usr/local/lib/node_modules/bower/node_modules/chalk  
npm ERR! invalid: ansi-regex@0.2.1 /usr/local/lib/node_modules/bower/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex  
npm ERR! invalid: configstore@0.3.1 /usr/local/lib/node_modules/bower/node_modules/update-notifier/node_modules/configstore  
npm ERR! invalid: object-assign@0.3.1 /usr/local/lib/node_modules/bower/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/object-assign  
npm ERR! invalid: registry-url@0.1.1 /usr/local/lib/node_modules/bower/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-url  
npm ERR! invalid: strip-ansi@0.2.2 /usr/local/lib/node_modules/bower/node_modules/update-notifier/node_modules/string-length/node_modules/strip-ansi  
npm ERR! not ok code 0**

I was getting this error having the same package installed both in "dependencies" and "devDependencies" with different versions.

nodejs使用mysql

$ cd /usr/local/lib         #默认安装时,模块文件夹放在这个目录下,为了便宜管理,模块还是统一安装到这里好。
$ npm install mysql         #安装mysql module 
 
$ cat mysql.js 
var Client = require('mysql').Client;
var client = new Client();
 
client.user = 'user';
client.password = 'password';
 
console.log('Connecting to MySQL...');
 
client.query('USE tiny_shop'); 
 
client.query(  
  'SELECT * FROM tags',  
  function selectCb(err, results, fields) {  
    if (err) {  
      throw err;  
    }  
 
    console.log(results);  
    console.log(fields);  
    client.end();  
  }  
);

forever让nodejs应用后台执行

nodejs一般是当成一条用户命令执行的,当用户断开客户连接,运用也就停了,很烦人。如何让nodejs应用当成服务,在后台执行呢?

最简单的办法:

$ nohup node your_app.js &

但是,forever能做更多的事情,比如分别记录输出和错误日志,比如可以在js中作为api使用。

$ sudo npm install forever -g   #安装
$ forever start app.js          #启动
$ forever stop app.js           #关闭
$ forever start -l forever.log -o out.log -e err.log app.js   #输出日志和错误

命令语法及使用 https://github.com/nodejitsu/forever

socket.io

nodeclub的安装

安装node

安装及运行mongod

$ npm install -g express ndir mogoose validator eventproxy nodemailer data2xml node-markdown ejs
 
$ cp config.default.js config.js
// modify the config file as yours
 
 
//app.js文件的修改
 
//var app = express.createServer();  
var app = express();        #如果用的是express是3.0这后的新版,请使用这个写法生成express服务。
 
//app.register('.html', require('/usr/local/lib/node_modules/ejs'));
app.engine('html', require('/usr/local/lib/node_modules/ejs').renderFile);   
 
//app.helpers
app.locals({
   config: config
});
 
#这段代码不知如何转换,先注释掉。
//app.dynamicHelpers({
//  csrf: function (req, res) {
//    return req.session ? req.session._csrf : '';
//  }
//});
$ node app.js
 
TypeError: Invalid select() argument. Must be a string or object.
    at Query.select (/usr/local/lib/node_modules/mongoose/lib/query.js:1061:11)
    at Function.find (/usr/local/lib/node_modules/mongoose/lib/model.js:863:9)
    at Object.get_all_tags (/root/tecshuttle/node.js/nodeclub/controllers/tag.js:286:7)
    at exports.index (/root/tecshuttle/node.js/nodeclub/controllers/site.js:62:12)
    at callbacks (/usr/local/lib/node_modules/express/lib/router/index.js:165:11)
    at param (/usr/local/lib/node_modules/express/lib/router/index.js:139:11)
    at pass (/usr/local/lib/node_modules/express/lib/router/index.js:146:5)
    at Router._dispatch (/usr/local/lib/node_modules/express/lib/router/index.js:173:5)
    at Object.router (/usr/local/lib/node_modules/express/lib/router/index.js:33:10)
    at next (/usr/local/lib/node_modules/express/node_modules/connect/lib/proto.js:190:15)

Mongoosejs

Mongoskin

https://github.com/kissjs/node-mongoskin