nodejs基础知识
nodejs和js的异同
相同点 | javascript和nodejs |
---|---|
以ECMAScript为基础 | 数据结构的定义,语法、内置对象和方法等都相同。 |
不同点 | javascript | nodejs |
---|---|---|
顶层对象 | window | global |
操作对象 | 操作浏览器,属前端范畴。包括↓ DOM: 操作页面元素的方法; BOM: 操作浏览器元素的方法。 |
操作系统/网络等,更偏向于后端。包括↓ OS: 操作操作系统的方法; file: 操作文件系统的方法; net: 操作网页的方法; database: 操作数据库的方法; net: 操作网页的方法。 |
模块概念 | 无 | 有 |
nodejs的使用
nodejs软件是无界面形式的,打开后类似于命令行,且可以直接在命令行里打开nodejs文件。
操作命令行
- windows下,通过
win+r
可以直接打开运行,输入cmd,打开命令行界面 - 直接敲
node
+回车,若得到>和闪动的光标
,表示node安装正确 - 按2次
ctrl+c
可结束执行进程 - win7在命令行里输入
CLS
可清屏 - 命令行里输入
node+空格+要运行的程序
- 命令行内不区分大小写
cd
回到根目录,cd..
回到上级目录后面输入路径,
>
后面输入命令
nodejs中的模块
- 在node中,文件和模块是一一对应的,一个文件就是一个模块
- 每个模块都有自己的作用域
- 通过var申明的并不是全局变量,而是该模块作用域下的变量
- node中要通过
global.变量名
的方式申明全局变量,前面不用加var - node中,像全局变量,实际上是当前模块下的变量有:
__filename
→ 当前文件被解析后的绝对路径,是属性,末尾无括号
模块加载机制
在js中通过
<script>
标签引入其他js文件,但在node中没有标签的概念,它是用模块加载机制
来引入其他文件。即:
require
方法 → require('模块地址'); → 使用时要注意两个问题:
- 一是路径问题:
1. 地址之间用/,而不是
2. 既可以写绝对地址,也可以写相对地址
3. 写相对地址时,若是同一文件夹下的文件,不能省略./
4. 一旦直接写文件名,会加载node中的核心模块,也就是node_modules
中的模块
- 二是文件查找问题:
查找顺序:文件名→文件名.js→文件名.json→文件名.node→还找不到,报错
module和exports
在node中用var定义的变量是当前模块下的变量,无法直接和外部模块共享,解决办法:
- 用
global
将变量定义为全局变量;→不推荐这样做 - 利用每个模块内置的module对象;
- 利用每个模块内置的exports对象;
module对象
module对象保存和提供了与当前模块有关的一些信息,比如,id是当前模块的唯一标识;filename是当前模块的文件名称;children是当前模块加载的其他模块的信息。
module对象中有一个子对象,即exports属性,可以将当前模块中的变量暴露出去;使用方法:module.exports.属性名=变量名;例:
var a=20;
module.exports.a=a;
require方法返回的就是被调用模块的module.exports属性;
exports对象
每个模块中内置的exports对象===该模块下的module.exports属性
一旦改写(不是添加)exports或module.exports,他们就没有指向关系了,所以尽量只是添加,不要改变他们的指向关系
process对象
process是全局对象
stdin/stdout: 标准输入输出流(IO)
stdin和stdout提供了操作输入和输出数据的方法,通常也被称为IO操作
Buffer类
Buffer类用于操作二进制数据流
当为buffer对象分配空间大小后,长度是固定的,不能更改
可以修改长度内的数据,但不能添加数据
length指的是字节长度,不是字符串的长度
new Buffer(size);
创建一个buffer对象并为这个对象分配一个大小,size为数据类型
var bf = new Buffer(5);
console.log(bf);//<Buffer 88 f3 30 00 02>→打印出来的是16进制数据,数据随机
bf[6]=8;
console.log(bf);//<Buffer 88 f3 30 00 02>→没有出现第6位数
bf[0]="8";
console.log(bf);//<Buffer 08 f3 30 00 02>→第一个数被改写
new Buffer(array);
创建一个buffer数组
var bf = new Buffer(["ab","2",4]);
console.log(bf);//<Buffer 00 02 04>
bf[6]=8;
console.log(bf);//<Buffer 00 02 04>→同样不能添加数据
bf[0]="8";
console.log(bf);//<Buffer 08 02 04>→同样可以改写数据
new Buffer(string,[encoding]);
创建一个buffer对象
var bf = new Buffer("hua","utf-8");
console.log(bf);//<Buffer 68 75 61>→16进制
for (var i=0; i<bf.length;i++){
console.log(bf[i]);//打印出来的是2进制
console.log(String.fromCharCode(bf[i]));//将编码转换成字符串
}
// 104
// h
// 117
// u
// 97
// a
buffer.write()方法
buffer对象.write(要写入到buffer对象的字符串[,从buffer对象的第几位开始写入][,写入的字符串长度],[写入的字符串的编码]);
buffer.slice()和buffer.copy()方法
相同点:都是截取一个buffer对象,获得一个新的buffer对象
不同点:使用slice,改变新buffer的数据会同时改变原buffer数据;copy就不会
var str="caihua";
var bf=new Buffer(str);
console.log(bf);//<Buffer 63 61 69 68 75 61>
var bf1=new Buffer(5);
console.log(bf1);//<Buffer 00 00 00 00 38>
var bf2=bf.copy(bf1,1,2,3);
console.log(bf1);//<Buffer 00 69 00 00 38>
console.log(bf2);//1,返回的是写入到新bf1的长度
File System文件系统模块
该模块是核心模块,需要使用require方法导入fs模块后使用
该模块提供了操作文件的一些API
fs.open(path,flags,[mode],callback)
path:要打开的文件路径
flags:打开文件的方式,读/写
mode:设置文件的模式,读/写/执行,对应4/2/1
callback:回调函数
err:文件打开失败的错误提示保存在err里,打开成功err为null
fd:被打开文件的标识
举个栗子
var projectData={
"name":"caihua",
"fileData":[
{
"name":"css",
"type":"dir"
},{
"name":"js",
"type":"dir"
},{
"name":"index.html",
"type":"file",
"content":"<html>
<head>
<title>title</title>
</head>
<body>
<h1>caihua is nice</h1>
</body>
</html>"
//
是折行, 是缩进
}
]
};
var fs=require("fs");
if(projectData.name){
fs.mkdirSync(projectData.name);
var fileData=projectData.fileData;
if(fileData && fileData.forEach){//如果fileData存在,并且它是数组,有forEach方法
fileData.forEach(function(f){//f是数组fileData里的每一个对象
f.path=projectData.name+"/"+f.name;//构建一个完整的目录
f.content=f.content||"";//避免没有content时出错
switch(f.type){
case "dir":
fs.mkdirSync(f.path);
break;
case "file":
fs.writeFileSync(f.path,f.content);
break;
default:
break;
}
})
}
合并文件,举个栗了
var fs=require("fs");
var filedir="./test";//要合并内容的文件夹
fs.watch(filedir,function(ev,file){//监听要合并内容的文件夹里的内容
fs.readdir(filedir,function(err,datalist){//读取该文件夹下的内容
//datalist是一个数组,用来装该文件夹下的文件名
var arr=[];//用来装要合并内容的文件的路径
datalist.forEach(function(f){
if(f){
var info=fs.statSync(filedir+"/"+f);//参数是路径
if(info.mode==33206){
arr.push(filedir+"/"+f);
}
}
});
var content="";//用来装该文件夹下每个文件的内容
arr.forEach(function(f){
var c=fs.readFileSync(f);
content+=c.toString()+"
";
});
fs.writeFile("./index.js",content);
})
});
用到的方法:
- fs.watch(文件夹所在路径,回调函数)→监听文件夹内的变化
- fs.readdir(文件夹所在路径,回调函数)→读取文件夹下所有文件
- fs.statSync(文件所在路径)→读取文件详情
- fs.readFileSync(文件所在路径)→读取文件内容,得到的是buffer对象,二进制,可以用toString方法转成字符串, 是换行
- fs.writeFile(要写入内容的文件的路径,要写入的内容)→写入内容
使用node进行web开发
- 用户通过浏览器发送http请求到指定的主机(服务器)
- 服务器接收请求,处理请求
- 服务器处理完成,返回对应的数据到用户机器(浏览器)
- 浏览器接收数据并进行分析和处理
浏览器即客户端,服务器就是服务端,请求过程就是两台机器之间的数据通信,即两台机器的交互过程
要进行web开发,必须搭建一个服务器来接收来自任何客户端的链接和请求,并进行对应的处理
nodejs中可以通过http模块来搭建服务器(非nodejs的核心模块)
http模块
用到的方法
- http.createServer(回调函数);→创建并返回一个web服务器对象。
- server.listen(port,[hostname,][backlog,][callback])→
listen方法
可以用来设定监听哪个网卡的哪个端口的数据。- server.address();→{ address: '::', family: 'IPv6', port: 9960 }→获取服务器地址,address不设置,默认监听所有网卡过来的数据;端口可以由node自动分配,每刷新一次,端口会变,所以建议手动设置端口。端口不能随意设置,1-1024之间的端口号已经被约定给系统或应用程序等使用(ipachy监听的端口号是80,ftp监听的端口号是21),所以建议将端口设置大一点。
- server.on("error",function(err){});→该方法是为了预防端口设置错误,当端口设置错误时触发。
- server.on("listening",function(){});→当端口设置正确时触发。
- server.on("request",function(){});→当向该地址的该端口发送请求时触发。
7.http.STATUS_CODES;→是一个对象,全部的标准http响应状态码的集合和简短描述。
端口的概念
网卡是一个数据交互的大通道,如果没有端口,任意程序可以监听网卡上的任意数据,会造成混乱,于是诞生了端口。网络数据交互的程序只能监听从对应端口进入的数据,一个应用程序可以监听多个端口的数据,一个端口的数据只能被一个应用程序监听。
request请求
- server.on("request",function(){})的request事件其实就是http.createServer(回调函数)里的回调函数,函数有两个参数request和response。request提供了一个客户端信息对象,会存储和客户端发送的请求有关的信息;response则提供了一个服务端信息对象,比如用来处理服务端向客户端发送的数据。
- request和response都是http对象的实例
request对象包含的重点内容
- httpVersion:使用的http版本信息,一般为1.1
- headers:请求头信息中的数据
- url:请求的地址
- method:请求方式
response对象包含的重点内容
- write(chunk,[encoding]):发送一个数据块到响应正文中。
- end([chunk,][encoding]):当所有的正文和头信息发送完了后调用该方法告诉服务器,数据已发送完成。这个方法在每次发送完信息后必须调用,且是在最后调用。
- statusCode:该属性用来设置返回的状态码
- setHeader(name,value):设置返回头信息
- writeHead(statusCode,[reasonPhrase],[headers]):这个方法只能在当前请求中使用一次
用户发送http请求→服务器响应并处理数据→返回给客户端→客户端接收数据并进行处理
url模块
url.parse(路径名);→将url解析成一个对象,方便调用
举个栗子
var http=require("http");
var url=require("url");
var server=http.createServer();
server.listen(8080,"localhost");
server.on("request",function(req,res){
var urlStr=url.parse(req.url);
switch (urlStr.pathname){
case "/":
//首页
res.writeHead(200,{
"content-type":"text/html;charset=utf-8"
});
res.end("<h1>这是首页</h1>");
break;
case "/users":
//用户页面
res.writeHead(200,{
"content-type":"text/html;charset=utf-8"
});
res.end("<h1>这是用户页面</h1>");
break;
default:
res.writeHead(404,{
"content-type":"text/html;charset=utf-8"
});
res.end("<h1>这是错误页面</h1>");
break;
}
});
使用fs模块实现行为表现分离
用到的方法和属性
__dirname
是全局变量。在任何模块文件内部,可以使用__dirname变量获取当前模块文件所在目录的完整绝对路径。- fs.readFile(file,function(err,data){});→读取文件内容