Node.js
1.Node.js 概述
运行在服务器的JS环境
(1)对比JS
JS运行在客户端浏览器,存在多款浏览器,有兼容性问题,Node.js运行在服务器端,只有一种解析器,没有兼容性问题
两者都有相同的内容之对象、自定义对象,不同的宿主对象
JS用于开发网页中的交互效果,Node.js用户服务器端的开发,例如:数据库访问,其他服务器调用
(2)运行
脚本模式
node 拖拽文件
交互模式
node 回车———进入交互模式
两次 ctrl + c 或者 一次 ctrl + d —— 退出
(3)特点
是单线程运行,支持数万个并发连接,适合做基于社交网络的大规模WEB应用
(4)API文档
www.nodejs.cn 中文网
www.nodejs.org 英文官网
2.全局对象
(1)global
检测一个变量或者函数是否为全局的
在Node.js下,一个JS文件不是全局作用域,防止全局污染
在JS下,一个JS文件是在全局作用域,存在全局污染
JS下的global 对象名称 window
(2)console
控制台对象,用于输出到控制台
console.log(1); //日志
console.info(2); //消息
console.warn(3); //警告
console.error(4); //错误
console.time(); //开始计时
console.timeEnd(); //结束计时
开始计时和结束计时的参数要保持一致
练习:统计while,do-while,for循环100000次的耗时
//开始计时
console.time('tao');
for(var i=1;i<=100000;i++){
}
//结束计时
console.timeEnd('tao');
console.time('while');
var j=1;
while(j<=100000){
j++;
}
console.timeEnd('while');
console.time('do-while');
var k=1;
do{
k++;
}while(k<=100000);
console.timeEnd('do-while');
(3)process
进程:计算机上启动的每一个软件都是一个进程,每个进程都有相应的CPU,内存占用
//process 是Node.js进程对象
process.arch(); //查看CPU的架构
process.platform(); //查看操作系统
process.pid(); //查看当前的进程编号
process.kill(); //结束指定编号的进程
(4)Buffer
缓冲区:是内存中的区域,用于临时存储数据
var buf = Buffer.alloc(5, 'abcde'); //创建buffer,分配空间大小,并存储数据,单位是字节,1个汉字占3个字节
buf.toString(); //将Buffer数据转为字符串格式
模块
每个模块是一个功能体,每个文件就是一个模块,一个模块可以被其他的模块所引入
1.模块中的成员
require(); //用于引入其他的模块
modeule.exports = ?; //当前模块导出的对象(暴露的内容),默认是一个空对象
__dirname; //获取当前模块的绝对路径
__filename; //获取绝对路径+模块名称
2.模块的分类
分为自定义模块、核心模块、第三方模块
3.包和npm
包:就是指第三方模块
npm:用来管理包的工具,例如:下载、上传、更新、卸载。。。
npm在Node.js安装的时候会附带安装
num -v 查看npm版本
CommonJS:是Node.js的模块规范,引入,暴露都是基于这个规范
网址: www.npmjs.com
(1)切换命令行的路径
(2)npm命令
npm init -y;
//会在命令行对应的目录下创建package.json ,作为项目说明文件,可以记录下载的包都有哪些
npm install 包名称;
//下载安装指定的包,将包放入到 node_modules 目录,如果目录不存在会自动创建;同时会生成记录所有包的版本号;在package.json 中也会记录下载的包
npm install;
//会自动读取package.json 和 package-lock.json中记录的包并安装
//其他npm命令,参考: www.npmjs.cn
4.查询字符串模块(querystring)
如: price=5000&sex=男
查询字符串:是浏览器向服务器传递参数的一种方式,位于网址中
查询字符串模块:用于操作查询字符串的工具
parse()
将查询字符中的参数转为对象
练习:获取以下查询字符串中传递的参数
ename=tao&sex=1&salary=50000
最后打印格式 ‘姓名:xxx 性别:x 工资:xxx ’
const querystring=require('querystring');
var str='ename=tao&sex=0&salary=50000';
var obj=querystring.parse(str);
console.log(obj);
console.log(`
姓名:${obj.ename}
性别:${parseInt(obj.sex) ? '男' : '女'}
工资:${obj.salary}
`);
5.网址模块(url)
URL:统一资源定位,互联网上任何的资源都有对应URL,URL用来访问资源
资源(html,css,js,图像,声音…)
http://www.codeboy.com:9999/product_details.html?lid=1#one
协议 域名/IP地址 端口 文件在服务器的路径 查询字符串 锚点
new URL() 将一个URL转换为对象,可以获取其中各个部分
练习:获取以下URL中传递的数据
https://www.tmooc.cn:443/course/web.html?cid=2109&cname=nodejs#two
最后打印 ‘课程编号:xxx 课程名称:xxx’
//引入查询字符串模块
const querystring=require('querystring');
var str='https://www.tmooc.cn:443/course/web.html?cid=2109&cname=nodejs#two';
//转换为对象
var obj=new URL(str);
//获取查询字符串部分
var str2=obj.search.slice(1);
//console.log(str2);
//将查询字符串转为对象
var obj2=querystring.parse(str2);
console.log(obj2);
console.log(`课程编号:${obj2.cid} 课程名称:${obj2.cname}`);
6.定时器
(1)一次性定时器
//开启
var timer = setTimeout(回调函数,间隔时间);
//当间隔时间到了,会调用一次回调函数
//清除
clearTimeout(timer);
(2)周期性定时器
//开启
var timer = setInterval(回调函数,间隔时间);
//,每隔一段时间,调用一次回调函数
//清除
clearInterval(timer);
(3)立即执行定时器
setImmediate(回调函数)/clearImmediate()
process.nextTick(回调函数)
//没有清除
练习:使用周期性定时器每隔3秒钟打印’hello’,打印3次后清除定时器。
预习nodejs第3天
//练习:使用周期性定时器每隔3秒钟打印'hello',打印3次后清除定时器。
//声明变量用于计数
var num=0;
var timer=setInterval(()=>{
console.log('hello');
//打印一次,记录一次
num++;
//判断是否为3
if(num===3){
clearInterval(timer);
}
},1000);
同步和异步
1.同步和异步
同步:在主线程中执行,会阻止后续代码的执行,通过返回值获取结果
异步:在一个独立的线程执行,不会阻止主程序中后续代码的执行,结果以回调函数的形式获取
2.文件系统模块(fs)
文件分为文件形式和目录形式
//左边同步,右边异步
(1)查看文件的状态
statSync(文件的路径) / stat(文件的路径,回调函数)
isFile(); //是否为文件形式
isDirectory(); //是否为目录形式
(2)创建目录
mkdirSync(目录的路径); / mkdir(目录的路径,回调函数);
(3)移除目录
rmdirSync(目录的路径); / rmdir(目录的路径,回调函数);
(4)读取目录
readdirSync(目录的路径); / readdir(目录的路径,回调函数);
//读取的结果为数组格式
(5)清空写入文件
writeFileSync(文件的路径,写入的数据); / writeFile(文件的路径,写入的数据,回调函数);
//如果文件不存在,先创建文件然后写入数据
(6)追加写入文件
appendFileSync(文件的路径,写入的数据); / appendFile(文件的路径,写入的数据,回调函数);
//如果文件已经存在,会在文件的末尾追加写入数据
(7)读取文件
readFileSync(文件的路径); / readFile(文件的路径,回调函数);
//读取的数据格式为buffer
(8)删除文件
unlinkSync(文件的路径); / unlink(文件的路径,回调函数);
(9)检测文件是否存在
existsSync(文件的路径);
//存在 -> true ; 不存在 -> false
(10)拷贝文件
copyFileSync(原文件路径,目标文件路径); / copyFile(源文件路径,目标文件路径,回调函数);
2.文件流
createReadStream(); //创建可读取的流对象,分段读取一个文件
createWriteStream(); //创建可写入的流,文件不存在会自动创建
pipe(); //管道,可以将读取的流分段添加到写入的流
on(事件名称,回调函数); //添加事件,事件名称是固定的字符串,一旦触发事件自动执行回调函数
//获取流入到内存的每一段数据
//添加事件:监听是否有数据流入,一旦有数据流入通过回调函数获取这段数据
//'data':固定用法,表示数据流入事件
var count=0;
rs.on('data',(chunk)=>{
//chunk获取的分段数据
//console.log(chunk);
count++;
});
//添加事件:监听是否读取结束,一旦结束执行回调函数
rs.on('end',()=>{
console.log(count);
});
3.http协议
超文本传输协议:是浏览器和WEB服务器之间的通信协议
(1)通用头信息
Request URL: 请求的URL,要请求的服务器上的资源
Request Method: 请求的方法,对资源的操作方法 get/post
Status Code: 响应的状态码
1**: 接收到了部分请求,还没有做出响应
2**: 成功的响应
3**: 响应的重定向
4**: 客户端请求错误
5**: 服务器错误
(2)响应头信息(Response)
Location: 设置要跳转的URl,结合状态码 3** 使用
Content-Type: 设置响应的内容类型,解决中文乱码 text/html;charset=utf-8
(3)请求头信息(Request)
(4)请求主体
只有在传递数据的时候才会出现
http模块
可以用来创建WEB服务器
http.creadteServer(); //创建WEB服务器
listen(); //设置端口,用来请求WEB服务器
//通过事件接受请求,做出响应
app.on('request',(req,res)=>{
//req 请求对象
//res 响应对象
res.writeHead(状态码,头信息); //设置响应的状态码和头信息
res.write(); //设置显示到浏览器的内容
res.end(); //结束并发送到浏览器
});
//设置端口
app.listen(8080,()=>{
console.log('服务器启动成功');
});
//请求服务器
// http://127.0.0.1:8080
// http://localhost:8080
//如:
//接收请求,作出响应
//给服务器添加事件,一旦请求,自动执行回调函数
app.on('request',(req,res)=>{
//req 请求的对象
//res 响应的对象
//设置头信息中内容类型
res.writeHead(200,{
'Content-Type':'text/html;charset=utf-8'
});
//设置响应到浏览器的内容
res.write('这是你的jianbing');
//结束并发送
res.end();
});
练习:创建WEB服务器,设置端口,添加事件接收请求,响应1.html网页到浏览器端(先使用同步方法读取文件数据,然后设置设响应的内容)
//引入http模块
const http=require('http');
//引入fs模块
const fs=require('fs');
//创建WEB服务器
const app=http.createServer();
//设置端口,用来请求WEB服务器
app.listen(8080,()=>{
console.log('服务器启动成功');
});
//添加事件,接收请求并作出响应
app.on('request',(req,res)=>{
//req请求的对象
//获取请求的URL,请求的方法
console.log(req.url,req.method);
/*
//设置响应的状态码和内容类型
//res.writeHead(200,{
// 'Content-Type':'text/html;charset=utf-8'
//});
//读取1.html文件中的数据
var data=fs.readFileSync('./1.html');
//把读取的文件作为要响应的内容
res.write(data);
//结束并发送响应
res.end();
//设置状态码为302,跳转的URL
res.writeHead(302,{
Location:'https://www.tmooc.cn/'
});
//结束并发送
res.end();
*/
//设置状态码为404
res.writeHead(404);
//设置响应浏览器内容为 'not found'
res.write('not found');
//结束并发送
res.end();
});
web 服务器下的响应对象(req)
req.url 获取请求的URL(资源名称)
req.method 获取请求的方法,默认是get
express框架
框架:是一整套解决方案,简化了已有的功能,添加了新的功能,专门用于项目开发。
1.express框架
基于Node.js平台,快速、开放、极简的WEB开发框架
属于第三方模块,需要自行下载
在 cmd 中 npm install express
(1)路由
路由用来处理特定的请求,包含三部分:请求的方法、请求的URL、回调函数、res响应对象
res 响应对象
res.send() 设置相应的内容并发送
res.redirect() 设置响应的重定向
res.sendFile() 设置响应的文件,需要使用绝对路径 __dirname
req 请求的对象
req.url 获取请求的URL
req.method 获取请求的方法
req.query 获取请求的URL中查询字符串传的参数,格式为对象
get传参:
网址?kw=dell req.query 格式为对象 {kw:‘dell’}
路由传参:
网址/keyword 需要加入形参 /:pname req.params 格式为对象 {pname:‘keyword’}
post传参:
URL中不可见 以流的方式,通过事件获取
req.body 获取对象的参数
//引入express模块
const express=require('express');
//创建WEB服务器
const app=express();
//设置端口
app.listen(8080);
//添加路由(get /search)
app.get('/search',(req,res)=>{
//响应文件
res.sendFile(__dirname+'/search.html');
});
//添加路由(get /mysearch),处理表单的提交,响应'搜索成功'
app.get('/mysearch',(req,res)=>{
//获取请求的URL,获取请求的方法
//console.log(req.url, req.method);
//将URL中查询字符串转为对象
console.log(req.query);
res.send('搜索成功');
});
//路由(get /login),响应文件
app.get('/login',(req,res)=>{
res.sendFile(__dirname+'/login.html');
});
//路由(get /mylogin)
app.get('/mylogin',(req,res)=>{
//获取get传递的参数
console.log(req.query);
res.send('登录成功');
});
//练习:添加路由用于查看包的详情介绍(get /package),响应'这是包的使用介绍'
//路由传参
app.get('/package/:pname',(req,res)=>{
//获取路由传参的值
console.log(req.params);
res.send('这是包的使用介绍');
});
2.路由器
用来管理路由,将同一个模块下所有的路由放入到一个路由器,最后在WEB服务器下使用路由器
//路由器模块,创建路由器对象
const r = express.Router();
//2.添加路由
//3.暴露路由器对象
module.exports = r;
//之后在WEB服务器中
app.use(要添加的前缀引入的路由器名称);
3.中间件
中间件用于拦截对服务器的请求
中间件分为应用级中间件、路由中间件、内置中间件、第三方中间件、错误处理中间件
(1)应用级中间件
就是一个函数,一旦拦截到自动调用这个函数
function fn(req,res){
next() //往后继续执行下一个中间件或者路由
}
app.use(拦截的URL,fn);
(2)路由中间件
路由器的使用
app.use('/user',路由器);
一旦拦截到,会到路由器下查找路由
(3)内置中间件
静态资源:html\css\js\图像\视频…
托管静态资源:客户端要请求静态资源,不需要通过路由去响应文件,而是自动到指定的目录查找
app.use(express.static('目录的路径'))
(4)第三方中间件
将post传递的参数转为对象
app.use(express.urlencoded({
extened:false //内部使用querystring
}));
//在路由中获取对象
req.body;
mysql模块
复习MySql基础用法
mysql -uroot<拖拽脚本文件
mysql.exe -h127.0.0.1 -P3306 -uroot -p
insert into 表名称 values(...);
update 表名称 set 列=值,列=值 where 条件;
delete from 表名称 where 条件;
1.mysql模块
是Node.js 下专门用于操作MySql数据库的模块
属于第三方模块,需先下载 npm install mysql;
createConnection() 创建普通连接
createPool() 创建连接池,产生多个连接,执行SQL命令的时候会从连接池中获取连接,执行完再把连接归还到连接池
query(SQL 命令,回调函数) 执行SQL命令,加入新的SQL命令。
SQL注入:是对数据库的攻击方式,破坏原来的SQL命令,加入新的SQL命令。
解决方法:对用户传递的值进行过滤
mysql模块使用占位符(?) 对传递的值进行过滤
2.接口
后端为前端提供的动态资源
RESTful接口:是一种接口的规范
(1)URL
http://127.0.0.1:8080/v1/emps
版本号 资源名称
http://127.0.0.1:8080/v1/emps/2
单个资源
http://127.0.0.1:8080/v1/users/login
特殊操作
(2)请求方法
对资源的操作方式
get 获取资源
detele 删除资源
put 修改资源
post 新建资源
(3)过滤数据
使用get传参
http://127.0.0.1:8080/v1/emps?pno=1&count=9
分页过滤 页码 每页数量
http://127.0.0.1:8080/v1/emps?s1=4000&s2=7000
工资过滤 工资开始 工资结束
(4)返回结果
格式json
包含状态码、消息、数据
登录的成功结果
{“code”:200,“msg”:“登录成功”}
获取一组员工结果
{“code”:200,“msg”:“获取成功”,“data”:[…]}
3.ApiPost
接口测试工具,可以生成接口使用文档
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
多个send被调用,说明响应多次,只能响应一次
test() 检测是否符合规则
repalce() 用于查找并替换
较完整的前后台数据传参的代码:
//引入express
const express=require('express');
//引入mysql模块
const mysql=require('mysql');
//创建连接池对象
const pool=mysql.createPool({
host:'127.0.0.1',
port:'3306',
user:'root',
password:'',
database:'tedu',
connectionLimit:15
});
//创建WEB服务器
const app=express();
//设置端口
app.listen(8080);
//使用中间件将post传递参数转为对象
app.use( express.urlencoded({
extended:false
}) );
//添加员工路由(post /v1/emps)
//接口地址:http://127.0.0.1:8080/v1/emps
//请求方法:post
app.post('/v1/emps',(req,res)=>{
//获取post传递的参数
console.log(req.body);
//执行SQL命令,将数据插入到数据表emp
pool.query('insert into emp set?',[req.body],(err,result)=>{
if(err) throw err;
console.log(result);
//获取了成功的结果,再去响应
res.send({code:200,msg:'员工添加成功'});
});
});
//按照编号查找员工(get /v1/emps/编号)
//接口地址:http://127.0.0.1:8080/v1/emps/2
//请求方法:get
app.get('/v1/emps/:eid',(req,res)=>{
//获取路由传参的值
console.log(req.params);
//执行SQL命令,查询编号对应的员工
pool.query('select * from emp where eid=?',[req.params.eid],(err,result)=>{
if(err) throw err;
console.log(result);
//查询的结果是数组,如果是空数组说明员工不存在,否则说明存在
if(result.length===0){
res.send({code:201,msg:'查无此人'});
}else{
res.send({code:200,msg:'查询成功',data:result});
}
});
});
//删除员工(delete /v1/emps/编号)
//接口地址:http://127.0.0.1:8080/v1/emps/2
//请求方法:delete
app.delete('/v1/emps/:eid',(req,res)=>{
//获取路由传参的值
console.log(req.params);
//执行SQL命令
pool.query('delete from emp where eid=?',[req.params.eid],(err,result)=>{
if(err) throw err;
console.log(result);
//结果是对象,如果对象下的affectedRows的值是0说明删除失败,否则删除成功
if(result.affectedRows===0){
res.send({code:201,msg:'删除失败'});
}else{
res.send({code:200,msg:'删除成功'});
}
});
});