irpas技术客

node.js学习记录总结_MrScarlett

网络投稿 5676

Node.js 模块化 什么是模块化?

模块化是指解决一个复杂问题时,自顶向下把系统划分成若干模块的过程。对于整个系统来说,模块是可组合、分解和更换的单元。

模块化的好处 提高代码的复用性提高代码的可维护性可以实现按需加载 node.js中模块的分类 内置模块(由node.js官方提供的模块,如fs、path、http等)自定义模块(用户创建的每个.js文件)第三方模块(由第三方开发出来的模块,使用需要下载) 加载模块

使用require()方法来加载所需模块

注意:

使用require()方法加载其他模块时,会执行被加载模块中的代码可以省略.js后缀名 模块作用域 和函数作用域相似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问好处: 防止全局变量污染问题 向外共享模块作用域中的成员 module 对象module.exports 对象 在自定义模块中,可以使用module.exports对象将模块内的成员共享出去外界用require方法导入自定义模块时,得到的就是module.exports所指向的对象 共享成员时的注意点:使用require()导入模块时,得到的结果永远以module.exports指向的对象为准exports和module.exports指向同一个对象 模块的加载机制 优先从缓存中加载

模块第一次加载后会被缓存,多次调用require()不会导致模块的代码被执行多次,从而提高模块的加载效率

内置模块的加载优先级最高

若遇到同名的模块,内置模块优先级较高

自定义模块加载机制(省略扩展名情况) 按照确切文件名进行加载补全.js扩展名进行加载补全.json扩展名进行加载补全.node扩展名进行加载加载失败,终端报错 第三方模块加载机制

从当前模块的父目录开始,若找不到,则移动到上一层父目录,进行加载,直到文件系统的根目录

以目录为模块传递给require 先寻找目录下package.json文件,查找main属性,作为require()加载的入口若没有package.json文件或main属性入口不存在或无法解析,则node.js会加载目录下的index.js若都失败了,则在终端打印错误信息,报告模块的确实:Error:Con not find module ‘xxx’ fs文件系统模块 利用require导入fs模块 const fs = require('fs');fs.readFile()读取文件 参数1:读取文件的存放路径(必选)参数2:读取文件的编码格式,一般默认为utf8(可选)参数3:回调函数,失败和成功的结果(function(){失败,成功})(必选) fs.writeFile()写入内容 参数1:读取文件的存放路径(必选)参数2:要写入的内容参数3:读取文件的编码格式,一般默认为utf8(可选)参数4:回调函数,失败和成功的结果(function(){失败,成功})(必选) 使用terminal出现路径拼接错误的问题,是因为使用了./ 或 …/开头的相对路径,使用绝对路径可以解决此问题__dirname表示当前文件所处的目录 注:fs.writeFile()写入同一文件时会覆盖 path路径模块 path.join()将多个路径片段拼接 各参数用’,'隔开 path.basename()从路径字符串解析出文件名 参数1:路径(必须)参数2:后缀名(可选),选择后会移除 path.extname()获取扩展名 http模块 IP地址:互联网上每台计算机的一个唯一标识 IP地址格式通常为“点分十进制”表示成(a,b,c,d)的形式,都是为0-255之间的十进制整数互联网中每台web服务器都有属于自己的IP地址 域名和域名服务器 IP地址和域名是一一对应的关系 端口号: 每个端口号不能同时被多个web服务占用在实际应用中,url的80端口可以省略 创建基本的web服务器步骤: 导入http模块 const http = require('http'); 创建web服务器实例 const server = http.createServer(); 为server绑定request事件,监听服务器请求 server.on('request', function(req, res){}) 启动服务器 server.listen(端口号, function(){}) 解决中文乱码的问题: 设置内容的编码格式 res.setHeader('Content-Type', 'text/html; charset=utf-8'); 向服务端发送内容 res.end('发送的内容') npm与包 创建自己的npm包 新建文件夹设置package.json文件,index.js文件和README.md文档 index.js放主要信息package.json放配置信息 例子: { "name": "", // 所起的名字 "version": "1.0.1", // 版本号 "main": "index.js", // 主要文件 即前面设置的index.js "description": "", // npm包的描述 "keywords": [], // 关键词 "license": "ISC" // 开源协议 } 可以将所创建的js文件放在新建的src文件夹下,每个js文件分别要module.exports在index.js中module.exports={}所有src里的js文件使用时通过require根文件夹引入 发布包 在cmd中cd至发布文件的根文件夹运行命令:npm publish 删除已发布的包 cmd中运行:npm unpublish 包名 --force注意: npm unpublish 命令只能删除72小时内发布的包删除的包在24小时内不允许重复发布 Express 了解Express 本质为npm上的第三方包,提供了快速创建Web服务器的便捷方法可以方便快速创建Web网站服务器或API接口的服务器 使用Express创建服务器 基本使用 const express = require('express'); const app = express(); app.listen(80, () => { console.log('server running at http://127.0.0.1'); }) 监听get请求 app.get('请求url',function(req,res){ res.send(); //向客户端响应的内容 }) req.query

