node.js 介绍
node.js是什么
- node.js 是一个开发平台,就像java开发平台...
- 何为开发平台? 有对应的编程语言,有语言运行时,有能实现特定功能的API
- 该平台使用的编程语言是 javascript 语言
- node.js 平台是基于 v8 引擎构建
- 基于node.js可以开发控制台程序(命令行程序,CLI程序),桌面应用程序(GUI)(借助node-webkit等框架实现),web应用程序(网站)
php开发技术栈: LAMP - Linux Apache MySQL PHP
node.js 全栈开发技术栈: MEAN - MongoDB Express Angular Node.js
node.js 有哪些特点?
- 事件驱动
- 非阻塞 I/O 模型
- 单线程
- 拥有世界最大的开源库生态系统 -- npm
node.js 安装和配置
- 官方解释
- LTS 版本: 稳定版
- current版本: 最新版本
- 注意
- 配置环境变量:
- 通过 nvm-windows 控制管理 node多个版本
node.js 开发web程序和 php,java,asp.net 等传统模式开发web应用程序区别
- 传统模式
- 有web容器
- node.js
- 没有web容器
在node.js 上编写程序
REPL介绍
REPL 全称 read eval print loop (交互式解释器)
R 读取
E 执行
P 打印
L 循环在REPL中编写程序(类似于F12 console控制台)
- 直接在控制台输入
node
命令进入REPL环境
- 按两次ctrl+c 退出REPL 或者 输入
.exit
fs
读取文件
var fs = require('fs')
fs.readFile('路径',(err,data)=>{
if(err) throw err;
cl(data.toString('utf-8'))
})
__dirname和__filename获取正在执行的js文件路径
这两个东西, 不算是全局的 相当于本地的 会在当前js中产生一个闭包吧他们传进去
path
通过path模块进行路径拼接
path.join(__dirname,'hello.txt');
error-first介绍(错误优先)
http模块
见: 搭建一个简单的http服务程序.js
通过服务区端设置响应报文头来解决乱码问题
response.setHeader('Content-Type','text/plain; charset=utf-8');
http响应报文头 作用 告诉浏览器发送数据的其他相关信息
根据不同请求做出不同的响应
response.setHeader('Content-Type','text/html; charset=utf-8');
try-catch 的使用
异步操作, try-catch是无法捕获异常的
对于异步操作, 要通过判断错误号(err.code)来进行出错出处理
npm模块 mime
通过mime 可以根据后缀 得到mime type
在请求服务器的时候, 请求的 url 就是一个标识
res对象
res.write()
res.end() 结束响应(请求)
res.setHeader('Content-Type': 'text/html') 设置响应报文头
res.statusCode = 404; 设置 http 响应状态码
res.statusMessage = 'Not Found'; 设置 http 响应状态码对于信息
-
res.writeHead() 直接向客户端响应(写入)响应报文头
res.writeHead(404,'Not Found',{ 'Content-Type': 'text/html' })
res.setHeader方法 和 res.writeHead方法 区别
npm (node package manager) node包管理器
npm 和 node.js
- npm本身也是基于node.js开发的包
如何安装npm
- npm会随着node.js自动安装
- 查看npm版本:
npm -v
- 更新npm:
npm install npm@latest -g
npm使用
- 在网站找到需要的包
- 在项目根目录下 执行
npm install 包名
安装 - 在node.js代码中通过 `require('包名') 加载该模块
---- 本地安装
npm全局安装介绍
npm install mime -g
mime a.txt
安装完毕后可以在命令行中直接使用(本质)
npm全局安装世纪做了两件事:
- 下载包到指定目录
- 创建一段命令行执行代码
package.json 文件(包说明文件)
元数据: 描述自身的数据
如何创建
- 通过
npm init
或者npm init -y
或者npm init -yes
命令 - 手动创建
注意
- 创建时,执行命令所在目录名称不能包含大写字母和中文
package-lock.json 文件
记录了要安装包的所有信息 可以加快安装速度
DAY 3
路由组成: 请求方法 请求路径
url模块 parse方法 获取get提交数据
str.startwith('xx') //字符串方法 以xx开头 返回boolean
const url = require('url');
const urlObj = url.parse(req.url, true); // 返回包含url所有内容的对象 第二个参数为true 可以让urlObj.jquery内容转为json对象
req.query 获取? 后面的数据
restful形式的接口
服务器想让浏览器做什么事情 通过传递响应报文头来实现
underscore 工具函数库
const _ = require('underscore');
_.zip
const html = '<%= name %>';
const fn = _.template(html)
html = fn({name:'哈哈'}) // 哈哈
DAY 4
封装异步代码 需要一个回调函数
nodejs中模块
一. nodejs中模块的分类
1.核心模块, 内置模块, 原生模块
2.文件模块
如果加载时没有指定后缀名,name按照如下顺序依次加载响应模块
- .js
- .json
- .node
- 文件夹 -> package.json -> main (入口文件 app.js 没有的话-> index.js/index.json/index.node) -> 加载失败
注意:文件模块加载 最好带后缀名
3.自定义模块
- mime
- moment
- ...
情况一: require() 参数是一个路径
- .js
- .json
- .node
- 文件夹 -> package.json -> main (入口文件 app.js 没有的话-> index.js/index.json/index.node) -> 加载失败
情况二: require() 参数是一个模块名称
- 核心模块: 直接加载
- 不是核心模块: 会在当前文件夹找 node_module
- 依次递归查找 node_module 目录中是否有相应的包
- 从当前目录开始,依次地魁查找所有父目录中的node_module 目录中是否有相应的包
- 如果查找完毕 磁盘根目录依然没有则加载失败
- 打印数据 module.paths 查看
Common JS 规范
总结: commonJS 是为JavaScript 语言判定的一种 模块规范, 编程 API规范
exports 和 module.exports 区别
- exports 和 module.exports 都指向的是用一个对象
- 最终 require() 函数返回的是 module.exports 中的数据
- exports 仅仅是一个 快捷方式
模块化思路
- 该模块要封装什么代码
- 这些代码有用到外部数据吗? 如果用到,是否需要通过参数传递到当前模块中
- 当前模块要暴露的东路(module.exports 的值)
DAY 5
Buffer
一. 类型介绍
- javascript 语言没有读取或操作二进制数据流的机制
- Node.js中引入 Buffer类型使我们可以操作TCP流或文件流
- Buffer类型的对象类似于整数数组, 但大小是固定的(buf.length是固定的,不允许修改)
- Buffer是全局的,所以使用的时候无需require()的 方式来加载
二. 创建一个Buffer对象
var buf = Buffer.from('你好!');
console.log(buf);
console.log(buf.toString('utf8'));
三. Buffer 对象与编码
node.js目前支持的编码:
- ascii
- utf8
- base64
- ...
使用 nodemon
工具来自动重启web服务器
- nodemon的作用:能够实时监听当前项目中,文件的变化;只要监听到了文件的变化,则 nodemon 工具,会自动重新启动 web 服务器,从而使最新的代码生效;免去了程序员手动重启服务器的困扰;
- 如何安装:运行
npm i nodemon -g
全局安装即可; - 如何使用:
- 之前使用
node 要执行的文件路径
来运行 Node.js 代码; - 现在使用
nodemon 要执行的文件路径
来运行 Node.js 代码;
- 之前使用
- 注意:今后在开发Web项目的时候,推荐使用
nodemon
来启动 web 服务器
express (Node 中开发web项目的框架)
定义(什么是Express):一个快速的网站开发框架,封装了原生的http模块,用起来更方便;API更人性化
什么是express
- 基于 node.js平台开发的 'web开发框架',就是一个node.js模块
- 作用: 提供一系列强大的特性,帮助你创建各种web和移动设备的应用
特点
- 实现了路由功能
- 中间件(函数)功能
- 对req,res对象的扩展
- 可以集成其他模板引擎
express 框架的安装和基本使用
- 安装:运行
npm i express -S
即可安装 - 创建基本的
express
服务器:- 导入
express
第三方模块; - 创建服务器的实例:调用
const app = express()
方法; - 通过
app.get()
或app.post()
方法,来监听客户端的get
或post
请求,具体语法:- 监听
GET
请求:app.get('请求地址', (req, res) => { 处理函数 })
- 监听
POST
请求:app.post('请求地址', (req, res) => { 处理函数 })
- 监听
- 启动 express 服务器:通过
app.listen(端口, IP地址, 启动成功后的回调函数)
启动服务器;
- 导入
express 中的快捷方法
-
res.send()
- 支持 发送 字符串
Content-Type: text/html;
- 支持 发送 对象 或 数组
Content-Type: application/json
- 支持 发送 Buffer 此时会当作文件下载;
- 支持 发送 字符串
-
res.sendFile()
- 用法1:
res.sendFile(path.join(__dirname, './view/index.html'))
- 用法2:
res.sendFile('./view/movie.html', { root: __dirname })
- 注意:
res.sendFile()
可以向浏览器发送 静态页面;
- 用法1:
res.send()和res.end()区别
res.end()用于快速响应,没有数据返回到客户端
res.send()有数据返回到客户端
res对象的常见方法
res.json() 等价于 res.send(json)
res.sendFile() 读取文件并响应
res.redirect() 重定向
res.status(404).end('文件不存在')
使用 express.static()
快速托管静态资源
如果我们网站中,有很多静态资源需要被外界访问,此时,使用 res.sendFile 就有点力不从心了;
这时候,express 框架,为我们提供了
express.static('静态资源目录')
来快速托管指定目录下的所有静态资源文件;
- 语法1:
app.use(express.static('public'));
-
app.use()
方法,是专门用来注册 中间件; -
express.static
是express的内置中间件;
-
- 语法2:
app.use('/虚拟目录', express.static('public'))
为 express 框架配置 ejs模板引擎 渲染动态页面
- 安装 ejs 模板引擎
npm i ejs -S
- 使用 app.set() 配置默认的模板引擎
app.set('view engine', 'ejs')
- 使用 app.set() 配置默认模板页面的存放路径
app.set('views', './views')
- 使用 res.render() 来渲染模板页面
res.render('index.ejs', { 要渲染的数据对象 })
,注意,模板页面的 后缀名,可以省略不写!
在 express 中配置 art-template
安装 两个包
cnpm i art-template express-art-template -S
自定义一个模板引擎
app.engine('自定义模板引擎的名称', 渲染函数)
将自定义的模板引擎,配置为 express 的默认模板引擎
app.set('view engine', '具体模板引擎的名称')
配置 模板页面得存放路径
app.set('views', '路径')
使用 express 框架中提供的路由来分发请求
- 什么是路由:路由就是对应关系;
- 什么叫做后端路由:前端请求的URL地址,都要对应一个后端的处理函数,那么 这种URL地址到 处理函数之间的对应关系,就叫做后端路由;
- 在Express中,路由的主要职责 就是 把请求分发到对应的处理函数中;
- 在Express中,如何 定义并使用路由呢?
js
// 1. 封装单独的 router.js 路由模块文件
const express = require('express')
// 创建路由对象
const router = express.Router()
router.get('/', (req, res)=>{})
router.get('/movie', (req, res)=>{})
router.get('/about', (req, res)=>{})
// 导出路由对象
module.exports = router
- express 创建的 app 服务器,如何使用 路由模块呢?
js
// 导入自己的路由模块
const router = require('./router.js')
// 使用 app.use() 来注册路由
app.use(router)
注册路由方式
- app.get()
- 请求路径必须完全匹配
- 方式必须是 get
- app.post()
- 请求路径必须完全匹配
- 方式必须是 post
- app.use()
- 请求路径中第一部分只要与 '/index' 相等即可,并不要求完全匹配
- 进行路由匹配的时候不限定方法
- app.all()
- 请求路径必须完全匹配
- 进行路由匹配的时候不限定方法
通过 正则 注册路由
1,方式是 get
2,请求路径以/index开头即可
app.get(/^\/index(\/.+)*$/,(req, res)=>{
res.send('hi!');
})
Express 框架里 中间件的概念
1. 什么是中间件
定义:中间件就是一个处理函数;只不过这个函数比较特殊,包含了三个参数,分别是
req
,res
,next
注意:中间件方法中的三个参数:
- req:请求对象;
- res:响应对象;
- next:next()可以被调用,表示调用下一个中间件方法;
2. Express 框架中对中间件的5种分类
- 应用级别的中间件: 挂载到 app 上的中间件
app.get('URL地址', (req, res, next)=> {})
; - 路由级别的中间件: 挂载到 router 对象上的中间件
router.get('url地址', (req, res, next)=>{})
- 错误级别的中间件: 回调函数中,有四个参数
app.use((err, req, res, next)=>{})
- 唯一内置的中间件:
express.static()
- 第三方中间件: 非express框架提供的,需要程序员手动安装才能使用的中间件;
body-parser
解析post 表单数据
中间件的概念,了解即可,因为实际开发中,我们都直接使用第三方现成的中间件;
express中获取参数的几种形式
-
获取
http://127.0.0.1:3001/user?id=10&name=zs
中的查询参数:- 直接使用
req.query
获取参数即可; - 注意:URL 地址栏中通过
查询字符串
传递的参数,express 框架会直接解析,大家只需要使用req.query
直接获取 URL 中 查询字符串的参数;
- 直接使用
-
从URL地址中获取路径参数:
- 假设后台的路由是
app.get('/user/:id/:name', (req, res) => {})
- 假设客户端浏览器请求的URL地址为:
http://127.0.0.1:3001/user/10/zs
- 直接使用
req.params
可以获取URL地址中传递过来的参数;
通过 req.params 获取路由中的参数
app.get('/news/:yeas/:month/:day',(req, res)=>{ res.send(req.params); })
- 假设后台的路由是
-
从post表单中获取提交的数据:
- 借助于
body-parser
来解析表单数据 - 安装:
npm i body-parser -S
- 导入:
const bodyParser = require('body-parser')
- 注册中间件:
app.use(bodyParser.urlencoded({ extended: false }))
- 使用解析的数据:
req.body
来访问解析出来的数据
- 借助于
Web 开发模式
1. 混合模式(传统开发模式)
- 以后端程序员为主,基本上不需要前端程序员,或者,前端程序员只负责画页面、美化样式、写JS特效,前端程序员不需要进行数据的交互;
- 这种开发模式,在早些年比较常见;
- 传统开发模式下,用的最多的是 Jquery + 模板引擎 + Bootstrap
- 后端页面 .php .jsp .aspx .cshtml
2. 前后端分离(趋势)
- 后端负责操作数据库、给前端暴露接口
- 前后端分离的好处:保证了各个岗位的职责单一;
- 前端负责调用接口,渲染页面、前端就可以使用一些流行的前端框架 Vue, React, Angular
MVC
JSONP 和 CORS 的区别
- JSONP的原理:动态创建script标签;
- JSONP发送的不是Ajax请求
- 不支持 Post 请求;
- CORS中文意思是
跨域资源共享
,需要服务器端进行CORS
配置;
- CORS 发送的是真正的Ajax请求
- CORS 支持Ajax的跨域
- 如果要启用 CORS 跨域资源共享,关键在于 服务器端,只要 服务器支持CORS跨域资源共享,则 浏览器肯定能够正常访问 这种 CORS 接口;而且,客户端在 发送 Ajax的时候,就像发送普通AJax一样,没有任何代码上的变化;
- 对于Node来说,如果想要开启 CORS 跨域通信,只需要安装
cors
的模块即可;
day 6
HTTP协议的无状态性
- HTTP协议的通信模型:基于
请求 - 处理 - 响应
的! - 由于这个通信协议的关系,导致了HTTP每个请求之间都是没有关联的,每当一个请求完成之后,服务器就忘记之前谁曾经请求过!
- 如果纯粹基于HTTP通信模型,是无法完成登录状态保持的!每次请求服务器,服务器都会把这个请求当作新请求来处理!
- 我们可以通过 cookie 技术,实现状态保持,但是由于cookie是存储在客户端的一门技术,所以安全性几乎没有,因此不要使用cookie存储敏感的数据!
cookie介绍
1. 什么是cookie,作用是什么
- 由于Http协议是无状态的,且传统服务器只能被动的响应请求,所以,当服务器获取到请求的时候,并不知道当前请求属于哪个客户端!
- 服务器为了能够明确区分每个客户端,需要使用一些小技术,来根据不同的请求区分不同的客户端;
- 只要有请求发生,那么必然对应一个客户端,我们可以在每次客户端发起请求的时候,向服务器自动发送一个标识符,告诉服务器当前是哪个客户端正在请求服务器的数据;
- 如何提供这个标识符呢?我们可以在请求头(Request Headers)中添加一个标签,叫做
cookie
,这样,每次发送请求,都会把这个cookie随同其他报文一起发送给服务器,服务器可以根据报文中的cookie,区分不同的客户端浏览器。 - 如何在客户端请求头中添加标识符?
- 在Node中可以在
writeHeader
的时候,通过Set-Cookie
来将cookie标识通过响应报文发送给客户端! - 客户端也可以通过一些方式来操作自己的cookie,比如通过
jquery.cookie
这个插件!
2. cookie的基本使用
var http = require('http');
var server = http.createServer();
server.on('request', function (req, res) {
// 解析cookie
var cookies = {};
var cookieStr = req.headers.cookie; // 从请求的headers中获取cookie信息
cookieStr && cookieStr.split(';').forEach(function (item) {
var parts = item.split('=');
cookies[parts[0].trim()] = parts[1].trim(); // 将cookie解析出来,保存到对象中
});
res.writeHeader(200, {
'Content-Type': 'text/plain; charset=utf-8',
"Set-Cookie": ['issend=ok', 'age=20']
});
if(cookies.issend ==='ok'){
res.end('不要太贪心哦!');
}else{
res.end('呐,赏你一朵小红花~~');
}
});
server.listen(4000, function () {
console.log('服务器已启动!');
});
3. 通过expires
设置Cookie的过期时间
// 设置 过期时间 为60秒之后
// 注意:在设置过期时间的时候,需要将时间转换为 UTC 格式
var expiresTime = new Date(Date.now() + 1000 * 60).toUTCString();
res.writeHeader(200, {
'Content-Type': 'text/html; charset=utf-8',
'Set-Cookie': ['isvisit=true;expires=' + expiresTime, 'test=OK']
});
res.end('<h3>你好,欢迎光临,送给你一个苹果!</h3>');
GMT和UTC有什么区别?格林尼治标准时(GMT)与世界时(UTC)是怎么回事
4. cookie可以被伪造,不安全
使用谷歌插件edit this cookie
,就能伪造cookie数据!所以不要使用cookie存储敏感的数据!比如登录状态和登录信息;
一些敏感的数据,应该存储都服务器端!
5. 什么是Cookie的应用场景
- 对安全性要求不高
- 不需要存储大量的数据
- 主要应用场景,是用来做 客户端 与 服务器之间的 状态保持技术;
登录退出及状态保存
1 使用express-session
来保存登录状态
1.1 什么是session
由于HTTP是无状态的,所以服务器在每次连接中持续保存客户端的私有数据,此时需要结合cookie技术,通过session会话机制,在服务器端保存每个HTTP请求的私有数据;
1.2 session原理
在服务器内存中开辟一块地址空间,专门存放每个客户端私有的数据,每个客户端根据cookie中保存的私有sessionId,可以获取到独属于自己的session数据。
1.3 在express中使用session
安装session模块
npm install express-session -S
-
导入session模块
var session = require('express-session')
-
在express中使用
session
中间件:// 启用 session 中间件 app.use(session({ secret: 'keyboard cat', // 相当于是一个加密密钥,值可以是任意字符串 resave: false, // 强制session保存到session store中 saveUninitialized: false // 强制没有“初始化”的session保存到storage中 }))
-
将私有数据保存到当前请求的session会话中:
// 将登录的用户保存到session中 req.session.user = result.dataValues; // 设置是否登录为true req.session.islogin = true;
-
通过
destroy()
方法清空session
数据:req.session.destroy(function(err){ if(err) throw err; console.log('用户退出成功!'); // 实现服务器端的跳转,这个对比于 客户端跳转 res.redirect('/'); });
day7
MD5 的特性
- MD5 是一种加密算法,在调用这个算法的时候,提供一个密码的明文, 调用的结果,得到一个 32 位长度的密文;
- MD5 算法的特性:相同的字符串,如果多次调用 md5 算法,得到的结果,完全一样;
- MD5 算法,无法被逆向解密;
- 但是,基于 md5 算法的第二个特性,我们可以进行碰撞暴力破解;(MD5 存在被暴力破解的安全性问题)
- 为了解决 简单的明文密码,被 md5 加密后,通过 暴力破解的安全性问题, 然后就出现了加盐的MD5加密;
- 目前,md5的暴力破解,又升级了,升级到了
彩虹表
; - 由于彩虹表出现,我们推荐大家,在存储网站密码的时候,使用
bcrypt
加密算法,得到加密之后的密文进行存储;
bcrypt 加密算法
- 在调用加密算法的时候,需要手动提供一个
幂次
; - 调用加密算法,得到的加密结果格式:
$版本号$循环的幂次$22位的随机盐 31位的密文
- 加密的
随机盐
和加密的幂次
,和加密算法的版本号
已经被存储到了真正的密文中;
- 加密的
项目中使用 bcrypt 的步骤
运行
npm i node-pre-gyp -g
在项目根目录中,打开终端,运行
cnpm install bcrypt -S
-
导入
bcrypt
// 导入加密的模块 const bcrypt = require('bcrypt')
-
定义幂次:
// 定义一个 幂次 const saltRounds = 10 // 2^10
-
调用
bcrypt.hash()
加密:// 加密的方法 bcrypt.hash('123', saltRounds, (err, pwdCryped) => { console.log(pwdCryped) })
-
调用
bcrypt.compare()
对比密码是否正确:// 对比 密码的方法 bcrypt.compare('123', '$2b$10$i1ufUKnC9fXTsF9oqqvLMeDnpNfYIvhyqKRG03adiebNFPkjW3HPW', function(err, res) { console.log(res) // 内部对比的过程: // 1. 先获取 输入的明文 // 2. 获取输入的密文 // 2.1 从密文中,解析出来 bcrypt 算法的 版本号 // 2.2 从密文中,解析出来 幂次 // 2.3 从密文中,解析出来前 22 位 这个随机盐 // 3. compare 方法内部,调用 类似于 hash 方法 把 明文,幂次,随机盐 都传递进去 最终得到正向加密后的密文 // 4. 根据最新得到的密文,和 compare 提供的密文进行对比,如果相等,则 返回 true ,否则返回 false; })
使用模板引擎处理公共部分
在PHP中,抽取公共的区域,直接使用PHP语法就行;
但是,在Express的框架中,并没有抽取页面公共部分的语法,需要模板引擎提供这样的语法;
添加文章并跳转到文章详情
- 发表文章之前,需要使用 第三方的插件,叫做
markdown + editor
=>mditor
- 注意:
mditor
这个第三方模块,提供了两个功能:
- 功能1: 可以当作一个纯粹的MarkDown编辑器插件,在前端页面中使用;
- 功能2: 在Node端,我们可以
require('mditor')
,使用这个模块,提供的方法,把markdown
文本,解析转换为HTML
内容;