1.什么是NodeJS
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
2.nodejs基础知识
global模块-全局变量
Node.js 中的全局对象是 global, 类似于浏览器中的window
常用的global属性:
console: 用于打印日志
setTimeout/clearTimeout: 设置清除延时器
setInterval/clearInterval: 设置清除定时器
__dirname: 当前文件的路径,不包括文件名
__filename: 获取当前文件的路径,包括文件名
//与模块化相关的,模块化的时候会用到
require
exports
module
注意:
- 除了global模块中的内容可以直接使用,其他模块都是需要加载的。
- 其他模块不是全局的,不能直接使用。因此需要导入才能使用。
fs模块
fileSystem-- 文件系统,提供了一系列操作文件的API接口,可以方便我读写文件
读取文件
语法:fs.readFile(path[, options], callback)
方式一:不传编码参数
//参数1: 文件的名字
//参数2: 读取文件的回调函数
//参数1:错误对象,如果读取失败,err会包含错误信息,如果读取成功,err是null
//参数2:读取成功后的数据(是一个Buffer对象)
fs.readFile("data.txt", function(err, data){
console.log(err);
console.log(data);
// 可以通过data.toString() 把二进制数据转成文本,当然前提是读取的文件本来就是文本,如果是图片获取的二进制就无法转换成文本
});
方式二:传编码参数
//参数1: 文件的路径
//参数2: 编码,如果设置了,返回一个字符串,如果没有设置,会返回一个buffer对象
//参数3: 回调函数
fs.readFile("data.txt", "utf8",function(err, data){
console.log(err);
console.log(data);
});
写文件
语法:fs.writeFile(file, data[, options], callback)
//参数1:写入的文件名(如果文件不存在,会自动创建)
//参数2:写入的文件内容(注意:写入的内容会覆盖以前的内容)
//参数3:写文件后的回调函数
fs.writeFile("2.txt", "hello world, 我是一个中国人", function(err){
if(err) {
return console.log("写入文件失败", err);
}
console.log("写入文件成功");
});
注意:
写文件时,会把文件中原本的内容替换
追加文件
语法:fs.appendFile(path, data[, options], callback)
//参数1:追加的文件名(如果文件不存在,会自动创建)
//参数2:追加的文件内容(注意:写入的内容会覆盖以前的内容)
//参数3:追加文件后的回调函数
fs.appendFile("2.txt", "我是追加的内容", function(err){
if(err) {
return console.log("追加文件内容失败");
}
console.log("追加文件内容成功");
})
文件同步与异步的说明
fs中所有的文件操作,都提供了异步和同步两种方式
1.异步方式:不会阻塞代码的执行
2.同步方式:会阻塞代码的执行
stream
stream是Node.js提供的又一个仅在服务区端可用的模块,目的是支持“流”这种数据结构。
什么是流?流是一种抽象的数据结构。想象水流,当在水管中流动时,就可以从某个地方(例如自来水厂)源源不断地到达另一个地方(比如洗手池)。我们也可以把数据看成是数据流,比如你敲键盘的时候,就可以把每个字符依次连起来,看成字符流。这个流是从键盘输入到应用程序,实际上它还对应着一个名字:标准输入流(stdin)。
如果应用程序把字符一个一个输出到显示器上,这也可以看成是一个流,这个流也有名字:标准输出流(stdout)。流的特点是数据是有序的,而且必须依次读取,或者依次写入,不能像Array那样随机定位。
有些流用来读取数据,比如从文件读取数据时,可以打开一个文件流,然后从文件流中不断地读取数据。有些流用来写入数据,比如向文件写入数据时,只需要把数据不断地往文件流中写进去就可以了。
在Node.js中,流也是一个对象,我们只需要响应流的事件就可以了:data事件表示流的数据已经可以读取了,end事件表示这个流已经到末尾了,没有数据可以读取了,error事件表示出错了。
举例:
'use strict';
var fs = require('fs');
// 打开一个流:
var rs = fs.createReadStream('sample.txt', 'utf-8');
rs.on('data', function (chunk) {
console.log('DATA:')
console.log(chunk);
});
rs.on('end', function () {
console.log('END');
});
rs.on('error', function (err) {
console.log('ERROR: ' + err);
});
要注意,data事件可能会有多次,每次传递的chunk是流的一部分数据。
要以流的形式写入文件,只需要不断调用write()方法,最后以end()结束:
'use strict';
var fs = require('fs');
var ws1 = fs.createWriteStream('output1.txt', 'utf-8');
ws1.write('使用Stream写入文本数据...\n');
ws1.write('END.');
ws1.end();
var ws2 = fs.createWriteStream('output2.txt');
ws2.write(new Buffer('使用Stream写入二进制数据...\n', 'utf-8'));
ws2.write(new Buffer('END.', 'utf-8'));
ws2.end();
所有可以读取数据的流都继承自stream.Readable,所有可以写入的流都继承自stream.Writable。
pipe
就像可以把两个水管串成一个更长的水管一样,两个流也可以串起来。一个Readable流和一个Writable流串起来后,所有的数据自动从Readable流进入Writable流,这种操作叫pipe。
在Node.js中,Readable流有一个pipe()方法,就是用来干这件事的。
让我们用pipe()把一个文件流和另一个文件流串起来,这样源文件的所有数据就自动写入到目标文件里了,所以,这实际上是一个复制文件的程序:
'use strict';
var fs = require('fs');
var rs = fs.createReadStream('sample.txt');
var ws = fs.createWriteStream('copied.txt');
rs.pipe(ws);
默认情况下,当Readable流的数据读取完毕,end事件触发后,将自动关闭Writable流。如果我们不希望自动关闭Writable流,需要传入参数:
readable.pipe(writable, { end: false });
path模块
在读写文件的时候,文件路径可以写相对路径或者绝对路径
//data.txt是相对路径,读取当前目录下的data.txt, 相对路径相对的是指向node命令的路径
//如果node命令不是在当前目录下执行就会报错, 在当前执行node命令的目录下查找data.txt,找不到
fs.readFile("data.txt", "utf8", function(err, data) {
if(err) {
console.log("读取文件失败", err);
}
console.log(data);
});
- 相对路径:相对于执行node命令的路径
- 绝对路径:__dirname: 当前文件的目录,__filename: 当前文件的目录,包含文件名
path模块的常用方法
关于路径,在linux系统中,路径分隔符使用的是/,但是在windows系统中,路径使用的\
path.join();//拼接路径
//windows系统下
> path.join("abc","def","gg", "index.html")
"abc\def\gg\a.html"
//linux系统下
> path.join("abc","def","gg", "index.html")
'abc/def/gg/index.html'
http模块
创建服务器步骤:
// 移入http模块
const http = require('http')
// 调用创建http 服务器的方法
const server = http.createServe()
// 给服务器注册request事件监听,每次浏览器像服务器发送请求的时候都会被监听到
server.on('request', function(request, response){
// request 浏览器请求的数据,包括请求方式method 请求的地址 url等
// response 浏览器的响应,可以设置响应头、响应体、响应状态码
const method = request.method
const url = request.url
// 设置响应的状态码
response.StatusCode = 404
// 设置响应的头
response.setHeader('Content-Type', 'text/html');
// 设置响应体内容,write可以调用多次
response.write('hello world!')
// 响应结束
response.end()
// 如果在end(content),这样的写法相当于是让write和end的合写
response.end('hello world!')
})
// 给服务器设置监听,相当于启动服务器
server.listen(8888,function(){
console.log('服务器启动成功')
})
// 简写方式
http.createServer((req,res) => {
....
}).listen(8888,() => {
....
})
- 给服务器注册request事件,只要服务器接收到了客户端的请求,就会触发request事件
- request事件有两个参数,request表示请求对象,可以获取所有与请求相关的信息,response是响应对象,可以获取所有与响应相关的信息。
- 服务器监听的端口范围为:1-65535之间,推荐使用3000以上的端口,因为3000以下的端口一般留给系统使用
3.模块化(module)
基本概念:在nodejs中,应用由模块组成,nodejs中采用commonJS模块规范。
- 一个js文件就是一个模块
- 每个模块都是一个独立的作用域,在这个而文件中定义的变量、函数、对象都是私有的,对其他文件不可见。
node中模块分类
- 核心模块:由 node 本身提供,不需要单独安装(npm),可直接引入使用
- 第三方模块:由社区或个人提供,需要通过npm安装后使用
- 自定义模块:由我们自己创建,比如:tool.js 、 user.js
核心模块
- fs:文件操作模块
- http:网络操作模块
- path:路径操作模块
- 基本使用:1 先引入 2 再使用
第三方模块
- 第三方模块是由 社区或个人 提供的
- 比如:mime模块/art-template/jquery…
- 基本使用:1 先通过npm下载 2 再引入 3 最后使用
用户自定义模块
- 由开发人员创建的模块(JS文件)
- 基本使用:1 创建模块 2 引入模块
- 注意:自定义模块的路径必须以./获取…/开头
模块导入
/*
nodejs中模块分为3大类
1. nodejs本身提供的核心模块 fs http path url querystring
核心模块不需要安装,直接导入即可。
核心模块的加载语法: const fs = require('fs')
2. 第三方模块 mime art-template
第三方模块: 必须先安装(npm install XXX) 才能导入
第三方模块的加载语法: npm install XXX const mime = require('mime')
3. 自定义的模块 一个js文件
不需要安装 只需要自己创建一个js文件
自定义模块的加载语法: require('模块的路径') 模块不能是名字,必须是路径 ./ ../ .js后缀是可以省略
require加载规则(以mime模块为例)
1. 判断是否是路径, 如果是 就是自定义模块
2. 如果是名字 判断是否是核心模块
3. 如果是第三方模块 在当前目录找node_modules
4. 在node_modules中查找mime文件夹
5. 查找是否有package.json, 查看是否main属性
6. 判断是否有main, 如果没有,默认查找index.js index.json index.node
7. 如果没有
8. 如果找不到,就去上一层目录,一直找到根目录
9, 如果还没有,就说明模块不存在
*/
模块导出
/*
1. 模块中定义的变量和函数都是私有的
2. 任意的一个模块中, 都有自带一个属性 module (全局属性) module代表的就是当前的这个模块。
3. module中有一个属性 exports ,这个exports属性是一个对象,代表的就是当前模块的导出 module.exports当前模块唯一能够被外界访问到的
*/
//通过module.exports对外导出一些值
module.exports = 值 只能导出一个值
module.exports = {} 可以把所有要导出的内容都放到一个新的对象中
module.export.xxx = 值
/*
在任意的模块中 module.exports表示该模块的导出
为了我们方便导出, 每个模块中还提供了 exports
exports 初始状态下,和module.exports指向了同一个对象。
注意点: 如果通过exports的方式来导出内容,只能给对象增加属性 不能替换这个对象
*/
// 我们真正到处的对象是module.exports指向的对象
exports = {} // 这样只是改了exports的指向,而module.exports的指向没有改变,所以这样是不对的
// 以下这种是允许的
exports.xxx = '值'