req.query默认是一个空对象,客户端使用?name=a&age=30这种查询字符串的形式发送到服务器的参数,可以用req.query获取 例:在地址栏输入127.0.0.1/?username=test&age=20

app.get('/',function(req,res)=>{ res.send(req.query); //{"username":"test","age":"20"} }) req.params

URL地址中,可以通过:参数名的形式匹配动态参数值,req.params默认是一个空对象,里面存放着通过:动态匹配到的参数值(参数名不固定) 例:在地址栏输入127.0.0.1/user/1

app.get('/user/:id', (req, res) => { res.send(req.params); //{"id":"1"} }) 监听post请求 app.post('请求url',function(req,res){ res.send();//向客户端响应的内容 }) 托管静态资源

app.use(express.static()) 注意:Express在指定静态目录中查找文件,并对外提供资源的访问路径,因此,存放静态文件的目录名不会在url中 若要托管多个目录,则多次调用express.static()函数

app.use('/public', express.static('./public'))

挂载路径前缀

Express路由 路由指的是客户端的请求与服务器处理函数之间的映射关系Express中的路由分3部分,分别是请求的类型、请求的url地址、处理函数 app.METHOD(PATH, HANDLER) 最简单的挂载方法 app.get('url', function(req, res)=>{}) app.post('url', function(req, res)=>{}) 模块化路由 创建路由模块对应的js文件调用express.Router()创建路由对象向路由对象上挂载具体的路由使用module.exports向外共享路由对象在路由文件下使用require()引入路由模块使用app.use()函数注册路由模块 在use中可以给路由模块添加统一的访问前缀 Express中间件 什么是中间件

中间件特指业务流程中的中间处理环节

中间件的格式

本质上是一个function处理函数格式如下:(有包含next则为中间件处理函数,没有则为路由处理函数)

const express = require('express'); const app = express(); app.get('/', function(req, res, next){ next(); }); app.listen(80); 全局中间件 使用app.use()定义的中间件函数,为全局中间件,next()函数可以共享给后面的路由若定义了多个全局中间件就依次写下去 局部生效中间件 使用app.use()定义的中间件,为局部生效中间件使用方式: const mw = (req, res, next)=>{ next(); } app.get('/', mw, (req, res)=>{ res.send() }) 定义多个局部中间件 //以下两种写法完全等价 app.get('/', mw1, mw2, (req, res)=>{res.send()}); app.get('/', [mw1, mw2], (req, res)=>{res.send()}); 监听req的data事件 //监听req的data事件,获取客户端发送到服务器的数据,若数据量比较大,客户端会把数据切割后分批发送到服务器,所以data事件有可能会触发多次,需要对接收到的数据进行拼接 let str = ''; req.on('data', (data)=>{ str += data; }); 监听req的end事件 //监听req的end事件(请求体发送完毕后自动触发),可用于获取完整请求体数据 req.on('end',()=>{ console.log(str); }); 中间件的分类 应用级别的中间件路由级别的中间件错误级别的中间件Express内置的中间件第三方的中间件 应用级别的中间件

通过app.use()或app.get()或app.post绑定到app实例上的中间件

路由级别的中间件

绑定到express.Router()实例上的中间件,用法和应用级别的没有区别

错误级别的中间件 作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题格式:错误级别的中间件有4个形参,(err, req, res, next)注意:错误级别的中间件必须注册在所有路由之后例: const express = require('express'); const app = express(); app.get('/', (req, res) => { throw new Error('服务器内部发生了错误!'); res.send('Home page'); }) app.use((err, req, res, next) => { console.log('wrong!'); res.send('Error' + err.message); }) app.listen(80, () => { console.log('http://127.0.0.1'); }) Express内置的中间件 express.static 快速托管静态资源的内置中间件,如:html文件、图片、css样式等((无兼容性)express.json解析JSON格式的请求体数据(有兼容性,在4.16.0+版本可用) app.use(express.json()) //在服务器可以使用req.body来接收客户端发送的请求体数据,若没有配置解析的中间件,req.body默认为{} app.post('/user', (req, res) => { console.log(req.body); res.send('ok'); }) express.urlencoded解析URL-encoded格式的请求体数据(有兼容性,在4.16.0+版本可用) app.use(express.urlencoded({ extended: false })) //在服务器可以使用req.body来接收客户端发送的请求体数据,若没有配置解析的中间件,req.body默认为{} app.post('/book', (req, res) => { console.log(req.body); res.send('ok'); }) 第三方中间件 运行npm install xxx使用require导入调用app.use()注册并使用例:(express.urlencoded和express.json基于body-parser封装而来) const express = require('express'); const app = express(); const parser = require('body-parser'); app.use(parser.urlencoded({ extended: false })) app.post('/user', (req, res) => { console.log(req.body); //req.body是请求体,包含了发送请求的内容 res.send('ok'); }) app.listen(80, () => { console.log('http://127.0.0.1'); }) 使用Express写接口 什么是CORS

CORS(Cross-Origin Resource Sharing,跨域资源共享)由一系列HTTP响应头组成,这些http响应头觉得浏览器是否阻止前端js代码跨域获取资源

浏览器的同源安全策略默认会阻止网页跨域获取资源,如果有接口服务器配置了CORS相关的HTTP响应头,就可以解除浏览器端的跨域访问限制

CORS的注意事项 CORS主要在服务器端进行配置,客户端无须做额外配置CORS在浏览器中有兼容性,只有支持XMLHttpRequest Level2的浏览器,才能正常访问(如:IE10+、Chrome4+、FireFox3.5+) CORS跨域资源共享 npm install cors 安装中间件require('cors')导入中间件app.use(cors())配置中间件 CORS响应头部 默认情况下,CORS仅支持客户端向服务器发送以下9个请求头: Accept、Accept-Language、Content-Language、DPR、DownLink、Sava-Data、ViewPort-Width、Width、Content-Type(值仅限于text/plain、multipart/form-data、application/x-www-form-urlencoded三者之一)默认情况下,CORS仅支持客户端发起GET、POST、HEAD请求 Access-Control_Allow-Origin

origin参数的值指定了允许访问该资源的外域URL

res.setHeader('Access-Control_Allow-Origin','<origin>')

*表示允许来自任何域的请求

res.setHeader('Access-Control_Allow-Origin','*') Access-Control_Allow-Headers

若客户端需要向服务器发送额外的请求头信息,需要在服务器端通过 Access-Control_Allow-Headers对额外的请求进行声明,否则请求会失败

res.setHeader('Access-Control_Allow-Header','Content-type, X-Custom-Header') Access-Control_Allow-Methods

如果客户端希望通过PUT、DELETE等其他方式请求服务器资源,需要通过Access-Control_Allow-Methods指明所允许使用的HTTP方法

res.setHeader('Access-Control_Allow-Methods','POST, GET, PUT, DELETE') //允许所有的HTTP方法 res.setHeader('Access-Control_Allow-Methods','*') JSONP 若要配置jsonp接口,必须在配置cors前配置jsonp,否则jsonp接口会被处理成开启了cors的接口例:(服务器端) app.get('/api/jsonp', (req, res) => { //获取客户端发送的回调函数的名字 const funcName = req.query.callback; //得到要通过JSONP发送给客户端的数据 const data = { name: 'a', age: 20 }; //根据前面的数据拼接函数调用的字符串 const str = `${funcName}(${JSON.stringify(data)})`; //将字符串响应给客户端的<script>标签进行解析执行 res.send(str); }) 数据库使用 SQL基本使用 select语句

select关键字对大小写不敏感

select * from 表名称 select 列名称 from 表名称 insert into语句

insert into 语句用于向数据表中插入新的数据行

insert into table_name(列1,列2...) values(值1,值2...) update语句

update用于修改表中的数据

update 表名称 set 列名称=新值 where 列名称=值 delete语句

delete用于删除表中的数据

delete from 表名称 where 列名称=值 count(*)函数

用于查询结果的总数据条数

select count(*) as total from 表名称 在项目中操作MySQL 安装第三方模块mysql通过mysql模块连接到数据集通过mysql模块执行sql语句基本使用: 建立连接 const mysql = require('mysql'); //建立连接关系 const db = mysql.createPool({ host: '127.0.0.1', //数据库的IP地址 user: 'root', //登录数据库的账号 password: '', //密码 database: '' //要操作的数据库 }) //测试mysql模块能否正常工作 db.query('select 1', (err, results) => { //查询失败 if (err) { return console.log(err.message); } //查询成功 //如果执行的是select查询语句,执行的结果是数组 console.log(results); }) 插入数据 //向users表中插入数据,可以通过affectedRows属性来判断是否插入成功 const user = { username: 'newUser', password: 'newPsd', status: '0' } //插入数据的简易写法 //const sqlStr = 'insert into users set ?'; const sqlStr = 'insert into users (username, password, status) values (?,?,?)'; //db.query(sqlStr, user, (err, results)=>{}) db.query(sqlStr, [user.username, user.password, user.status], (err, results) => { if (err) { return console.log(err.message); } //执行后结果是一个对象,可以通过affectedRows判断是否成功 if (results.affectedRows === 1) { console.log('插入成功!'); } }) 更新数据 //更新用户信息 const user = { id: 3, username: 'sb', password: '54sb', } //更新数据简便写法 //const sqlStr = 'update users set ? where id=?'; const sqlStr = 'update users set username=?, password=? where id=?'; //db.query(sqlStr, [user, user.id], (err, results) => {}) db.query(sqlStr, [user.username, user.password, user.id], (err, results) => { if (err) { return console.log(err.message); } if (results.affectedRows === 1) { console.log('更新成功!'); } }) 标记删除

先将欲删除的行进行标记,避免因误删而导致数据丢失的悲惨后果

const sqlStr = 'update users set status=1 where id=?'; db.query(sqlStr, 4, (err, results) => { if (err) { return console.log(err.message); } if (results.affectedRows === 1) { console.log('标记删除成功!'); } }) Web开发模式 服务器渲染的Web开发模式 概念:服务器发送给客户端的html页面,是在服务器通过字符串的拼接动态生成的,客户端不需要使用ajax这样的技术额外请求页面的数据优点: 前端耗时少有利于SEO。服务器的响应是完整的html页面内容,爬虫更容易爬取获得信息,更有利于SEO 缺点: 占用服务器端资源不利于前后端分离,开发效率低 前后端分离的Web开发模式 概念:依赖ajax技术,后端只提供api接口,前端使用ajax调用接口的开发模式优点: 开发体验好用户体验好减轻服务器端的渲染压力 缺点: 不利于SEO(可以利用Vue、React等前端框架的SSR(Server side render)技术解决) 如何选择Web开发模式 如果网站的主要功能是展示而没有复杂的交互,并且需要良好的SEO,可以使用服务器渲染的开发模式如果网站的交互性比较强,不需要考虑SEO,可以使用前后端分离若为了兼顾首页的渲染速度和前后端分离的开发效率,也可以采用首屏服务器渲染和其他页面前后端分离的开发模式 身份认证 不同开发模式的身份认证 服务器端渲染推荐使用Session认证机制前后端分离推荐使用JWT认证机制 Session认证机制 Http协议的无状态性

客户端的每次http请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次http请求的状态

Cookie Cookie是存储在用户浏览器的一段不超过4kb的字符串,有一个名称(Name)、一个值(Value)和其他几个用于控制Cookie有效期、安全性、使用范围的可选属性组成Cookie的几大特性: 自动发送域名独立过期时限4kb限制 过程: 客户端第一次请求服务器时,服务器通过响应头的形式,向客户端发送一个身份认证的Cookie,客户端将Cookie保存在浏览器中,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的Cookie通过请求头的形式发送给服务器,服务器即可验证客户端的身份 Cookie不具备安全性,不要使用Cookie存储重要且隐私的数据 Express中使用Session认证 配置session中间件 const session = require('express-session'); app.use(session({ secret: 'xxx', resave: false, saveUninitialized: true })) 向session中存放数据

express-session中间件配置后,可以通过req.session来访问和使用session对象,从而存储用户的相关信息

req.session.user = req.body; //存储用户信息 req.session.isLogin = true; //存储登录状态 清除session中的数据 req.session.destroy(); session认证的局限性

Session认证需要配合Cookie才能实现。Cookie默认不支持跨域访问,当涉及前端跨域请求后端接口的时候,需要做很多额外的配置,才能实现跨域Session认证

JWT认证机制 认识JWT JWT(JSON Web Token)是目前最流行的跨域认证解决方案工作原理:用户的信息通过Token字符串的形式,保存在客户端浏览器中,服务器通过还原Token字符串的形式来认证用户身份JWT组成部分:Header(头部)、Payload(有效荷载)、Signature(签名),三者之间用英文的“.”分割,格式:Header.Payload.Signature Payload部分存放真正的用户信息,是用户信息经过加密之后生成的字符串Header和Signature是安全性相关的部分,为了保护Token的安全

Signature 客户端收到服务器返回的JWT后,通常会存储在localStorage或sessionStorage中,客户端之后与服务器的通信中,可以将JWT放在HTTP请求头的Authorization字段中,来进行身份验证

Authorization:Bearer <token> Express中使用JWT认证 安装jsonwebtoken和express-jwt包 jsonwebtoken用于生成JWT字符串express-jwt用于将JWT字符串解析还原成JSON对象 定义secret密钥 保证JWT字符串的安全性,防止JWT字符串在网络传输过程中被别人破解当生成JWT字符串时,需要使用secret密钥对用户信息进行加密,最终得到加密好的字符串把JWT字符串解析还原成JSON对象时,需要使用secret密钥进行解密 生成JWT字符串 调用jsonwebtoken包提供的sign()方法,将用户信息加密成JWT字符串,响应给客户端jwt.sign()方法包含3个参数: 参数1:用户的信息对象参数2:加密的密钥参数3:配置对象,配置当前token的有效期(expiresIn:‘3s’ && expiresIn:‘3h’) const tokenStr = jwt.sign({ username: userInfo.username }, secretKey, { expiresIn: '30s' }); res.send({ //为了方便客户端使用token,在服务器端直接拼上前缀 token: 'Bearer ' + tokenStr }) 将JWT字符串解析为JSON对象

注意:

只要配置成功了express-jwt这个中间件,就可以把解析出来的用户信息挂载到req.user属性上不要把密码加密到token中 // expressJWT({secret: secretKey}) 用来解析token的中间件 // .unless用来指定哪些接口不需要访问权限 app.use(expressJWT({ secret: secretKey, algorithms: ['HS256'] }).unless({ path: [/^\/api\//] })) 捕获解析JWT失败后产生的错误 app.use((err, req, res, next)=>{ // token解析失败导致的错误 if(err.name === 'UnauthorizedError'){ return res.send({ status: 401, message: '无效token' }) } //其他原因导致的错误 res.send({ status: 500, message: '未知错误' }) }) 对密码进行加密处理

可以使用bcryptjs对要存放进数据库的密码进行加密

优点:

加密之后的密码无法被逆向破解同一密码多次加密,得到的加密结果不相同

步骤:

安装bcryptjs包导入bcryptjs在处理函数中调用bcrypt.hashSync(明文密码,随机的长度)方法进行加密处理,示例: const bcrypt = require('bcryptjs'); password = bcrypt.hashSync(password, 8); 调用bcrypt.compareSync(用户提交的密码,数据库中的密码)方法比较密码是否一致,返回值是布尔值,示例: const compareResult = bcrypt.compareSync(xx,xx); if(!compareResult){ return res.send({ status: 1, message: '密码错误!' }) } 表单验证 前端验证为辅,后端验证为主适当使用第三方的包来辅助验证(如:joi包、@escook/express-joi包)例: const joi = require('joi') /** * string() 值必须是字符串 * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 * min(length) 最小长度 * max(length) 最大长度 * required() 值是必填项,不能为 undefined * pattern(正则表达式) 值必须符合正则表达式的规则 */ // 用户名的验证规则 const username = joi.string().alphanum().min(1).max(10).required() // 密码的验证规则 const password = joi .string() .pattern(/^[\S]{6,12}$/) .required() 解析表单数据

注:使用express.urlencoded()无法解析multipart/form-data格式的请求体数据

可以使用multer来解析multipart/form-data格式的表单数据

步骤:

安装multer包导入并配置multer,创建multer的实例对象,通过dest属性指定文件的存放路径 const multer = require('multer'); const path = require('path'); const upload = multer({ dest: path.join(__dirname, 'xxx') }) // upload.single() 是一个局部生效的中间件,用来解析 FormData 格式的表单数据 // 将文件类型的数据,解析并挂载到 req.file 属性中 // 将文本类型的数据,解析并挂载到 req.body 属性中 router.post('/add', upload.single('cover_img'), article_handler.addArticle)


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #nodejs学习记录总结