NodeJS 初体验
console.log('%s: %d', 'Hello', 25); // 可以像C语言格式一样输出
//app.js
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<h1>Node.js</h1>');
res.end('<p>Hello World</p>');
}).listen(3000);
console.log("HTTP server is listening at port 3000.");
小技巧——使用 supervisor
Node.js 只有在第一次引用到某部份时才会去解析脚本文件,以后都会直接访问内存,避免重复载入
Node.js的这种设计虽然有利于提高性能,却不利于开发调试,因为我们在开发过程中总是希望修改后立即看到效果,
而不是每次都要终止进程并重启。
supervisor 可以帮助你实现这个功能,它会监视你对代码的改动,并自动重启 Node.js/
使用方法很简单,首先使用 npm 安装 supervisor:
npm install -g supervisor
接下来,使用 supervisor 命令启动 app.js:
supervisor app.js
异步式 I/O 与事件式编程
这种模式与传统的同步式 I/O 线性的编程思路有很大的不同,因为控制流很大程度上要靠事件
和回调函数来组织,一个逻辑要拆分为若干个单元。
线程必须有事件循环,不断地检查有没有未处理的事件,依次予以处理.
Node.js 使用了单线程、非阻塞的事件编程模式。
异步式编程的缺点在于不符合人们一般的程序设计思维,容易让控制流变得晦涩难懂,给编码和调试都带来不小的困难.
回调函数
让我们看看在 Node.js 中如何用异步的方式读取一个文件,下面是一个例子:
//readfile.js
var fs = require('fs');
fs.readFile('file.txt', 'utf-8', function(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
console.log('end.');
运行的结果如下:
end.
Contents of the file.
同步的方式:
var fs = require('fs');
var data = fs.readFileSync('file.txt', 'utf-8');
console.log(data);
console.log('end.');
运行的结果与前面不同,如下所示:
$ node readfilesync.js
Contents of the file.
end.
可以看到, 异步的方式与同步的方式执行结果不同, 同步的方式好理解, 就是传统的程序运行的方式.
异步式读取文件就稍微有些违反直觉了,end.先被输出. 我们必须先知道在 Node.js 中,
异步式 I/O 是通过回调函数来实现的. fs.readFile 接收了三个参数, 第一个是文件名,第二个是编码方式,
第三个是一个函数,我们称这个函数为回调函数。
//readfilecallback.js
function readFileCallBack(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
}
var fs = require('fs');
fs.readFile('file.txt', 'utf-8', readFileCallBack);
console.log('end.');
fs.readFile 调用时所做的工作只是将异步式 I/O 请求发送给了操作系统,然后立即返回并执行后面的语句,
执行完以后进入事件循环监听事件。当 fs 接收到 I/O 请求完成的事件时,事件循环会主动调用回调函数以完成后续工作。
因此我们会先看到 end.,再看到file.txt 文件的内容。
Question : 什么时候将控制权返回给事件循环 ?
事件
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。,事件由 EventEmitter 对象提供。
前面提到的 fs.readFile 和 http.createServer 的回调函数都是通过 EventEmitter 来实现的。
Node.js 的事件循环机制
Node.js 在什么时候会进入事件循环呢?
答案是 Node.js 程序由事件循环开始,到事件循环结束,所有的逻辑都是事件的回调函数,所以 Node.js 始终在事件循环中,
程序入口就是事件循环第一个事件的回调函数。事件的回调函数在执行的过程中,可能会发出 I/O 请求或直接发射(emit)事件,
执行完毕后再返回事件循环, 事件循环会检查事件队列中有没有未处理的事件,直到程序结束。
与其他语言不同的是,Node.js 没有显式的事件循环, Node.js 的事件循环对开发者不可见,由 libev 库实现.
模块和包
模块(Module)和包(Package)是 Node.js 最重要的支柱。开发一个具有一定规模的程序不可能只用一个文件,
通常需要把各个功能拆分、封装,然后组合起来,模块正是为了实现这种方式而诞生的, 而且模块都是基于文件的,机制十分简单。
我们经常把 Node.js 的模块和包相提并论,因为模块和包是没有本质区别的.
模块是 Node.js 应用程序的基本组成部分,文件和模块是一一对应的. 我们曾经用到了 var http = require('http'),其中 http
是 Node.js 的一个核心模块,其内部是用 C++ 实现的,外部用 JavaScript 封装.(也是一个文件)
我们通过require 函数获取了这个模块,然后才能使用其中的对象。
创建模块
在 Node.js 中,创建一个模块非常简单,因为一个文件就是一个模块,我们要关注的问题仅仅在于如何在其他文件中获取这个模块.
。Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,
即所获取模块的 exports 对象。
让我们以一个例子来了解模块。创建一个 module.js 的文件,内容是:
//module.js
var name;
exports.setName = function(thyName) {
name = thyName;
};
exports.sayHello = function() {
console.log('Hello ' + name);
};
在同一目录下创建 getmodule.js,内容是:
//getmodule.js
var myModule = require('./module');
myModule.setName('BYVoid');
myModule.sayHello();
从以上例子中, 我们可以看到, "模块即文件", 为了方便引用.
,npm 提供的上万个模块都是通过这种简单的方式搭建起来的.
单次加载
上面这个例子有点类似于创建一个对象,但实际上和对象又有本质的区别,因为require 不会重复加载模块,
也就是说无论调用多少次 require,获得的模块都是同一个。
//loadmodule.js
var hello1 = require('./module');
hello1.setName('BYVoid');
var hello2 = require('./module');
hello2.setName('BYVoid 2');
hello1.sayHello();
运行后发现输出结果是 Hello BYVoid 2,这是因为变量 hello1 和 hello2 指向的是同一个实例,
因此 hello1.setName 的结果被 hello2.setName 覆盖,最终输出结果是由后者决定的.
覆盖 exports
有时候我们只是想把一个对象封装到模块中,例如:
//singleobject.js
function Hello() {
var name;
this.setName = function (thyName) {
name = thyName;
};
this.sayHello = function () {
console.log('Hello ' + name);
};
};
exports.Hello = Hello;
此时我们在其他文件中需要通过 require('./singleobject').Hello 来获取Hello 对象,这略显冗余,可以用下面方法稍微简化:
//hello.js
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello;
这样就可以直接获得这个对象了:
//gethello.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();
注意,模块接口的唯一变化是使用 module.exports = Hello 代替了 exports.Hello=Hello, 另外, 就是文件名变了.
创建包
包是在模块基础上更深一步的抽象,Node.js 的包类似于 C/C++ 的函数库或者 Java/.Net的类库.
它将某个独立的功能封装起来,用于发布、更新、依赖管理和版本控制.
Node.js 的包是一个目录, 其中包含一个 JSON 格式的包说明文件 package.json。严格按照以下规则:
package.json 必须在包的顶层目录下;
二进制文件应该在 bin 目录下;
JavaScript 代码应该在 lib 目录下;
文档应该在 doc 目录下;
单元测试应该在 test 目录下。
作为文件夹的模块
模块与文件是一一对应的, 文件不仅可以是 JavaScript 代码或二进制代码,还可以是一个文件夹, 最简单的包,
就是一个作为文件夹的模块, 下面我们来看一个例子,建立一个叫做 somepackage 的文件夹, 在其中创建 index.js,内容如下:
//somepackage/index.js
exports.hello = function() {
console.log('Hello.');
};
然后在 somepackage 之外建立 getpackage.js,内容如下:
//getpackage.js
var somePackage = require('./somepackage');
somePackage.hello();
运行 node getpackage.js,控制台将输出结果 Hello.
我们使用这种方法可以把文件夹封装为一个模块,即所谓的包. 包通常是一些模块的集合, 在模块的基础上提供了更高层的抽象,
相当于提供了一些固定接口的函数库. 通过定制package.json,我们可以创建更复杂、更完善、更符合规范的包用于发布.
package.json
在前面例子中的 somepackage 文件夹下,我们创建一个叫做 package.json 的文件,内容如下所示:
{
"main" : "./lib/interface.js"
}
然后将 index.js 重命名为 interface.js 并放入 lib 子文件夹下。以同样的方式再次调用这个包,依然可以正常使用。
Node.js 在调用某个包时,会首先检查包中 package.json 文件的 main 字段,将其作为包的接口模块,
如果 package.json 或 main 字段不存在,会尝试寻找 index.js 或 index.node 作为包的接口。
下面是一个完全符合 CommonJS 规范的 package.json 示例:
Node.js 包管理器
Node.js包管理器,即npm是 Node.js 官方提供的包管理工具,它已经成了 Node.js 包的标准发布平台,
用于 Node.js 包的发布、传播、依赖控制。npm 提供了命令行工具,使你可以方便地下载、安装、升级、删除包,也可以让你作为开发者发布并维护包。
获取一个包
npm [install/i] [package_name], 例如 : npm install express
并且放置在当前目录的 node_modules 子目录下.
本地模式和全局模式
npm在默认情况下会从http://npmjs.org搜索或下载包,将包安装到当前目录的node_modules子目录下。
在使用 npm 安装包的时候,有两种模式:本地模式和全局模式。默认情况下我们使用 npminstall命令就是采用本地模式,
即把包安装到当前目录的 node_modules 子目录下. Node.js的 require 在加载模块时会尝试搜寻 node_modules 子目录,
因此使用 npm 本地模式安装的包可以直接被引用。
npm 还有另一种不同的安装模式被成为全局模式,使用方法为:npm [install/i] -g [package_name]
我们在 介绍 supervisor那个小节中使用了 npm install -g supervisor 命令,就是以全局模式安装 supervisor。
为什么要使用全局模式呢?
因为本地模式不会注册 PATH 环境变量.举例说明,我们安装supervisor 是为了在命令行中运行它,譬如直接运行 supervisor script.js,
这时就需要在 PATH环境变量中注册 supervisor。
使用全局模式安装的包并不能直接在 JavaScript 文件中用 require 获得,因为 require 不会搜索PATH中对应的某个目录.总而言之,当我们要把某个包作为工程运行时的一部分时,通过本地模式获取,如果要在命令行下使用,则使用全局模式安装。
创建全局链接
npm 提供了一个有趣的命令 npm link,它的功能是在本地包和全局包之间创建符号链接。
我们说过使用全局模式安装的包不能直接通过 require 使用,但通过 npm link命令可以打破这一限制.
npm link 命令不支持Windows
包的发布
npm 可以非常方便地发布一个包, 通过使用 npm init 可以根据交互式问答产生一个符合标准的 package.json,
例如创建一个名为 byvoidmodule 的目录,然后在这个目录中运行npm init:
Package name: (byvoidmodule) byvoidmodule
Description: A module for learning perpose.
Package version: (0.0.0) 0.0.1
Project homepage: (none) http://www.byvoid.com/
Project git repository: (none)
Author name: BYVoid
Author email: (none) byvoid.kcp@gmail.com
Author url: (none) http://www.byvoid.com/
Main module/entry point: (none)
Test command: (none)
What versions of node does it run on? (~0.6.10)
About to write to /home/byvoid/byvoidmodule/package.json
{
"author": "BYVoid <byvoid.kcp@gmail.com> (http://www.byvoid.com/)",
"name": "byvoidmodule",
"description": "A module for learning perpose.",
"version": "0.0.1",
"homepage": "http://www.byvoid.com/",
"repository": {
"url": ""
},
"engines": {
"node": "~0.6.12"
},
"dependencies": {},
"devDependencies": {}
}
Is this ok? (yes) yes
接下来,在 package.json 所在目录下运行 npm publish,稍等片刻就可以完成发布了。
打开浏览器,访问 http://search.npmjs.org/ 就可以找到自己刚刚发布的包了。
现在我们可以在世界的任意一台计算机上使用 npm install byvoidmodule 命令来安装它
如果你的包将来有更新,只需要在 package.json 文件中修改 version 字段, 然后重新使用 npm publish 命令就行了.
如果你对已发布的包不满意, 可以使用 npm unpublish 命令来取消发布。
调试
命令行调试
在命令行下执行 node debug debug.js,将会启动调试工具
使用 eclipse 调试
NodeJS 初体验的更多相关文章
- Mac下nodeJS初体验
Mac下nodeJS初体验 这两天博主出门在外,抽空体验一下大名鼎鼎的node 安装 brew install node 安装测试 $ node -v v8.4.0 运行本地脚本 用文本编辑器编辑一段 ...
- nodejs初体验
安装好nodejs之后 在命令行中直接运行:node -v //若安装成功则显示版本号 var http = require('http'); http.createServer(function ( ...
- Nodejs初阶之express
PS: 2014/09/24 更新<Express 4.X 启航指南>,欢迎阅读和评论:) 老规矩,开头部分都是些自娱自乐的随想,想到哪写到哪... 到今天俺已经在俺厂工作俩年零几天了 ...
- node.js 初体验
node.js 初体验 2011-10-31 22:56 by 聂微东, 174545 阅读, 118 评论, 收藏, 编辑 PS: ~ 此篇文章的进阶内容在为<Nodejs初阶之express ...
- grunt 构建工具(build tool)初体验
操作环境:win8 系统,建议使用 git bash (window下的命令行工具) 1,安装node.js 官网下载:https://nodejs.org/ 直接点击install ,会根据你的操 ...
- gulp快速入门&初体验
前言 一句话先 gulp 是一个可以简单和自动化"管理"前端文件的构建工具 先说我以前的主要工作,我主要是做游戏服务端的,用c++/python,所以我对东西的概念理解难免要套到自 ...
- node.js + express 初体验【hello world】
[node.js] 一个神奇的XX 呵呵 :) 不知道怎么形容他才好! [express] 是node.js 开发web应用程序的框架 开发环境:XP 大家共同进步吧 :) 一:前期准备: 1:下载 ...
- ionicframework I ------------- 初体验
ionicframework I ------------- 初体验 Create hybrid mobile apps with the web technologies you love. Fr ...
- Node.js 网页瘸腿爬虫初体验
延续上一篇,想把自己博客的文档标题利用Node.js的request全提取出来,于是有了下面的初哥爬虫,水平有限,这只爬虫目前还有点瘸腿,请看官你指正了. // 内置http模块,提供了http服务器 ...
随机推荐
- hduoj 1251 统计难题
http://acm.hdu.edu.cn/showproblem.php?pid=1251 统计难题 Time Limit: 4000/2000 MS (Java/Others) Memory ...
- 网络层、传输层、应用层、端口通信协议编程接口 - http,socket,tcp/ip 网络传输与通讯知识总结
引: http://coach.iteye.com/blog/2024511 什么是TCP和UDP,以及二者区别是什么? TCP的全称为传输控制协议.这种协议可以提供面向连接的.可靠的.点到点的通信. ...
- link和@import的区别、及各自的应用
面试的过程中遇到的问题,当时自己回答的感觉自己心里还是很满意的,但是回来百度查看后才知道自己回答的有多么的糟糕: 下面我这这个知识点做一些总结的书面说明,为了少走点弯路,多涨点见识吧. 首先我们要了解 ...
- NCreport报表控件教程:设计页眉和页脚
一.设计页眉 一般来说页眉部分一般是用于包含标题的内容, 首先我们会添加列标签到页眉部分,标签都是简单的文本,标签项一般是用于在报表上显示一些描述信息,标签都是静态项,所以它们的值不会有变化. 添加标 ...
- Dynamics AX 2012 的工业物联网解决方案
Dynamics AX 2012 的工业物联网解决方案 物联网 物联网的概念在这两年非常火,包括近期很火的共享单车初创公司--摩拜单车,在产品中运用了Azure Iot物联网技术.但是,物联网并不是一 ...
- zigbee学习之路(十二):zigbee协议原理介绍
一.前言 从今天开始,我们要正式开始进行zigbee相关的通信实验了,我所使用的协议栈是ZStack 是TI ZStack-CC2530-2.3.0-1.4.0版本,大家也可以从TI的官网上直接下载T ...
- laravel框架总结(十) -- 返回值
以前用CI框架对于返回值没有过多关注,但是发现使用laravel框架的时候出现了一些小问题,特意实践总结了一些常用情形,希望对大家有所帮助 先理解几个概念: 1>StdClass 对象=&g ...
- QM模块包含主数据(Master data)和功能(functions)
QM模块包含主数据(Master data)和功能(functions) QM主数据 QM主数据 1 Material Master MM01/MM02/MM50待测 物料主数据 2 Sa ...
- ifconfig 下面的一些字段(errors, dropped, overruns)
一台机器经常收到丢包的报警,先看看最底层的有没有问题: # ethtool em2 | egrep 'Speed|Duplex' Speed: 1000Mb/s Duplex: Full # etht ...
- ORACLE 11g安装
下载地址 win 32位操作系统 下载地址: http://download.oracle.com/otn/nt/oracle11g/112010/win32_11gR2_database_1of2. ...