注意:node.js的学习至少需要有ECMAjavascript的基础
本文配合尚硅谷的nodejs基础视频食用更佳喔
文章目录
一 ,行窗口(小黑屏),CMD窗口,终端,shell
–开始菜单–>运行–>CMD–>回车
–常用的指令
dir:列出当前目录下的所有文件
cd:目录名,进入到指定的目录
md:目录名,创建一个文件夹
rd:目录名,删除一个问加你啊
–目录
- 表示当前目录
- 表示上一级目录
–环境变量(windows系统中变量)
属性–>高级系统设置–>环境变量
path:放的是一个一个路径
当我们在命令行窗口打开一个文件或调用一个程序时,系统会首先在当前目录下寻找文件程序,如果找到了则直接打开,如果没找到会依次到环境变量path的路径中寻找,直到找到位置,如果没找到则报错
–所以,我们可以将一些经常需要访问的程序和文件的路径添加到path中,这样我们就可以在任意位置访问这些文件和程序了
二,进程和线程
进程
–进程负责为程序的运行提供必备的环境
–进程相当于工厂中的车间(程序代码都存放在进程中)
线程
–线程是计算机中最小的计算单位,线程负责执行进程中的程序
==线程相当于工厂中的工人
单线程
一个线程(一个人干活)
js是单线程
多线程(多个人干活)
三,Node.js的理解
1.之前的js是在浏览器上运行的(本地)node.js是一个能够在服务器端运行(系统中运行)js的开放源代码,跨平台js运行环境。
2.node采用谷歌开发的v8引擎运行js代码,使用事件驱动,非阻塞和异步I/O模型等技术来提高性能,可优化应用程序的传输量和规模:
(客户端给服务器发送请求,服务器与数据库发生交互(IO),服务器再响应客户端)
客户端比喻成食客,服务器是服务员,数据库比喻成厨师。如何让效率更快?可以让服务员服务完这个客户后再去服务下一个客户。
Node是单线程。所有请求只有一个线程。一个人一个服务员,两个人一个服务员,一万个人都是这一个服务员。
而V8引擎能够满足高性能web,所以出现了Node.js来写这个高性能的服务器。
先进Node.js并不用于写服务器,而是用来帮助我们的开发。Node又名节点,节点可以扩充,Node可以像一个节点一样,希望可以接入新的节点来将这个“生态环境”扩张的更大
3.Node的用途
web服务API,比如REST
实现多人游戏
后端的web服务,例如跨域,服务器端的请求
基于web的应用
多客户端的通信,如即时通信
四,hello Nodejs
4.1 下载Node.js
node.js下载官网
选择左边版本即可。
按照要求下载之后,打开cmd,输入node -v和node -npm,如果有一定版本显示则安装成功。
4.2 用nodejs运行,利用终端
js文件
终端
建议使用vscode直接点击调试即可运行
五,Node.js规范
5.1 ECMAScript标准的缺陷
- (模块化:降低程序之间的耦合性,方便我们的代码复用)
- 没有模块系统
- 标准库较少
- 没有标准接口
- 缺乏管理系统
5.2(模块化)CommonJS规范
如果程序设计的规模达到了一定程度则必须对其进行模块化。CommonJS的模块功能可以解决问题
CommonJS规范为JS制定了一个美好的愿景,希望JS能够在任何地方运行
Common对模块化的定义十分简单:
-模块引用
-模块定义
-模块标识5.2.1 模块引用
一个js文件就是一个模块。
在node中,通过require()函数来引入外面的模块
requires()可以传递一个文件的路径作为参数,node将会自动根据该路径来引入外部模块。
备注:路径如果是相对路径,必须以.或…开头
注意,使用require()引入模块以后,该函数会返回一个对象,这个对象代表的是引入模块
会出现什么结果?
为什么x已经定义了,而这边显示的是undefined?
我们干脆去看一看该模块的内容
//在node中,一个js文件就是一个模块 //在node中,每一个js文件中的js代码都是独立运行在一个函数中而不是全局作用域 //所以一个模块中的变量和函数在其他模块中无法访问
如何访问?————暴露
向外部暴露属性或方法我们可以通过exports来向外部暴露变量和方法,只需要将需要暴露给外部的变量或方法设置为exports的属性即可
优点:选择性暴露5.2.2 暴露函数练习题
/* *定义一个模块math 在该模块中提供两个方法 add(a,b);//求两个数的和 mul(a,b);//求两个数的积 */
5.3 模块标识
我们使用require()引入外部模块时使用的就是模块标识。我们可以通过模块标识来找到指定的模块
模块分为
-核心模块
-由node引擎提供的模块
-核心模块的标识就是,模块的名字
-文件模块
-由用户自己创建的模块
-文件模块的表示就是文件的路径(绝对路径,相对路径)5.4 模块化详解
当node在执行模块中的代码时,它会首先在代码的最顶部,添加如下代码
function(exports,require,module,_filename,dirname)
实际上模块中的代码都是包装在一个函数中执行的,并且在函数执行时,同事传递进了5个实参
exports–该对象用来将变量或函数暴露到外部
require–函数,用来引入外部的模块
module–代表当前模块本身,而exports就是module的属性,既可以使用exports导出,也可以使用module.exports导出_filename–当前模块的完整路径,通过该路径可以找到现在的模块
_dirname–当前模块所在文件夹的完整路径5.5 exports和module.exports
exports和module.exports类似
首先直接体现在:(相同之处)module.exports.name="孙悟空"; exports.age=18; exports.sayName=function(){ console.log("我是孙悟空");
也可以直接作用于整个对象module.exports={ name:"猪八戒", age:28, sayName: function(){ console.log("我是猪八戒"); } };
var rswk=require("./swk.js"); console.log(rswk.age); console.log(rswk.name);
可如果把代码中的module删掉则无法运行。exports={ name:"猪八戒", age:28, sayName: function(){ console.log("我是猪八戒"); } };
var rswk=require("./swk.js"); console.log(rswk.age); console.log(rswk.name);
如果用module.exports实际上是在该变量的属性,而直接使用exports,则仅仅只是在该一个变量
总结:
通过exports只能使用.的方式向外暴露内部变量exports.xxx=xxx
而module.exports既可以通过.的形式,也可以直接赋值
module.exprorts.xxx=xxx module.exports={}
六,包 package
6.1 包简介
- ConmmonJS包规范允许我们将一组相关的模块组合到一起,形成一组完成整的工具
- ConmmonJS的包规范由包结构和包描述文件两个部分组成
*包结构
-用于组织包中的各种文件 - 包描述文件
-描述包的相关信息,以供外部读取分析
6.2 包结构
包实际上就是一个压缩文件,相当于一个文件夹,解压以后还原为目录,符合规范的目录应该包含如下文件
–package.json 描述文件,说明书
–bin 可执行二进制文件
–lib js代码
–doc 文档
–test 单元测试6.3 包描述文件
- 包描述文件用于表达非代码相关的信息,它是一个JSON格式的文件–package.json,位于包的根目录下,是包的重要组成部分。
- package.json中的字段
name,bugs,licenses,dependencies,cpu,os,bin,main等;
我们在写代码的时候可以先建一个json文件来说明包
这里要注意,json文件中不能出现注释喔
七,NPM简介(Node Package Manager)
7.1 NPM介绍
相当于一个软件管家,我们要找什么包,搜索出来之后直接下载即可
- 若说CommonJS包规范是理论,NPM是其中一种实践
- 对于Node而言,NPM帮助其完成了第三方模块的发布,安装和依赖等。借助NPM,Node与第三方模块之间形成了很好的一个生态系统。
什么叫做依赖功能??
包与包之间存在依赖关系,比如B依赖于A,C依赖于B。加入我们现在想要用C,而C又依赖于B,B又依赖于A,所以我们连着要下ABC,而在NPM中如果你下载了C,自动会帮助你把C所依赖的包给下载下来。这样能把开发变得更加简单
7.2 NPM命令
- npm -v 查看版本
- npm 帮助说明
- npm search 包名 (搜索模块包包)
- npm install/i 包名(在当前目录安装包)
- npm install/i 包名(全局目录安装包)
- npm remove/r 包名(删除包)
- npm install 包名 --save(安装包并添加到依赖中)
- npm install 下载当前项目所依赖的包
- npm install 包名 -g(全局安装的包一般都是一些工具)
7.3 用NPM在文件夹中安装包
比如这张图,我搜索math包后得到的结果
第一列包名,第二列说明,第三列作者,第四列更新的时间,第五列版本
那我们如何安装呢??
这一列,我们对它进行安装
如果我在这里装,则就是装在了当前目录。如果我不想在这里装,我可以在需要的地方建立一个文件夹
直接在文件夹里cmd
首先我们需要建立一个package.json让电脑确认我们是要将math装入这个包中,也可以直接利用cmd初始化,工具将代理你创建一个package.json
工具提示你它将给json文件取名
如果想用这个名字直接回车
不想要的话把要改的名字写在后面(以此类推,不想改也可以直接按回车)输入指令
下载完毕
所有下载的东西都会在modules中如果相差安相关信息查看math中的package即可
那我们该怎么用创建出来的这个math呢?
创建出一个js文件,引用math包
7.4 用NPM在编译器中安装包
如何在我们的编译器中做这件事呢
7.5 npm install 包名 --save
使用之后会出现依赖关系
–save有一个功能:把你安装的包设置成了依赖。我们上传到网上时没有必要把你依赖的包全部上传,只这一个就行八,配置cnpm
8.1 介绍与安装
npm是美国写出来的东西,使用的也是美国的服务器。所以我们利用npm去下载一些东西速度可能不够快,性能不够稳定。
所以我们最好能去创建一个镜像服务器去下载。
在百度上搜索npm会显示“淘宝镜像”
淘宝镜像
如果使用淘宝的服务器(中国)不够灵活
所以淘宝为我们提供cnpm,如果想使用美国的服务器就直接npm
安装cnpm:
好处:想用哪个用哪个
(安装速度可能有点慢)8.2 用法
cnpm和npm用法一样,指令一样。
比如,我们利用cnpm来下载math
因为是国内的服务器,速度应该更快
不会与官方版本覆盖。九,node搜索包的流程
node在使用模块名字来引入模块时,它会首先在当前目录的node_moduies中寻找是否含有模块。
如果有则直接使用
如果没有则去上一级目录中的node_moduies中寻找
如果有则直接使用,如果没有则再去上一级目录寻找,直到找到为止
直到找到磁盘的根目录,如果依然没有,则报错十,Buffer(缓冲区)
- 从结构上看Buffer非常像一个数组,它的元素为16进制的两位数
- 实际上一个元素就表示内存中的一个字节
- 实际上Buffer中的内存不是通过javaScript分配的,而是在底层通过C++申请的。
- 也就是我们可以直接通过Buffer来创建内存中的空间
10.1 Buffer介绍
Buffer(缓冲区)
-Buffer的结构和数组很像,操作方法也和数组类似
为什么出现Buffer
1.js中原生数组性能相对较差
2.数组中不能存储二进制文件(图片等)
3.使用buffer不需要引入模块,直接使用即可
所以,Buffer实际上就是一个存储二进制文件的东西)var str="Hello Atguigu"; //将一个字符串保存到buffer中 var buf=Buffer.from(str); console.log(str); console.log(buf.length); console.log(buf);
4.在Buffer中储存为二进制数据,但是显示时都是以十六进制形式显示出来,这样更短
5.Buffer中每一个元素的范围是从00-ff,0-255
——00000000-11111111 计算机一个0或一个1我们称为1位(bit)
——8bit=byte(字节)1024byte=1kb 1-024mb=1gb 1024gb=1td
——buffer中的一个元素,占用内存的一个字节
一个汉字占3个字节,一个字母占1个字节
上图中,一个是长度,一个是放进Buffer后所占的字节数10.2 创造指定大小的Buffer
方法1:
var buf2=new Buffer(10); console.log(buf2.length);
方法2:var buf2=Buffer.alloc(10);//(alloc意为分配)内存中分配10个空间来装buf2
10.3 向Buffer中添加元素
//通过索引操作Buffer元素 buf2[0]=88; buf2[1]=255; buf2[2]=0xaa; ...... console.log(buf2);
为什么没有第十个元素?
因为buffer的长度一旦确定便永远不能更改,buffer实际上是对底层内存的直接操控buffer中这样方便内存更好的维护
为什么第四个元素处是0?因为buffer的范围是0-255,而我们写256则数据溢出所以我们要注意范围10.4 读取buffer中的元素
类似数组
为什么第三个数读出来是170?
只要数字在控制台或页面中输出一定是10进制
如果不想看十进制可以使用toString()把它转化为字符串,并在括号里加入想要转化的格式,十六进制就写16,二进制就写2//通过索引操作Buffer元素 buf2[0]=88; buf2[1]=255; buf2[2]=0xaa; buf2[10]=15; buf2[3]=256; console.log(buf2); console.log(buf2[2]); console.log(buf2[2].toString(16));//转化为16进制字符串 console.log(buf2[2].toString(2));
同样我们也可以对其进行遍历
(注:70代表7个0)10.5 allocUnsafe(size)
创建一个指定大小的buffer,但是buffer中可能含有敏感数据
什么意思?
为什么我们并没有写内容而显示出来的有内容呢?
因为我们一般创建的buffer都是一段单独创造出来的内存空间(删除了之前的内容新创造出来的内存空间),而通过这种方法创造出来的空间是已经用过并且没有删除的。
是用户使用过的,所以是敏感数据。可是为什么要出现这种办法?
因为这种方法不用删除,性能更好。所以我们一般还是使用alloc,避免内容泄露10.5 本节总结
Buffer.from(str);//将一个字符串转化为buffer Buffer.alloc(size);//创建一个指定大小的Buffer Buffer.allocUnsafe(size);//创建一个指定大小的Buffer,可能会包含敏感数据
Buffer为什么叫缓冲区?
客户端发送请求给服务器。发过来的如果是二进制数据,则需要Buffer来存放
直接用toString()把缓冲区数据转化为字符串buf.toString();
==其他方法我们可以查看Node.js中文网中的文档中查看
十一,同步文件写入
11.1 文件系统
文件系统(File System)
简单来说就是通过node.js来对文件进行增删改查等操作- 在Node中,与文件系统的交互是非常重要的,服务区的本质就将本地的文件发送给远程的客户端
- Node通过fs模块来和文件系统进行交互
- 该模块提供了一些标准文件访问API来打开,读取,写入文件,以及与其交互
- 要使用fs模块,首先需要对其进行加载 == -conist fs=require(“fs”)==
使用文件系统需要引入fs模块(核心模块,无需下载)
11.2 同步和异步调用
这里看文档中给出的两个方法
(异步)有callBack
一个有sync,一个没有,带sync是同步的,不带的是异步方法- fs模块中所有的操作都有两种形式可供选择,同步和异步
- 同步文件系统会阻塞程序的执行,也就是除非操作完毕,否则不会向下执行代码。
- 异步文件系统不会阻塞程序的进行,而是在操作完成时,通过回调函数将结果返回
11.3 文件的写入
手动操作步骤:打开文件->向文件中写入内容->保存并关闭文件
11.3.1 同步文件的写入
同步文件相对于异步文件来说更好理解
fs.openSync("hello.txt");
传入三个参数- path:打开文件的路径
- flags:打开文件要做的操作类型(r:只读,w:可写)
- mode:设置文件的操作权限,一般不写
var fs=require("fs"); //同步文件写入 fs.openSync("hello.txt","w");
在内存中打开,有返回值。var fd=fs.openSync("hello.text","w"); console.log(fd);
文件编号是3
文件已打开
11.3.2 向文件中写入内容
使用writeSync方法
参数:对象名,字符串(要写进去的内容)var fs=require("fs"); var fd=fs.openSync("hello.text","w"); console.log(fd); //写入内容 fs.writeSync(fd,"我真可爱") //字符串,要写入的内容
fs.writeSync(fd,"我真可爱",2)
在索引为2的地方写。由于前面没有给内容所以前两个字符显示不出来
11.3.3 关闭文件
在服务器中必须关闭文件
fs.closeSync(fd);
11.4 异步文件的写入
11.4.1 打开文件
//引入fs模块 var fs=require("fs");
//打开文件 fs.open("hello2.txt","w",function(){ console.log(arguments); }); //open用来打开一个文件,异步调用方法,结果都是通过回调函数的参数返回的 //所以不需要定义一个返回值fd
所以要有一个回调函数(callback),也就是上述代码中的function()
在用到回调函数时,我们先要确认回调函数中有哪些参数:
err:错误对象,如果没有错误则为null(错误优先,只要有可能出现错误异常回调函数第一个参数绝对是err)所以我们可以定义一个err和fd
判断是否出错
如果err存在,出错,反之不出错。
通过这种方式对错误进行处理
把w改成r(只读,但是发现文件不存在)
改成w则不出错11.4.2 写入文件
首先我们来看一下阻塞问题:
fs.open("hello2.text","w",function(err,rd){ console.log("回调函数中的代码"); if(!err){ console.log(fd); }else{ console.log(err); } }); console.log("open下的代码");
代码执行,先执行下面的的,再执行callback回调函数内的,说明异步文件不会有阻塞程序的进行
回调函数的代码后执行//引入fs模块 var fs=require("fs"); //打开文件 fs.open("hello2.text","w",function(err,fd){ console.log("回调函数中的代码"); if(!err){ //如果没有出错则对文件进行写入操作 fs.write(fd,"这是异步写入的内容",function(err){ if(!err){ console.log("写入成功"); } }); }else{ console.log(err); } }); console.log("open下的代码"); //open用来打开一个文件,异步调用方法,结果都是通过回调函数的参数返回的 //回调函数两个参数:err错误对象,如过没有错误则为null(错误优先),fd:文件的描述符
11.4.3 关闭文件
fs.close(fd,function(err)){ if(!err){ console.log("文件已关闭"); } }
总结
1.同步符合人类思维习惯,而异步并不符合,可异步性能更好。
2.同步同样也要处理异常。可同步异常处理非常麻烦十二,简单文件写入
- fs.writeFile(file,data[,options],callback)
- fs.writeFileSync(file,data[,options])
-file 要操作文件的路径(相对路径绝对路径都可,绝对路径\换成/)
-data 要写入的数据
-options 选项,可以对写入进行一些设置
-callback 当写入完成后执行的函数
12.1 fs.writeFile
fs.writeFile(file,data[,options],callback)
一步到位//引入fs模块 var fs=require("fs"); fs.writeFile("hello3.txt","这是通过writeFile写入的内容",function(err){ if(!err){ console.log("写入成功"); } })
为什么这么简单?它把代码封装啦(了解即可)
注意,如果我在我的helllo.txt写上哈哈哈哈哈哈,那么我再执行这个代码会怎么样呢?答案是覆盖,那如果我不想从头开始写怎么办??fs.writeFile("hello3.txt","这是通过writeFile写入的内容",{flag:"a"},function(err)
12.1.1 操作符
-flag
- r–只读
- w–可写
- a–追加
十三,流式文件写入
13.1 流式写入
同步,异步,简单文件的写入都不适合大文件的写入,性能较差,容易导致内存溢出。
A,B是两个大水缸(内存),要把A中的水倒入B中去(文件),则两遍都需要有足够大的空间(内存),而流式文件写入相当于在鱼缸中间插入一根管子
速度较快,持续写入13.2 写入方法
var fs=require("fs"); //先创建可写流(createwrite,createread) /*fs.createWriteStream(Path[,options]) -可以用来创建一个科协刘 -path,文件路径 -option 配置的参数 */ var ws=fs.createWriteStream("hello3.txt"); //通过ws想文件中输出内容 ws.write("通过可写流写入文件的内容");
持续多次写入文件,适合大型文件
打开与关闭(监听)(了解即可)//通过监听流的open和close事件来监听流的打开和关闭 ws.on("open",funtion(){ console.log("流打开了"); });//on绑定长期事件,once一次性(该事件只能触发一次不能触发多次) ws.once("close",function(){ console.log("流关闭了"); }) //关闭流要用end()结束流入 ws.end();
十四,文件的读取
14.1 读取文件
读取hello.text
//fs.readFile(path,options,callback) var fs=require("fs"); fs.readFile("hello.text",function(err,data){ if(!err){ console.log(data); console.log(data.toString()); } })
为什么读出来的不能是直接的字符串而返回的是Buffer?
因为我们读到的有可能是音频或者是图片,则转化为字符串的可读性和通用性不强。14.2 把读到的文件写出去(通过此文件完成复制)
var fs=require("fs"); fs.readFile("hello.text",function(err,data){ if(!err){ console.log(data); console.log(data.toString()); fs.writeFile("nodehello.txt",data,function(err){ if(!err){ console("ok"); } }) } })
十五,流式文件的读取
适用于比较大的文件
var fs=require("fs"); var rs=fs.createReadStream("hello.txt"); //创建科协刘 war ws=fs.createWriteStream("hello.txt"); //监听流的开启和关闭 rs.once("open",function(){ console.log("可读流打开了"); }); rs.once("close",function(){ console.log("可读流关闭了"); }); //如果要读取一个可读流的数据,必须要为可读流绑定一个data事件,data事件绑定完毕,它会自动开始读取数据 rs.on("data",function(data){ console.log(data); });
十六,fs模块的其他方法
var fs=require("fs"); //1.验证文件路径是否存在 var isExist=fs.existsSync("a123.mp3"); //2.获取文件的状态 fs.stat("hello.text",function(err,stat){ console.log("stat"); /* size 文件的大小 isFile() 是否是一个文件 isDirectory() 是否是一个文件夹 */ console.log(stat.isDirectory()); }); //删除文件 fs.unlink(path,callback); fs.unlinkSync(path); //读取一个目录的结构 fs.readdir(path,options,callback); fs.readdirSync(path,options); //截断文件,路径,将我们的文件修改为指定大小 fs.truncate(path,len,allback); fs.truncateSync(path,len); //创建一个目录 fs.mkdirSync("hello"); //删除一个目录 fs.rmdirSync(""); //重命名 fs.rename(oldpath,newpath,callback); fs.renameSync(oldpath,newpath); //监视文件 fs.watchFile(filename,options,listener);//listener是回调函数当文件发生变化回调函数执行