node.js实际上就是一个可以让js脱离浏览器环境独立运行的一个运行环境,这样就可以把js变成一种独立的开发语言,node.js与jvm的作用差不多,只有安装了jvm环境(可以让java运行在几乎所有平台的环境)才可以让java达到所谓的一次编写处处运行
vue实际上只是一个前端框架,只需要引入在html文件里就可以使用,与node.js结合的话,node.js可以让它使用数据注入的方式,在诸恶的依赖过多的时候更好的使用
打开终端,键入node进入命令交互模式
使用node.js不是只使用了一个应用,实际上是实现了整个http服务器,实际上web应用和对应的web服务器基本上是一样的
使用php编写后端代码的话需要apache或nginx的http,并配上 mod_php5 模块和 php-cgi。可以看出接受http请求并提供web页面的功能实际上是由nginx等前端服务器框架提供的,而node.js是基友一个后端处理的逻辑语法还提供一个内嵌服务器框架
作为一个可以使用js开发后端的环境,接下来发展的就是后端mvc框架,springmvc是java的mvc框架,express.js这种就是js 的mvc框架
js中的模块化,还有很多规范,比如commonjs,amd,cmd,umd
commonjs是运行在nodejs端,amd,cmd,umd是运行在浏览器
一个mvc,js能做出来10+种框架,可想而知模块化,js也对应很多框架,例如commonjs,requirejs,seajs等等
nginx是http基础设施
node.js的http包理论上可以替代nginx,实际部署的时候都是node.js作为逻辑层,位于nginx之后,因为nginx的成熟的反向代理和高性能高科配置以及高稳定性
tomcat是java的http容器,相当于node.js的http包或Python的wsgi
后台搭建java一般不需要node.js,但是异步的node.js某些特定情况可以互补,如天猫后台使用java,前端渲染使用node.js
nginx托管静态资源的性能远远高于node.js
node.js三个部分
先引入require模块:由require指令引入node.js模块
var http = require("http");
之后创建服务器:有了服务器才可以监听到客户端的请求,类似于apache,nginx等http服务器
使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定 8888 端口。 函数通过 request, response 参数来接收和响应数据。
实例如下,在你项目的根目录下创建一个叫 server.js 的文件,并写入以下代码:
var http = require('http');
http.createServer(function (request, response) {
// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// 发送响应数据 "Hello World"
response.end('Hello World\n');
}).listen(8888); //listen 的方法,这个方法有一个数值参数, 指定这个 HTTP 服务器监听的端口号
// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');
使用 node 命令执行以上的代码:
node server.js
Server running at http://127.0.0.1:8888/
然后接受并响应请求:客户端发送请求,服务器接受
npm升级:npm install npm -g
使用NPM安装模块:
npm install <Module Name>
比如安装一个node,js的weGB框架express:npm install express
安装之后一般就放在工程目录下的 node_modules 目录中,需要的话使用require(‘express’) 引用
var express = require('express');
-g是全局安装的意思
如果出现以下错误:
npm err! Error: connect ECONNREFUSED 127.0.0.1:8087
解决办法为:
$ npm config set proxy null
可以使用 npm list -g
查看所有全局安装的模块
可以使用 npm list
模块名 查看某个模块的版本号
package.json 位于每个模块的目录下,用于定义包的属性
卸载模块
npm uninstall 模块名
使用npm ls
看看模块是不是还在
可以使用 npm update
模块名 更新模块
可以使用 npm search
模块名 搜索模块
创建模块
npm init命令可以生成一个package.json文件,根据提示输入模块名,描述和git仓库地址生成地址等之后输入yes就可以了
可以使用 npm adduser
命令在npm资源库里使用邮箱注册用户,写下用户名密码和邮箱就可以了
之后可以使用npm publish
发布模块
版本号更新
实际上版本号更新也有一定的规则
语义版本号分为X.Y.Z三位,分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。
修复bug,需要更新Z位。
新增了功能,但是向下兼容,需要更新Y位。
大变动,向下不兼容,需要更新X位。
版本号有了这个保证后,在申明第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。例如"argv": "0.0.x"表示依赖于0.0.x系列的最新版argv
npm常用命令
使用npm help
可查看某条命令的详细帮助,例如npm help install。
在package.json所在目录下使用npm install . -g
可先在本地安装当前命令行程序,可用于发布前的本地测试。
使用npm update
使用npm update
可以把全局安装的对应命令行程序更新至最新版。
使用npm cache clear
可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。
使用npm unpublish
可以撤销发布自己发布过的某个版本代码。
淘宝镜像
国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像
可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install [name]
cmd输入node即可使用
Node REPL 支持输入多行表达式,这就有点类似 JavaScript
Node.js 异步编程的直接体现就是回调
异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了
Node 使用了大量的回调函数,Node 所有 API 都支持回调函数
回调函数一般作为函数的最后一个参数出现:
function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }
阻塞代码实例
创建一个文件 input.txt ,内容如下:
菜鸟教程官网地址:www.runoob.com
创建一个main.js文件用于读取input.txt文件
var fs = require("fs");
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序执行结束!");
使用 node main.js
执行文件
菜鸟教程官网地址:www.runoob.com
程序执行结束!
非阻塞代码
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序执行结束!");
$ node main.js
程序执行结束!
菜鸟教程官网地址:www.runoob.com
node.js是单线程,因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高
Node.js 几乎每一个 API 都是支持回调函数
基本上所有的事件机制都是用设计模式中观察者模式实现
单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数
事件驱动的意思是服务器接收到一个请求之后先把请求关闭之后处理请求,处理完服务下一个请求,请求完成会被放到处理队列,在它到达队列开头的时候结果被返回给用户,类似于一个观察者模式,事件相当于一个主题,所有注册到这个事件上的处理函数相当于观察者
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
以下程序绑定事件处理程序:
// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);
我们可以通过程序触发事件:
// 触发事件
eventEmitter.emit('eventName');
实际上emit触发使用的是函数的别名,on方法负责把函数的别名与他实际的实现函数绑定到一起并开启对这个事件的监听器方便之后监听到调用这个事件的emit触发事件
对于每一个事件实际上支持多个事件监听器,事件触发的时候注册到这个事件的事件监听器依次被触发,事件参数作为回调函数参数传参
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列
node.js里很多对象都会分发事件,比如一个net服务器对象会在每次连接的时候触发一个事件,一个文件阅读对象会在文件被打开的时候触发一个事件,这些都是事件触发函数的实例
events模块实际上只提供一个事件触发函数,事件触发的核心就是事件触发和事件监听器功能的封装
事件触发对象在实例化的时候发生错误会触发一个error事件,添加新的监听器的时候newListener 事件会触发,当监听器被移除时,removeListener 事件被触发
需要使用emit往事件传入参数直接在事件后面写入参数即可
//event.js 文件
var events = require('events');
var emitter = new events.EventEmitter();
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener1', arg1, arg2);
});
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener2', arg1, arg2);
});
emitter.emit('someEvent', 'arg1 参数', 'arg2 参数');
EventEmitter 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发一个事件。接下来我们来具体看下 EventEmitter 的属性介绍
1 addListener(event, listener)
为指定事件添加一个监听器到监听器数组的尾部。
2 on(event, listener)
为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
server.on('connection', function (stream) {
console.log('someone connected!');
});
3 once(event, listener)
为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
server.once('connection', function (stream) {
console.log('Ah, we have our first user!');
});
4 removeListener(event, listener)
移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。
它接受两个参数,第一个是事件名称,第二个是回调函数名称。
var callback = function(stream) {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
5 removeAllListeners([event])
移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
6 setMaxListeners(n)
默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于改变监听器的默认限制的数量。
7 listeners(event)
返回指定事件的监听器数组。
8 emit(event, [arg1], [arg2], […])
按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
一般不直接使用事件触发函数,都是在对象里继承他,包括fs,net和http在内,所有支持事件响应的核心模块都是事件触发函数的子类
node.js的缓冲区
js只有字符串数据类型没有二进制数据类型,但是处理tcp流或者文件流时必须使用二进制数据,所以在node.js里定义了一个buffer缓存类放二进制数据
buffer类是和node内核一起发布的核心库,提供了一个存储原始数据的方法,类似一个整数数组,对应v8内存外的一块原始内存
在v6之前可以使用new Buffer()构造函数来创建对象实例,但是这种方式buffer对内存的权限操作较大所以可以使用Buffer.from() 接口去创建Buffer对象
buffer实例一般用于表示编码字符的序列,如utf-8,base64等,通过使用显示的字符编码可以在js字符串与buffer实例之间转换
const buf = Buffer.from('runoob', 'ascii');
// 输出 72756e6f6f62
console.log(buf.toString('hex'));
// 输出 cnVub29i
console.log(buf.toString('base64'));
Buffer 提供了以下 API 来创建 Buffer 类:
Buffer.alloc(size[, fill[, encoding]]): 返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0
Buffer.allocUnsafe(size): 返回一个指定大小的 Buffer 实例,但是它不会被初始化,所以它可能包含敏感的数据
Buffer.allocUnsafeSlow(size)
Buffer.from(array): 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖)
Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。
Buffer.from(buffer): 复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例
Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例
// 创建一个长度为 10、且用 0 填充的 Buffer。
const buf1 = Buffer.alloc(10);
// 创建一个长度为 10、且用 0x1 填充的 Buffer。
const buf2 = Buffer.alloc(10, 1);
// 创建一个长度为 10、且未初始化的 Buffer。
// 这个方法比调用 Buffer.alloc() 更快,
// 但返回的 Buffer 实例可能包含旧数据,
// 因此需要使用 fill() 或 write() 重写。
const buf3 = Buffer.allocUnsafe(10);
// 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。
const buf4 = Buffer.from([1, 2, 3]);
// 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。
const buf5 = Buffer.from('tést');
// 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
const buf6 = Buffer.from('tést', 'latin1');
写入缓冲区
buf.write(string[, offset[, length]][, encoding])
buf = Buffer.alloc(256);
len = buf.write("www.runoob.com");
console.log("写入字节数 : "+ len);
从缓冲区读取数据
buf.toString([encoding[, start[, end]]])
buf = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {
buf[i] = i + 97;
}
console.log( buf.toString('ascii')); // 输出: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5)); //使用 'ascii' 编码, 并输出: abcde
console.log( buf.toString('utf8',0,5)); // 使用 'utf8' 编码, 并输出: abcde
console.log( buf.toString(undefined,0,5)); // 使用默认的 'utf8' 编码, 并输出: abcde
Buffer 转换为 JSON 对象:
buf.toJSON()
当字符串化一个 Buffer 实例时,JSON.stringify() 会隐式地调用该 toJSON()
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);
// 输出: {"type":"Buffer","data":[1,2,3,4,5]}
console.log(json);
const copy = JSON.parse(json, (key, value) => {
return value && value.type === 'Buffer' ?
Buffer.from(value.data) :
value;
});
// 输出:
console.log(copy);
缓冲区合并
Buffer.concat(list[, totalLength])
var buffer1 = Buffer.from(('菜鸟教程'));
var buffer2 = Buffer.from(('www.runoob.com'));
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log("buffer3 内容: " + buffer3.toString());
缓冲区比较
buf.compare(otherBuffer);
var buffer1 = Buffer.from('ABC');
var buffer2 = Buffer.from('ABCD');
var result = buffer1.compare(buffer2);
if(result < 0) {
console.log(buffer1 + " 在 " + buffer2 + "之前");
}else if(result == 0){
console.log(buffer1 + " 与 " + buffer2 + "相同");
}else {
console.log(buffer1 + " 在 " + buffer2 + "之后");
}
拷贝缓冲区
buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
var buf1 = Buffer.from('abcdefghijkl');
var buf2 = Buffer.from('RUNOOB');
//将 buf2 插入到 buf1 指定位置上
buf2.copy(buf1, 2);
console.log(buf1.toString());
缓冲区裁剪
buf.slice([start[, end]])
var buffer1 = Buffer.from('runoob');
// 剪切缓冲区
var buffer2 = buffer1.slice(0,2);
console.log("buffer2 content: " + buffer2.toString());
缓冲区长度
buf.length;
stream是一个抽象接口,request对象就是一个stream,stdout(标准输出)也是
所有Stream 对象都是 EventEmitter 的实例,常用的事件有:
data - 当有数据可读时触发。
end - 没有更多的数据可读时触发。
error - 在接收和写入过程中发生错误时触发。
finish - 所有数据已被写入到底层系统时触发。
从流中读取数据
var fs = require("fs");
var data = '';
// 创建可读流
var readerStream = fs.createReadStream('input.txt');
// 设置编码为 utf8。
readerStream.setEncoding('UTF8');
// 处理流事件 --> data, end, and error
readerStream.on('data', function(chunk) {
data += chunk;
});
readerStream.on('end',function(){
console.log(data);
});
readerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序执行完毕");
写入流
var fs = require("fs");
var data = '菜鸟教程官网地址:www.runoob.com';
// 创建一个可以写入的流,写入到文件 output.txt 中
var writerStream = fs.createWriteStream('output.txt');
// 使用 utf8 编码写入数据
writerStream.write(data,'UTF8');
// 标记文件末尾
writerStream.end();
// 处理流事件 --> finish、error
writerStream.on('finish', function() {
console.log("写入完成。");
});
writerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序执行完毕");
管道流
提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中
var fs = require("fs");
// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');
// 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');
// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);
console.log("程序执行完毕");
链式流
通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。
创建 compress.js 文件, 代码如下:
var fs = require("fs");
var zlib = require('zlib');
// 压缩 input.txt 文件为 input.txt.gz
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'));
console.log("文件压缩完成。");
执行完以上操作后,我们可以看到当前目录下生成了 input.txt 的压缩文件 input.txt.gz。
接下来,让我们来解压该文件,创建 decompress.js 文件,代码如下:
var fs = require("fs");
var zlib = require('zlib');
// 解压 input.txt.gz 文件为 input.txt
fs.createReadStream('input.txt.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('input.txt'));
console.log("文件解压完成。");
模块是node.js的基本组成,文件和模块一一对应,文件可以使js代码,json或C扩展
模块使用require就可以引入
var hello = require(’./hello’);引入的是一个hello.js文件(node.js默认是.js后缀)
node.js提供exports和require两个对象,exports是模块公开的接口,require从外部调用exports暴露的方法
如hello.js文件里
exports.world = function() {
console.log('Hello World');
}
使用exports把world变量变成一个模块的访问接口,这样使用 require(’./hello’) 加载这个模块后就可以直接访问exports对象的成员函数了
var hello = require('./hello');
hello.world();
有时候想把一个对象封装到模块里可以
//hello.js
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello;
这种方式就是可以直接使用Hello()函数了,hello.js文件里暴露的不是一个方法集合而是一个单独的hello()方法的访问接口
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();
模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports
在js里一个函数可以作为另一个函数的参数,node.js也一样
function say(word) {
console.log(word);
}
function execute(someFunction, value) {
someFunction(value);
}
execute(say, "Hello");
匿名函数
就是直接在调用方法的时候传入一个匿名函数过去作为参数
可以分析一下怎么使用函数传递让http服务器工作的
下面创建了一个函数在监听到8888端口异常的时候触发
var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
实际上引入http模块作为变量之后,调用http模块的产生服务器的方法的时候直接传入了一个匿名函数,里面定义了respond的参数内容和结束
所以也可以把匿名方法封装一下
var http = require("http");
function onRequest(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
向node.js路由传入请求的url和get/post参数,之后路由可以根据这些数据执行相关的代码
所以查看http请求需要从里面提取出请求的URL以及get/post参数
需要的数据都包含在request对象里,此对象作为onRequest()回调函数的第一个参数传递,解析这些数据还需要url和querystring两个模块
实际上也可以使用querystring 模块来解析 POST 请求体中的参数
可以给onRequest() 函数加上一些逻辑,用来找出浏览器请求的 URL 路径
var http = require("http");
var url = require("url");
function start() {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
先把http和URL模块引入,写一个start函数,里面前天一个onequest函数,onrequest里有request和response参数,获取一下request里得到的url路径名,之后写响应头(回应200),响应内容之后结束response,start里写完呕暖equest函数之后监听服务器的8888端口,这样得到了每次监听到的请求的不同的url
此时可以写一个route.js的路由文档处理来自不同URL的请求
扩展 index.js,使得路由函数可以被注入到服务器中
//route.js
function route(pathname) {
console.log("About to route a request for " + pathname);
}
exports.route = route;
在添加更多逻辑之前可以看一下怎么把路由和服务器整合起来
服务器是应该知道路由的存在并有效利用路由的,可以使用硬编码的方式把该依赖项绑定到服务器上,但是非常麻烦,所以使用依赖注入的方式松散的添加路由模块
首先扩展一下服务器的 start() 函数,以便将路由函数作为参数传递过去,实际上只需要在服务器留一个route函数的传递接口参数,server.js 文件代码如下
var http = require("http");
var url = require("url");
function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(pathname);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
同时,我们会相应扩展 index.js,使得路由函数可以被注入到服务器中:
// index.js
var server = require("./server");
var router = require("./router");
server.start(router.route);
启动
node index.js
js里有全局对象,全局对象和其所有属性都是可以在程序的任何地方访问的,在浏览器的js 里,窗口windows是迁居对象,node.js里则是global,除了global对象本身,其他的全局变量都是global对象的属性,在node.js里可以直接访问global的属性而不需要在应用里包含它
global的作用就是作为其他全局变量的宿主,按照 ECMAScript 的定义,满足以下条 件的变量是全局变量:
在最外层定义的变量
全局对象的属性
隐式定义的变量(未定义就直接赋值的变量)
只要定义一个全局变量,此变量就会成为全局对象global的属性,不过在node.js里不能在最外层定义变量,因为所有用户代码都属于当前模块,模块本身不是最外层的上下文
最后不用使用var定义变量,这样会引入全局变量,污染命名空间,提供代码耦合风险
__filename是当前正在执行的脚本的文件名,会输出文件所在位置的绝对路径,并且和命令行指定的文件名不一定相同,在模块里返回的是模块文件的路径(包括文件名)
__dirname 表示当前执行脚本所在的目录,不展示文件名
setTimeout(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。:setTimeout() 只执行一次指定函数。
返回一个代表定时器的句柄值。
clearTimeout( t ) 全局函数用于停止一个之前通过 setTimeout() 创建的定时器。 参数 t 是通过 setTimeout() 函数创建的定时器
setInterval(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)
setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭(ctrl + c )
console 对象,用于向标准输出流(stdout)或标准错误流(stderr)输出字符
console.log([data][, ...])
向标准输出流打印字符并以换行符结束。该方法接收若干 个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则 以类似于C 语言 printf() 命令的格式输出。
console.info([data][, ...])
该命令的作用是返回信息性消息,这个命令与console.log差别并不大,除了在chrome中只会输出文字外,其余的会显示一个蓝色的惊叹号。
console.error([data][, ...])
输出错误消息的。控制台在出现错误时会显示是红色的叉子。
console.warn([data][, ...])
输出警告消息。控制台出现有黄色的惊叹号。
console.dir(obj[, options])
用来对一个对象进行检查(inspect),并以易于阅读和打印的格式显示。
console.time(label)
输出时间,表示计时开始。
console.timeEnd(label)
结束时间,表示计时结束。
console.trace(message[, ...])
当前执行的代码在堆栈中的调用路径,这个测试函数运行很有帮助,只要给想测试的函数里面加入 console.trace 就行了。
console.assert(value[, message][, ...])
用于判断某个表达式或变量是否为真,接收两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为false,才会输出第二个参数,否则不会有任何结果。
process 是全局变量
可以描述当前node.js进程状态,提供了一个与操作系统的简单接口
process 对象的一些最常用的成员方法:
exit
当进程准备退出时触发。
beforeExit
在node清空事件循环并且没有其他安排的时候触发这个事件,这样就可以让node继续执行
uncaughtException
一个异常冒泡回到事件循环的时候可以触发这个事件,不过在给异常添加了监视器的情况下,不会触发默认的操作(打印堆栈跟踪信息并退出)
Signal 事件
进程收到信号的时候触发,有一个详细的信号处理列表--可以看posix信号名
process的属性
标准输入输出和标准错误流
argv返回一个由命令执行脚本时的各个参数组成的数组,第一个参数一般是node,第二个一般是脚本的文件名,其他是脚本文件的参数
execPath返回当前脚本的node二进制文件的绝对路径
execArgv返回命令行下执行脚本时在node可执行文件和脚本文件之间的命令行参数的数组
env返回shell的环境变量
exitCode是进程退出时的代码,如果进程通过process.exit()退出的话不需要指定退出码
version返回node的版本
versions返回node的版本和依赖
config是一个包含用来编译当前node执行文件的js配置选项的对象,与运行./config脚本生成的config.gypi文件相同
pid是当前进程的进程号
title是进程名,默认为node,可以自定义
arch是当前CPU的架构,arm或ia32或x64
platform是运行程序所在平台系统,'darwin', 'freebsd', 'linux', 'sunos' 或 'win32'
mainModule是require.main的备选方案,不同之处在于,主模块运行时改变,require.main依然可能返回老的模块,这两个实际上是阴影了同一个模块
process方法
abort()可以让node退出并生成一个核心文件
chdir(directory)可以改变当前工作进程的目录,操作失败的话会抛出异常
cwd()返回当前进程的工作目录
exit([code])可以使用指定的代码结束进程
getgid()可以获取进程的群组标识,可以接受数字id或群组名,如果指定了群组名,会阻塞等待解析为数字id
getuid()可以获取进程的用户标识,此为数字的用户id不是用户名且只在posix平台可以使用
getgroups(groups)设置进程的群组id,是授权操作需要root权限或者cap_setgid能力
initgroups(user,extra_group)可以读取/etc/group,并初始化群组访问列表,使用成员所在的所有群组,是授权操作,需要root权限或cap_setgid能力
kill(pid[, signal])可以发送信号给进程id,signal是发送的信号的字符串描述,信号名是字符串,比如 'SIGINT' 或 'SIGHUP'
memoryUsage()返回node进程的内存使用情况,单位是字节
nextTick(callback)事件循环结束之后调用回调函数
umask([mask])设置或读取进程文件的掩码,子进程从父进程继承掩码,mask参数有效的话返回旧的掩码,否则返回当前掩码
uptime()返回node已经运行的描秒数
hrtime()返回当前进程的高分辨事件,,形式为 [seconds, nanoseconds]数组,与日期无关不受时钟漂移的影响,把之前的结果传递给当前进程的hrtime()会返回两者之间的时间差,用于基准和测量时间间隔
util
可以提供一些弥补js功能不足的常用函数
util.callbackify(original) 可以把async异步函数变成一个异常优先风格的函数,在回调函数里第一个参数是拒绝的原因,(如果是promise已经解决就返回null),第二个参数是解决的值
回调函数是异步执行的,且有异常堆栈错误跟踪,一旦回调函数抛出一个异常,进程会触发一个未捕获的异常,没有被捕获的话,进程会退出
null在回调函数里,如果回调函数的首个参数是promise拒绝的原因并且带有返回值,并且值可以转换为布尔值false,此值可以被封装在error对象里,可以通过属性reason获取
util.inherits(constructor, superConstructor) 是一个实现对象间原型继承的函数
js的面向对象的特性是基于原型的,与基于类是不同的,js无法进行对象继承,所以是通过原型复制实现继承功能
util.inspect(object,[showHidden],[depth],[colors]) 是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出。它至少接受一个参数 object,即要转换的对象
showHidden是一个可选参数,如果值为true会输出更多的隐藏信息
depth表示最大递归的层数,对象复杂的话可以指定层数控制输出信息的多少,不指定的话,默认会递归2 层,指定为null表示会将不限递归层数完整遍历对象,colors的值是团社的话,输出格式会以ansi的颜色编码
util.inspect 并不会简单地直接把对象转换为字符串,即使该对 象定义了 toString 方法也不会调用
node.js提供一组类似 UNIX(POSIX)标准的文件操作API
导入方法:
var fs = require("fs")
异步和同步
fs模块里的方法都有同步和异步版本,如读取文件内容就有异步的 fs.readFile() 和同步的 fs.readFileSync()方法
异步方法的最后一个函数是回调函数,回调函数的第一个参数包含了错误信息
建议使用异步方法,比起同步,异步方法性能更高速度更快且没有阻塞
使用fs.readFile方法需要传入需要读取的文件以及读取文件之后的处理方法
fs.readFileSync的同步读取方法只需要传入需要读取的文件,它只返回文件内容
在异步模式下打开文件的语法格式:
fs.open(path, flags[, mode], callback)
path是文件打开的路径,flags是文件打开的方式(只读,追加,写入等),莫得设置文件模式(权限,文件创建的默认权限是0666-可读可写),callback是回调函数,一般带有两个参数,如callback(err, fd)
stats–异步获取文件信息
可以判断文件是文件还是目录,是块设备还是字符设备,是不是软链接,是不是fifo,是不是socket
异步获取文件信息语法
fs.stat(path, callback)
单纯打印stat获取的文件信息的数据可以看到里面有文件的dev,mode,nlink,uid,gid,ino ,size ,blocks,atime 等参数
异步模式下写入文件的语法格式:
fs.writeFile(file, data[, options], callback)
默认覆盖原有文件
callback是只包含error,在写入失败的时候返回
异步模式下读取文件的语法格式:
fs.read(fd, buffer, offset, length, position, callback)
使用了文件描述符读取文件,参数:
fd--通过fs.open()返回的文件描述符
buffer--数据写入的缓冲区
offset--缓冲区写入的写入偏移量
length--要从文件里读取的字节数
position--是文件读取的起始位置,position值为null就会从当前文件指针的位置读取
callback - 回调函数,有三个参数err, bytesRead, buffer,err 为错误信息, bytesRead 表示读取的字节数,buffer 为缓冲区对象。
异步模式下关闭文件的语法格式
fs.close(fd, callback)
异步模式下截取文件部分内容的语法格式:
fs.ftruncate(fd, len, callback)
使用了文件描述符来读取文件。
删除文件的语法格式
fs.unlink(path, callback)
创建目录的语法格式
fs.mkdir(path[, options], callback)
读取目录的语法格式
fs.readdir(path, callback)
删除目录的语法格式:
fs.rmdir(path, callback)
GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此你可以手动解析后面的内容作为GET请求的参数
url 模块中的 parse 函数提供了这个功能
var http = require('http');
var url = require('url');
var util = require('util');
http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
// 解析 url 参数
var params = url.parse(req.url, true).query;
res.write("网站名:" + params.name);
res.write("\n");
res.write("网站 URL:" + params.url);
res.end();
}).listen(3000);
POST 请求的内容全部的都在请求体中,http.ServerRequest 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作。比如上传文件,而很多时候我们可能并不需要理会请求体的内容,恶意的POST请求会大大消耗服务器的资源,所以 node.js 默认是不会解析请求体的,当你需要的时候,需要手动来做
var http = require('http');
var querystring = require('querystring');
var util = require('util');
http.createServer(function(req, res){
// 定义了一个post变量,用于暂存请求体的信息
var post = '';
// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
req.on('data', function(chunk){
post += chunk;
});
// 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
req.on('end', function(){
post = querystring.parse(post);
res.end(util.inspect(post));
});
}).listen(3000);
1 OS 模块
提供基本的系统操作函数。
2 Path 模块
提供了处理和转换文件路径的工具。
3 Net 模块
用于底层的网络通信。提供了服务端和客户端的的操作。
4 DNS 模块
用于解析域名。
5 Domain 模块
简化异步代码的异常处理,可以捕捉处理try catch无法捕捉的。
web服务器一般指网站服务器,提供信息浏览服务,只需要支持http,html及url即可
大多数web服务器都支持服务端的脚本语言(php、python、ruby)等,并通过脚本语言从数据库获取数据,将结果返回给客户端浏览器。
目前最主流的三个Web服务器是Apache、Nginx、IIS。
可以使用node创建web服务器
使用http模块,http模块主要就是用于搭建http服务端和客户端,使用http服务器或客户端功能必须调用http模块
是node.js的简单的web应用框架,提供可以创建各种web应用的强大特性和丰富的http工具
express框架核心特性:
可以设置中间件响应http请求
定义了路由表用于执行不同的http请求
可以通过向模板传递参数动态渲染html页面
可以使用cnpm安装,并保存到依赖列表里
$ cnpm install express --save
会将 Express 框架安装在当前目录的 node_modules 目录中, node_modules 目录下会自动创建 express 目录。以下几个重要的模块是需要与 express 框架一起安装的:
$ cnpm install body-parser --save
$ cnpm install cookie-parser --save
$ cnpm install multer --save