node.js学习
node.js 是什么
node.js是一款基于Chrome V8内核的javaScript运行环境
下载与安装
官网下载,傻瓜式安装
使用
命令行使用
fs文件系统模块 const fs = require(‘fs’);
定义
fs模块是Node.js官方提供的,用来操作文件的模块,它提供了一系列的方法和属性,用来满足用户对文件的操作需求
例如
- fs.readFile()方法,用来读取指定文件中的内容
- fs.writeFile()方法,用来向指定的文件中写入内容
注意:使用该模块需要先导入
读取指定文件中的内容 fs.readFile() 方法
格式:fs.readFile(path[,CharSet],callback);
向指定文件中写入内容
格式:fs.writeFile(file,data[,option],callback)
- file ==>文件路径字符串
- data ==> 表示要写入的内容
- option ==> 写入格式 默认utf8
- callback ==> 回调函数
处理路径问题
路径拼接错误
因为终端的执行路径,导致动态拼接的路劲为错误路径
解放方案:
- 1.提供绝对路径
- 2.__dirname 表示当前目录
path路径模块 const path = require(‘path’);
常用方法
- path.join() 将多个路径片段拼接成一个完整的路径字符串
- path.basename() 在路径字符串中将文件名解析出来 可以传递第二个参数,用于删除末尾的字符串
path.basename('\\a\\b\\c\\index.html','html') //<==这个结果是index
–path.extname 获取文件扩展名
http模块
http模块是node.js官方提供的用来创建服务器的模块.通过http模块提供的http.createServer()方法,就能很方便的把一台普通的电脑,变成一台Web服务器,从而对外提供Web 资源服务
- 需要导入http模块
- const http = require('http);
服务器以及相关概念
IP地址
每台计算机的唯一表示
采用点分十进制表示(a,b,c,d)
域名(Domain Name)和域名服务器
端口号
每个web服务都对应唯一的一个端口号,通过这玩意准确识别
创建一个服务器的步骤
//导入模块
const http = require('http');
//创建服务
const server = http.crearServer();
//为服务器绑定请求事件
server.on('request',(req,res)=>{});
//启动服务器
server.listen(80,()=>{
console.log('port 80 running...')
})
req 请求对象
- url:地址
- method:请求类型
res 响应对象
- end(data):向服务器发送数据并结束本次请求
模块化
遵守对应的规则,把一个大文件拆分成独立并互相依赖的小模块
分类
- 自定义模块
- 官方模块
- 第三方模块
加载
require方法
加载模块时,会执行模块中的代码
作用域
在模块内定义的成员无法被模块外访问
module.exports 对象
在自定义模块中可以使用module.exports对象,将模块中的成员共享出去,供外界使用,外界用require方法导入自定义模块时,得到的就是module.exports所指向的对象
共享成员时的注意点
s使用require()当打导入模块时,导入的结果永远以module,exports指向的对象为准
exports对象
由于module.exports单词写起来比较复杂,为了简化共享成员的代码,Node提供了exports对象。默认情况下,exports和module.exports都指向同一个对象,最终的结果,以module.export指向的对象为准
node.js中的模块规范
Node.js遵循了CommonJS模块化规范,CommonJs中规定了模块的特性,和各模块之间如何相互依赖
CommonJs规定
- 每个模块内部,module变量代表当前模块
- module变量是一个对象,它的export属性(即module.exports)是对外的接口
- 加载某个模块其实就是加载这个模块的module.export属性,require()方法用于加载模块
NPM与包
what is 包
就是第三方模块,叫法不同而已
包的来源
由第三方开发,免费下载使用
why need 包
由于Node.js的内置模块仅仅提供了一些底层的API,导致在基于内置模块进行项目开发的时候,效率很低,包是基于内置模块封装出来的,提供了更高级,更方便的API,极大的提高了开发效率,包和浏览器之间的关系,就类似于Jquery和浏览器内置Api之间的关系
从哪里下载包
- https://npmjs.com 全球最大包共享网站
- https://registry.npmjs.org 专门下载包的服务器
如何下载
npm(node package manage)包管理工具
npm install 完整的包名称
npm i 完整的包名称
注意点
-
初次装包完成后,在项目文件夹下多一个叫做node_modules的文件夹和package-lock.json的配置文件,其中nodel_modules文件夹用来存放所有已安装到项目中的包。require()导入第三方包时,就是从这个目录中查找并加载包
package-lock.json配置文件用来记录node_modules目录下的每一个包的下载信息,例如包的版本号,下载地址等 -
安装指定版本的包
默认情况下,会自动安装最新版本的包,如果需要安装指定版本的包,可以在包名后面加上@符号指定需要的版本
例:
npm i moment@2.22.2
- 包的语义化版本规范
包的版本号是以“点分十进制”定义的,总共有三位数字
- 第一位:大版本
- 第二位:功能版本
- 第三位: bug修复版本
包管理配置文件
- 多人协作的问题:第三方的包体积过大,不方便成员之间共享项目源代码
解决方案:共享时剔除node_modules - 在项目根目录中,创建一个叫做package.json的配置文件,即可用来记录在项目中安装了哪些包,从而方便剔除node_modules目录之后,在团队成员之间共享项目的源代码
- npm包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中快速创建package.json这个包管理配置文件
npm init -y
上述命令只能在英文的目录下运行成功,所以项目文件夹的名称一定要使用英文
dependencies节点
dependencies节点专门用来记录安装了哪些包
一次性安装package.json中dependencies记录的所有包
npm i
卸载包
- uninstall 包名
devDependencies 节点
如果某些包只在项目开发阶段会用到,在项目上线以后就不会用到,则可以放到这个包中
//安装指定的包,并记录到devDependencies节点中
npm i 包名 -D
//注意:上述命令只是简写形式,等价与下面完整的写法
nom install 包名 --save-dev
解决包下载速度过慢的问题
//查询当前的下载路径
npm config get registry
//修改当前的下载路径
npm config set registry = 下载路径 '淘宝:https://registry.npm.taobao.org'
nrm管理镜像源
//安装 -g表示全局可用
npm i nrm -g
//查看所有可用的镜像源
npm ls
包的分类
项目包
- 开发依赖包(被记录到devDependencies节点的包,只在开发时候使用)
npm install packageName --save-dev
- 核心依赖包(被记录到dependencies节点中的包,开发上线都用)
npm i packageName
全局包
规范的包结构
- 包必须以单独的目录存在
- 包的顶级目录要包含package.json这个包管理配置文件
- package.json中必须包含name,version,main这三个属性,分别表示包的名字,版本号和入口
/* 在导入一个包时,会首先执行main指向的对象*/
开发属于自己的包
初始化包结构
- 新建包文件夹,作为包的根目录
- 在包根目录中,创建如下三个文件。
- package.json 包管理配置文件
- index.js 包的入口文件
- README.md 包的说明文档
package.js的五个必要属性
{
"name":"zengzeng", //包的名称
"version":"1.0.0", //包的版本号
"main":"index.js", //包的入口
"description":"这是一个测试用包", //包的描述信息
"keywords":["zeng","pro"], //搜索关键字
"license":"ISC" //开源许可协议
}
- 展开预算符 … 将对象中的属性全部展开
包的发布
//进入包的根目录
npm publish //发布包
npm unpublish //只能删除72小时以内发布的包
//并且24小时以内不允许发布相同名字的包
模块的加载机制
模块在第一次加载后会被缓存,也就是说,即使多次require模块,也不会导致模块中的代码反复执行
- 内置模块加载优先级最高
自定义模块的加载机制
使用require加载自定义模块时,必须指定./或者…/开头,否则会被当做内置模块或者第三方模块进行加载
引入自定义模块若忽略扩展名会自动补全以下扩展
- 确切文件名
- .js
- .json
- .node
- 找不到,报错
第三方模块的加载机制
在加载一个第三方模块时。node会先找寻父级目录下的node_modules中查找,如果没有则会在父级的父级目录下的node_modules中查找,以此类推,一直找到盘符的根,如果还没有则报错
目录作为模块的加载机制
当把模块作为模块标识符,传递给require()进行加载的时候有三种加载方式
- 在被加载的目录下面查找一个叫做package.json的文件,并寻找main属性作为入口
- 找寻目录下的index.js文件
- 上二者皆不成功则报错
express的基本使用
参数的获取
req.query 这个属性包含get请求中的所有参数
req.param 这个属性可以动态获取链接中的值
//注意 路由格式('/acb/:d') :后表示动态参数
静态资源托管
express.static(‘要托管的文件夹’)
一般写法
app.use(express.static(‘要托管的文件夹’))
挂载路径前缀
app.use(‘前缀’,express.static(‘要托管的文件夹’))
app.use()这个方法常用于注册全局中间件
nodemon插件
nodemon 命令启动项目实现热重载
路由
每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的而处理函数,在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的URL同事匹配成功,则Express会将注册请求交给对应的function进行处理
模块化路由
为了方便对路由进行模块化的管理,express不建议将路由直接挂载到app上,应当抽离出来作为单独的文件存在
- 创建路由模块对应的.js文件
- 调用express.Router()函数创建路由对象
- 向路由对象上挂载具体的路由
- 使用module.exports向外共享路由对象
- 使用app.use()注册路由
中间件
中间件的格式
express的中间件本质上就是一个function处理函数
注意:中间件的形参列表中,必须包含next参数。而路由的处理函数只包含req和res参数。
next函数的作用
next函数是实现多个中间件连续调用的关键,它表示把流转关系交给下一个中间件或路由
中间件的作用
多个中间件之间,他们的req和res参数都是相同的,所以上游可以在这两个对象上挂载参数供下游的中间件使用
app.get("/",(req,res,next)=>{
req.a = "hello"
})
app.get("/",(req,res,next)=>{
console.log(req.a) //此时就会得到hello
})
中间件有req,res,next三个参数组成
中间件的注意事项
- 一定要在路由之前注册中间件
- 客户端发送过来的请求,**可以连续调用多个中间件进行处理
- 执行完毕中间件的逻辑之后要记得调用next()函数
- 在执行next()不要进行其他的操作
- req和res在多个中间件之间是共享的
中间件的分类
- 应用级别
通过app.use()或app.get()或app.post()绑定的中间件 - 路由级别
绑定到Route上的中间件 - 错误级别
捕获并处理全局的异常,这个级别的中间件有四个形参(err,req,res,next) - Express内置
- express.static 用于快速托管静态资源的内置中间件。
- express.json 用于解析JSON格式的请求体数据
- express.urlencoded用于解析URL-encoded格式的请求体数据 需要加上配置参数对象
//通过use对中间件进行配置 app.use(express.json()) app.use(express.urlencoded({extended:false}))
- 第三方
由第三方开发的中间件,例如body-parser
自制解析post请求的中间件
//制作一个全局中间件放到最前面
app.use((req,res,next)=>{
//定义数据字符串
let str = '';
//通过监听req的data时间来接收数据
req.on('data',e=>str+=e);
//通过监听req的end时间来确认数据接收完毕
req.on('end',()=>{
//调用内置模块的qureystring.parse方法将字符串转化为实体对象并赋值给req.body
req.body = querystring.parse(str);
//将权限转交出去
next()
})
})
跨域问题
CORS(Cross-Origin Resource Sharing,跨域资源共享)由一系列的http响应头组成,这些http响应头决定浏览器是否阻止前端js代码跨域获取资源。
浏览器的同源安全策略默认会阻止网页“跨域”获取资源 。但如果接口服务器配置了CORS相关的http响应头就可以接触浏览器短的跨域访问限制
注意
- cors主要在服务器端进行配置。客户端浏览器无需做任何额外的配置,即可请求开启了cors的接口
- cors在浏览器中有兼容性。只有支持XMLhttpRequst Level2的浏览器才可正常使用。
身份认证
- 服务器端渲染推荐使用Session认证机制
- 前后端分离推荐使用JWT认证机制
jwt跨域身份认证
定义
JWI (JSON Web Token) 目前最流行的跨域身份认证解决方案
原理
用户访问服务器后,服务器根据相关信息生成一个Token,发送给客户端,客户端存储到本地,在客户端再次访问服务器时,发送token,服务器再解密出token中的信息
JWT的组成部分
jwt通常由三部分组成,分别是Header(头部),Payload(有效荷载),Signature(签名)。中间以.分隔
- Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串
- Header 和Signature是安全性相关的部分,只是为了保证Token的安全性。
使用方式
客户端收到服务器返回的JWT之后通常会将它存储在localStorage或者sessionStorage中。
此后,客户端每次与服务器通信都要带上这个JWT的字符串,从而进行身份认证。推荐的做法是把JWT放在HTTP请求头的Authorization字段中,格式如下:
Authorization:Bearer <token>
两个相关的包
- jsonwebtoken 用于生成jwt字符串
- epress-jwt 用户将jwt字符串解析还原成JSON对象
在express中使用jwt
为了保证JWT字符串的安全性,防止JWT在网路传输过程中被别人破解,我们需要专门定义一个用于加密和解密的secret密钥
- 在生成JWT字符串时需要根据secret加密
- 在解析JWT字符串时需要根据secret解密
sign方法
返回一个加密的字符串
三个参数:
- 用户的信息对象
- 加密的密钥
- 配置对象,配置当前有效期 expiresIn