1.什么是Node.js
1.1Node.js是一款基于“V8引擎”的javaScript运行环境
V8引擎:V8引擎是一款专门解释和执行JS代码的虚拟机,任何程序只要集成了V8引擎都可以执行JS代码
总结:Node.js不是一门语言,它是一个运行环境,这个运行环境下可以运行我们编写的JS代码,这个运行环境最大的特点就是提供了操作“操作系统底层的API”,通过底层的API我们可以编写出网页中无法实现的功能(诸如:打包工具,网站服务器等)
1.2 Node.js环境的搭建
(1)node.js官网地址:https://nodejs.org/zh-cn/
(2)官网下载msi安装包
(3)全程下一步
(4)安装完成后在工具中输入 node -v
推荐借助NVM的搭建方式
(1)下载NVM:https://githup.com/coreybutler/nvm-windows
(2)创建一个目录
(3)在目录中创建NVM和NODE子目录,把nvm包解压到nvm中
(4)在install.cmd文件上面右键选择(以管理员身份运行)
在弹出来的终端中直接按下回车,将弹出的文件另存为到NVM中,打开并修改root:安装NVM的目录,path:安装node的文件路径
(5)配置环境变量
1.3 NodeJS执行javaScript代码
(1)REPL环境:REPL(Read Eval Print Loop),直接在命令行里编写JS代码:打开终端,输入node 回车
(2)以文件的形式执行:找到js文件路径,打开终端:node xxx.js
(3)在开发工具中运行
1.4 NodeJs环境和浏览器环境执行JS代码的区别
(1)内置对象的不同:浏览器是window,node里的是global
(2)this默认指向不同
浏览器环境:this=>window(默认)
NodeJS环境:this =>{}(默认指向空对象)
(3)API不同
浏览器环境:提供了操作节点的DOM相关API和操作浏览器BOM相关的API
NodeJS环境:没有HTML节点也没有浏览器,NodeJs环境中没有DOM/BOM
1.5 模块
(1)浏览器开发中的模块:在浏览器开发中为了避免命名冲突,方便维护等,采用类或者立即执行函数的方式来封装代码,这里的类或者函数就是浏览器开发中的一模块(问题:人多杂乱无标准)
(2)NodeJS中的模块:nodeJs采用CommonJS规范,它规定了如何定义一个模块,如何暴露(导出)模块中的变量函数,以及如何使用定义好的模块
CommJs规范中一个文件就是一个模块
CommJs规范中每个文件中的变量函数都是私有的,对其他文件不可见
CommJs规范中每个文件中的变量函数必须通过exports暴露之后才能使用
CommJs规范中想要使用其它文件暴露的变量函数必须通过require()导入模块才可以使用
1.6 NodeJs全局属性和方法
(1)_dirname:被执行的JS文件所在的目录 D:\dev\study
(2)_filename:被执行的JS文件的绝对路径 D:\dev\study\xxx.js
(3)定时器:setInterval setTimeout cleanInterval cleanTimeout
(4)exports:导出
(5)module.exports:导出
(6)require():导入
(7)全局对象导出 globle.xxx =xxx 但是使用必须通过require导入
(8)exports和module.exports的区别
exports =xxx:此时拿到的是一个空对象,不可以直接赋值
module.exports = xxx :能够拿到具体的值,可以直接赋值
1.7 require导入注意点
(1)require()导入模块是可以不添加导入模块的类型,如果没有指定导入模块的类型,那么会依次查找.js .json .node文件,无论哪三种类型,导入之后都会转换成JS对象返回给我们
(2)导入自定义模块时必须指定路径(./xxx),require()可以导入“自定义模块、系统模块、第三方模块”,导入自定义模块时必须加上路径,导入系统模块和第三方模块时不用添加路径
1.8 Node包和包管理
(1)简而言之:一个模块就是一个单独的文件,一个包中可以有一个或多个模块
(2)NodeJS包管理:在NodeJS中为了方便开发人员发布、安装和管理,NodeJS推出了一个包管理工具NPM(Node Package Manager),NPM不需要单独安装,只要搭建好NodeJS环境就自动安装好了。
2.NPM使用
2.1全局安装
(1)npm install list :查看npm的基本配置
(2)npm install -g nrm 安装nrm 安装最新版本
(3)npm uninstall -g nrm 卸载nrm
(4)npm install -g nrm@xxx 安装指定版本
(5)npm update -g nrm 更新指定的包到最新版本
2.2本地安装
本地安装:一般用于安装当前项目使用的包,存储在当前项目node_modules中
(1)npm init -y 初始化package.json文件
(2)npm install 安装所有的包
(3)npm install --production 只会安装dependencies中的包
(4)npm install --development 只会安装devDependencies中的包
2.3.NRM使用
(1)全局安装nrm:npm install -g nrm
(2)nrm --version 查看nrm的版本
(3)nrm ls 查看允许切换的资源地址
(4)nrm use taobao 将下载地址切换到淘宝
(5)切换以后还是通过npm指令进行安装
2.4.CNPM使用
(1)npm install cnpm -g -registry://registry.npm.taobao.org
(2)个人不推荐.....
2.5.Yarn使用
(1)npm install -g yarn
(2)yarn --versin
(3)yarn add xxx 安装xxx 默认安装到dependencies
(4)yarn add --save 安装到dependencies
(5)yarn add --dev 安装到devDependencies
(6)yarn remove xxx 卸载xxx依赖
(7)yarn upgrade add xxx 更新xxx版本
3.NodeJs核心API
(1)Buffer类
Buffer是NodeJs全局对象上的一个类,是一个专门用于存储字节数据的类
1.创建一个Buffer对象
Buffer.alloc(size,fill,encoding) size:长度 fill:填充数 encoding:编码
let str = Buffer.alloc(3)
console.log(str);//<Buffer 00 00 00>
注意点:通过console.log输出Buffer,会自动将存储的内容转换成16进制再输出。
let buf = Buffer.alloc(3,17);
console.log(buf);//<Buffer 11 11 11>
2.根据数组/字符串创建一个Buffer对象
Buffer.from(string,encoding)
如: let buf = Buffer.from("abc");
console.log(buf);//<Buffer 61 62 63>
let buf2 = Buffer.from([1,3,5]);
3.Buffer对象的本质就是一个数组
4.toString()
5.write(data,offset,length,encoding)
data:写入的数据
offset:从哪儿开始写入
length:写入的长度
encoding:默认utf-8
6.slice(n,m)
n:从索引n开始截取
m:截取到索引m(不包含索引m的值)
7.isEncoding()//检查Buffer是否支持某种编码格式
let res = Buffer.isEncoding("utf8")
console.log(res);//true
8.isBuffer()//检查是否是Buffer类型对象
let obj = {};
let res =Buffer.isBuffer(obj);
console.log(res);//false
9.byteLength();//获取Buffer实际字节长度
let buf = Buffer.from("123");
let buf2 = Buffer.from("秋风");
let res = Buffer.byteLength(buf);//3
let res2 = Buffer.byteLength(buf2);//6
10.concat([a1,a2]);//合并Buffer中的数据
let buf1 = Buffer.from("123");
let buf2 = Buffer.from("abc");
let buf3 = Buffer.from("xxx");
let res = Buffer.concat([buf1, buf2, buf3]);
console.log(res);
console.log(res.toString());//123abcxxx
(2)Path 路径模块
Path和Buffer一样,都是NodeJS中一个特殊的模块,不同的是Buffer模块已经添加到Global上了, 所以不需要手动导入而Path模块没有添加到Global上, 所以使用,时需要手动导入
let path = require("path");
1.basename用于获取路径的最后一个组成部分
let res = path.basename('/a/b/c/d/index.html');
let res = path.basename('/a/b/c/d');
let res = path.basename('/a/b/c/d/index.html', ".html");
console.log(res);
2.dirname用于获取路径中的目录, 也就是除了最后一个部分以外的内容
let res = path.dirname('/a/b/c/d/index.html');
let res = path.dirname('/a/b/c/d');
console.log(res);
3.extname用于获取路径中最后一个组成部分的扩展名
let res = path.extname('/a/b/c/d/index.html');
let res = path.extname('/a/b/c/d');
console.log(res);
4.isAbsolute用于判断路径是否是一个绝对路径
注意点:区分操作系统
在Linux操作系统中/开头就是绝对路径
在Windows操作系统中盘符开头就是绝对路径
在Linux操作系统中路径的分隔符是左斜杠 /
在Windows操作系统中路径的分隔符是右斜杠 \
let res = path.isAbsolute('/a/b/c/d/index.html'); // true
let res = path.isAbsolute('./a/b/c/d/index.html'); // false
let res = path.isAbsolute('c:\\a\\b\\c\\d\\index.html'); // true
let res = path.isAbsolute('a\\b\\c\\d\\index.html'); // false
console.log(res);
5.path.sep用于获取当前操作系统中路径的分隔符的
如果是在Linux操作系统中运行那么获取到的是 左斜杠 /
如果是在Windows操作系统中运行那么获取到的是 右斜杠 \
console.log(path.sep);
6.path.delimiter用于获取当前操作系统环境变量的分隔符的
如果是在Linux操作系统中运行那么获取到的是 :
如果是在Windows操作系统中运行那么获取到的是 ;
console.log(path.delimiter);
7.path.parse(path): 用于将路径转换成对象
let obj = path.parse("/a/b/c/d/index.html");
console.log(obj);
/*{
root: '/',
dir: '/a/b/c/d',
base: 'index.html',
ext: '.html',
name: 'index'
}*/
8.path.format(pathObject): 用于将对象转换成路径
let obj = {
root: '/',
dir: '/a/b/c/d',
base: 'index.html',
ext: '.html',
name: 'index'
}
let newPath = path.format(obj)
console.log(obj);//"/a/b/c/d/index.html"
9.path.join([...paths]): 用于拼接路径
注意点:如果参数中没有添加/, 那么该方法会自动添加,如果参数中有..,
那么会自动根据前面的参数生成的路径, 去到上一级路径
let str = path.join("/a/b", "c"); // /a/b/c
let str = path.join("/a/b", "/c"); // /a/b/c
let str = path.join("/a/b", "/c", "../"); // /a/b/c --> /a/b
let str = path.join("/a/b", "/c", "../../"); // /a/b/c --> /a
console.log(str);
10.path.normalize(path): 用于规范化路径
let res = path.normalize("/a//b///c////d/////index.html");
console.log(res);//'/a/b/c/d/index.html'
11.path.relative(from, to): 用于计算相对路径
第一个参数: /data/orandea/test/aaa
第二个参数: /data/orandea/impl/bbb
/data/orandea/test/aaa --> ../ --> /data/orandea/test
/data/orandea/test --> ../ --> /data/orandea
let res = path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');
console.log(res);//..\..\impl\bbb
12.path.resolve([...paths]): 用于解析路径
注意点: 如果后面的参数是绝对路径, 那么前面的参数就会被忽略
let res = path.resolve('/foo/bar', './baz'); // /foo/bar/baz
let res = path.resolve('/foo/bar', '../baz'); // /foo/baz
let res = path.resolve('/foo/bar', '/baz'); // /baz
console.log(res);
(3) fs 文件模块
1.fs :文件模块:封装了各种文件相关的操作
2.查看文件的状态
fs.stat(path,options,callback);//异步获取
fs.statSync(path,options);//同步获取
let fs = require("fs");
fs.stat(__dirname,function(err,stats){
console.log(stats);
//stats对象中包含:
//birthime:文件创建的时间
//mtime:文件中内容发生变化,被修改的时间
if(stats.isFile()){
//isFile():判断是否是一个文件
console.log("当前路径对应的是一个文件");
}else if(stats.isDirectory(){
//isDirectory():判断是否是一个目录
console.log("当前路径对应的是一个目录");
}
})
let stats = fs.statSync(__dirname);
console.log(stats);
3.文件的读取 fs.readFile(path,options,callback);fs.readFileSync(path,options)
注意点:没有指定第二个参数,默认会将读取到的数据放到Buffer中
第二参数指定为utf8,返回的数据就是字符串
let fs = require("fs");
let path = require("path");
let str = path.join(__dirname,"data.txt");//拿到需要读取的文件路径
fs.readFile(str,"utf-8",function(err,data){
if(err){
throw new Error("读取文件失败");
}
console.log(data);
console.log(data.toString());
})
let data = fs.readFileSync(str,"utf-8");
console.log(data);
4.文件的写入 fs.writeFile(file,data,callback);fs.writeFileSync(file,options);
let fs = require("fs");
let path = require("path");
let str = path.join(__dirname,"luchunqiong.txt");//拼接写入的路径
fs.writeFile(str,"www.luchunqiong.com","utf-8",function(err){...});
let buf = Buffer.from("www.luchunqiong");
fs.writeFile(str,buf,"utf-8",function(err){
if(err){
throw new Error("写入数据错误");
}else{
console.log("写入数据成功");
}
})
// let res = fs.writeFileSync(str, "www.luchunqiong.com", "utf-8");
// console.log(res);
注意点://writeFile()和writeFileSync()写入数据后会覆盖以前的数据
//如果是追加:必须用appendFile()和appendFileSync();
注意点://前面的方法关于文件的写入和读取操作都是一次性将数据读入内存或者一次性写入到
//文件中,但是如果数据比较大,直接将所有数据都读到内存中会导致计算机内存爆炸
//卡顿,死机等,所以对于比较大的文件我们需要分批读取和写入
//fs.createReadStream(path,options);fs.createWriteStream(path,options)
let fs = require("fs");
let path = require("path");
//拼接读取的路径
let str = path.join(__dirname,"luchunqiong.txt");
//创建一个读取流
let readStream = fs.createReadStream(str,{
encoding:"utf-8",//文件读取的编码格式
highWaterMark:1 //每次读取的大小
})
//添加事件监听
readStream.on("open",function(){
console.log("表示数据流和文件建立了关系");
});
readStream.on("error",function(){
console.log("表示数据流和文件建立关系失败");
});
readStream.on("data",function(){
console.log("表示通过读取流从文件中读取到了数据",data);
});
readStream.on("close",function(){
console.log("表示数据流断开了和文件的关系,并且数据已经读取完毕");
})
//拼接写入的路径
let str = path.join(__dirname,"zhouhong.txt");
//创建一个写入流
let writeStream = fs.createWriteStream(str,{
encoding:"utf-8"
});
//监听写入流的事件
writeStream.on("open", function () {
console.log("表示数据流和文件建立关系成功");
});
writeStream.on("error", function () {
console.log("表示数据流和文件建立关系失败");
});
writeStream.on("close", function () {
console.log("表示数据流断开了和文件的关系");
});
let data = "www.it666.com";
let index = 0;
let timerId = setInterval(function () {
let ch = data[index];
index++;
writeStream.write(ch);
console.log("本次写入了", ch);
if(index === data.length){
clearInterval(timerId);
writeStream.end();
}
}, 1000);
//1.生成读取和写入的路径
let readPath = path.join(__dirname,"canjinkong.mp4");
let writePath = path.join(__dirname,"yanmi.mp4");
//2.创建一个读取流和写入流
let readStream = fs.createReadStream(readPath);
let writeStream = fs.createWriteStream(writePath);
//3.监听读取流事件
readStream.on("open", function () {
console.log("表示数据流和文件建立关系成功");
});
readStream.on("error", function () {
console.log("表示数据流和文件建立关系失败");
});
readStream.on("data", function (data) {
// console.log("表示通过读取流从文件中读取到了数据", data);
writeStream.write(data);
});
readStream.on("close", function () {
console.log("表示数据流断开了和文件的关系, 并且数据已经读取完毕了");
writeStream.end();
});
//监听写入流事件
writeStream.on("open", function () {
console.log("表示数据流和文件建立关系成功");
});
writeStream.on("error", function () {
console.log("表示数据流和文件建立关系失败");
});
writeStream.on("close", function () {
console.log("表示数据流断开了和文件的关系");
});
// 1.生成读取和写入的路径
let readPath = path.join(__dirname, "test.mp4");
let writePath = path.join(__dirname, "abc.mp4");
// 2.创建一个读取流
let readStream = fs.createReadStream(readPath);
// 3.创建一个写入流
let writeStream = fs.createWriteStream(writePath);
// 利用读取流的管道方法来快速的实现文件拷贝
readStream.pipe(writeStream);
目录操作
1)、创建目录
fs.mkdir(path, callback)
fs.mkdirSync(path)
2)、读取目录
fs.readdir(path,options, callback)
fs.readdirSync(path,options)
3)、删除目录
fs.rmdir(path, callback)
fs.rmdirSync(path)
练习:利用NodeJS生成项目模板
projectName
|---images
|---css
|---js
|---index.html
let fs = require("fs");
let path = require("path");
class CreatProject {
constructor(rootPath,projectName){
this.rootPath = rootPath;
this.projectName = projectName;
this.subFils = ["images","css","js","index.html"];
}
initProject(){
let projectPath = path.join(this.rootPath,this.projectName);
this.subFils.forEach(fileName =>{
if(path.extname(fileName) ===""){
let dirPath = path.join(projectPath,fileName);
fs.mkdirSync(dirPath);
}else{
let filePath = path.join(projectName,fileName);
fs.writeFileSync(filePath,"");
}
})
}
}
let projectNode = new CreateProject(__dirname, "taobao");
projectNode.initProject();
(4)HTTP模块
HTTP模块:通过Nodejs提供的http模块,我们可以快速的构建一个web服务器,接收浏览器请求、响应浏览器请求等
(1)WEB服务器的搭建
步骤: 1).导入HTTP模块
2).创建服务器实例对象
3).绑定请求事件
4).监听指定端口请求
let http = require("http");
let server = http.createSever();
server.listen(3000);
server.on("request",function(req,res){
//writeHead:告诉浏览器返回的数据是什么类型的,返回的数据需要用什么字符集来解析
res.writeHead(200,{
"Content-Type":"text/plain:charset = utf=8"
});
res.end("卢春琼");//end:结束本次请求并且返回数据
})
链式编程:
http.createServer(function(req,res){
res.writeHead(200,{
"Content-Type":"text/plain:charse=utf-8"
});
res.send("www.luchunqiong.com");
}).listen(3000);
(2)路径分发
路径分发也称为路由,就是根据不同的请求路径返回不同的数据。
通过请求监听方法中的request对象,就可以获取到当前请求的路径,通过判断请求路径的
地址就可以实现不同的请求路径返回不同的数据。
let http = require("http");
let server = http.createServer();
server.listen(3000);
server.on("request",function(req,res){
res.writeHead(200,{
"Content-Type":"text/plain; charset = utf-8"
});
if(req.url.startWith("/index")){
/*res.end("首页1");//如果通过end方法返回数据,那么只会返回一次*/
res.write("首页2");//write方法返回数据,可返回多次,但是write方法不具备结束请求功能,需要手动结束
res.end();
}else if(req.url.startWith("/login")){
res.end("登录");
}else{
res.end("没有数据");
}
});
(3)获取GET请求传递的参数 url.parse()和url.format()
let url = require("url");
let http = require("http");
let server = http.createServer();
server.on("request",function(req,res){
let obj = url.parse(req.url,true);
res.end(obj.query.xxx);//在get传参中使用query对象下xxx获取
});
server.listen(3000);
(4)获取post请求传递的参数
let http = require("http");
let queryString = require("querystring");
let server = http.createServer();
server.on("request",function(req,res){
let params = "";
req.on("data",function(chunk){
params += chunk; //在nodeJs中,post请求的参数我们不能一次性拿到,必须分批获取
});
req.on("end",function(){
let obj = queryString.parse(params);
res.end(obj.xxx);
})
})
server.listen(3000)
(5) 服务端区分GET和POST;通过HTTP模块中的IncomingMessage类中的method属性
let http = require("http");
let server = http.createServer();
server.on("request",function(req,res){
res.writeHead(200,{
"Content-Type":"text/plain;charset = utf-8"
});
if(req.method.toLowerCase() === "get"){
res.end("利用GET请求的方式处理参数");
}else if(req.method.toLowerCase() === "POST"){
res.end("利用post请求的方式处理参数")
}
})
server.listen(3000)