nodejs-day02
1.package.json文件
package.json文件就是一个项目信息描述的文件,它用来记录项目的一些基本信息,还有第三方模块的记录信息。
如果我们需要自己创建一个全新的项目,也需要创建项目的描述文件package.json
在项目文件夹下打开终端,执行命令 npm init -y 就可以生成一个 package.json 文件,文件内容描述如下:
{
"name": "demo", //项目名称
"version": "1.0.0", //项目版本
"description": "", //项目描述
"main": "index.js", //项目的入口文件
"scripts": { //项目的脚本
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [], //关键字
"author": "", //作者
"license": "ISC" //开发协议
"dependencies": { //项目的运行依赖 :代表项目运行的时候需要使用到的第三方包。
"jquery": "^3.4.1"
},
"devDependencies": { //项目的开发依赖:项目开发阶段需要用到的第三方包
"less":"2.5"
}
}
注意:
package-lock.json文件 会记录下载第三方包的源地址。
如果项目出了问题,第三方包需要重新下载,我们使用npm install命令下载的时候就会直接找到package-lock.json文件中记录的地址直接下载。
2. 服务器
服务器:能够提供web访问服务的机器就是网站服务器,能接收浏览器端的请求,能对请求做出响应。
客户端:就是使用服务的电脑,或者浏览器
ip地址:就是一串数字,使用这一串数字来区分 internet 上的每一台电脑 本机有一个特定的 IP 地址是127.0.0.1;每一个电脑使用的ip地址是不能重复的,就像身份证编号一样,是不能重复的。
ip地址是由4个0~255的数字组成,比如说:192.168.14.102
域名:ip地址难以记忆,有含义的域名是比较好记忆的,我们访问网站的时候不会使用ip地址去进行访问
而是使用域名。域名和ip地址之间是有对应关系的。如果将本机作为服务器,本机有一个特定的域名是 localhost
端口:指的是服务器上提供服务的地方,在一台电脑中,端口范围0~65535。
每一个服务需要占用一个端口,我们可以根据不同的端口号去使用服务器不同的服务。
0~1024端口是系统使用的端口,最好不要去使用。
url地址:http://localhost:8080/#/login
http:// :协议 我们访问网站的时候是要遵循http协议的 。
localhost :访问的网站的域名,localhost代表的是我们自己的电脑。
:8080 :网站使用了8080端口提供服务
http://www.itcast.cn:8080/first/index.html?uname=jack&pwd=123#one
/first/index.html :访问服务器中的文件的路径
?uname=jack&pwd=123 : url地址参数
#one :锚链接
3. 服务器与客户端间的请求与响应
创建web服务器
在NodeJs中不需要安装额外的软件充当网站服务器,NodeJs中提供的HTTP模块即可创建web服务器。
// 引用系统模块
const http = require('http');
// 创建web服务器
const server = http.createServer();
// 当客户端发送请求的时候
server.on('request', (req, res) => {
// 设置响应头
res.writeHead(200, {
'Content‐Type': 'text/html;charset=utf8'
});
// 设置响应体
res.write('<h1>哈哈哈</h1>');
// 结束请求
res.end();
});
// 监听3000端口
server.listen(3000, error => {
if (!error) {
console.log('服务器已启动,监听3000端口,请访问 localhost:3000')
}
});
浏览器向服务器发送的叫做请求( request )
服务器向浏览器返回的结果叫做响应( response )
在发送请求的时候,最重要的就是要知道请求的地址是什么,还有请求的方式是什么
请求分为get,post两种方式,参数被放置在地址栏中,格式为:name=zhangsan&age=20
get方式一般用来获取数据
// 处理get参数
const url = require('url');
let { query } = url.parse(req.url, true);
post方式一般用来发送数据
// 处理post参数
// 由于post传递的参数数据量比较大,在网络中并不是一次性传递完成的,而是分成了多次传递
// 所以在接收的时候也需要分为多次接收
// 在NodeJs中接收post参数需要使用事件完成
const querystring = require('querystring');
let formData = '';
req.on('data', chunk => formData += chunk);
req.on('end', () => console.log(querystring.parse(formData)));
//on方法就是事件监听,监听的是request事件,request事件会在客户端访问服务器的时候发生
app.on("request",function(req,res){
//req这个参数表示的是request请求对象
//res这个参数表示的是response响应对象
req.method;// 获取浏览器向服务器发送的请求类型 (get/post);
req.url;// 获取浏览器访问服务器的对应地址 。
req.headers;// 获取浏览器发送给服务器的请求信息
res.end("hello world");
})
req.method;// 获取浏览器向服务器发送的请求类型 (get/post);
req.url;// 获取浏览器访问服务器的对应地址 。
req.headers;// 获取浏览器发送给服务器的请求信息
使用系统模块url来处理请求路径
导入url模块
const url = require(“url”);
使用url模块提供的parse方法来解析请求中的url地址,parse方法的第二个参数意思是 是否需要将参数解析为对象
const urlObj = url.parse( req.url, true );
使用解构赋值获取到urlObj对象中的属性
//const query = urlObj.query
//const pathname = urlObj.pathname
const { query, pathname } = urlObj
前端发送post请求,后台如何处理
前端需要使用form表单来发送post请求,如果表单中的表单元素有name属性,那么这个表单元素会作为post请求的参数数据进行发送
<form action="http://localhost:3000/post" method="post">
<!-- zhanghao=jack&pass=123456 -->
账号:<input type="text" name="zhanghao">
密码:<input type="password" name="pass">
<input type="submit">
</form>
后台要如何接收post请求的数据(请求参数),并进行处理
//需要使用querystring模块进行参数数据转换为对象的处理
const querystring = require('querystring');
//使用url模块进行解析
const urlObj = url.parse( req.url , true );
//解构赋值
const { query,pathname } = urlObj;
if(pathname == '/' || pathname=='/index'){
res.writeHead(200,{
"content-type":"text/html;charset=utf8"
})
res.end("<h1>下午好,"+query.uname+"</h1>");
}else if(pathname=='/post' && req.method == 'post'){
var res = "";
//要接收前端发送的参数数据,需要使用两个事件,data事件,end事件
req.on("data",function(val){
res+=val;
})
//如果触发了end事件,说明数据已经接收完毕了
req.on("end",function(){
const obj = querystring.parse( res );
console.log(obj);
res.end("处理post请求完毕");
})
}else{
res.writeHead(404,{
"content-type":"text/html;charset=utf8"
})
res.end("<h1>您请求的资源不存在</h1>");
}
4. 路由
路由是指URL地址与程序的映射关系,更改URL地址可以改变程序的执行结果。简单说就是请求什么响应什么
const http = require('http');
const url = require('url');
const finalhandler = require('finalhandler')
// 创建网站服务器
const server = http.createServer();
// 当客户端发来请求的时候
server.on('request', (req, res) => {
// 处理404方法
var done = finalhandler(req, res);
// 获取客户端的请求路径
let { pathname } = url.parse(req.url);
// 对请求路径进行判断 不同的路径地址响应不同的内容
if (pathname == '/' || pathname == '/index') {
res.end('index');
}else if (pathname == '/list') {
res.end('list');
}else if (pathname == '/about') {
res.end('about');
}else {
// 如果以上路由都没有匹配上, 则进行404处理
done();
}
});
// 程序监听端口
server.listen(3000);
5. 静态资源访问
1.获取客户端要访问的那个地址中的文件的路径
//假如url获取的是 /index.html /list.html /1.jpg
let url = url.parse(req.url).pathname;
2.将获取的url路径进行拼接变成服务器的绝对路径
let filepath = path.join(__dirname,“public”,url);
3.可以使用fs模块去读取文件
在读取文件之前应该要先获取文件的mime类型。
// 在终端中执行命令 npm i mime 下载 mime 模块
const mime = require("mime");
const type = mime.getType(filepath);
fs.readFile(filepath,function(err ,doc){
if(!err){
res.writeHead(500,{
"content-type":"text/html";
})
res.end("<h1 >服务器读取网页失败</h1>");
return;
}
//需要设置mime类型
res.writeHead(200,{
"content-type": type;
})
//将读取到的文件源代码返回给客户端
res.end(doc);
})
6. nodejs工作原理
同步异步
同步或者异步指的是服务器端处理请求的方式。
同步方式
生动形象的比喻
当你来到餐馆,一个指定的服务员被分配给你服务,当你点完餐时,服务员将订单送到厨房并在厨房等待厨师制作菜肴,
当厨师将菜肴烹饪完成后,将菜肴送到你的面前,服务完成.此时这个服务员才能服务另外的客人.同步模式就是一个服 务员同时只能服务于一个客人.
你可以想象,餐馆就是服务器,服务员就是线程,客户端向服务器端发送的请求就是吃饭的客人.当客户端向服务器发送
请求时,服务器端需要为请求分配一个线程处理该请求,在请求的过程中可能会去读取出数据或者文件,这是一个需要
时间的操作,就好像厨师炒菜需要时间一样,此时线程只是等待操作的完成.如果此时有新的请求来了,服务器需要分配
一个新的线程为该请求服务,必须要知道的是,分配一个线程大约消耗服务器端2MB的内存.想象一下,如果同一时间,
服务器接收到大量的请求,服务器可能会消耗掉所有内存提供服务,同一时间再有请求来的时候必须等待,等待有空闲
的线程才能为该请求服务,如果不想让该请求等待,服务器端需要增加更多的硬件(内存)设备.这种同步方式显然没有 有效的利用现有的资源.
异步方式
生动形象的比喻
当你来到餐馆,在点餐后服务员将你的订单送到厨房,此时服务员没有在厨房等待厨师烹饪菜肴,而是去服务了其他客人,当厨房你的菜肴烹饪好以后,服务员再将菜肴送到你的面前.异步模式就是一个服务员同时可以服务多个客人.
Node就是基于异步的应用程序.在Node中,用一个线程处理所有的请求.当客户端向服务器端发送请求时,服务器端的一个线程处理该请求,在请求的过程中如果去查询数据库或者读取文件,该线程不会等待查询的结果,转而去处理其他请求,Node程序会监控数据库的查询结果,如果查询完成,该线程会重新回来处理该请求.Node擅长处理高并发的网络请求或频繁的数据取读取型应用程序,不会带来硬件成本的增加.这种异步的模式显然有效的利用了现有的资源.
代码执行顺序
同步方式
代码一行一行执行,下一行的代码必须等待上一行代码执行完成以后才能执行.
异步方式
他可以同时运行多句代码,效率会更高。
Promise
Promise出现的目的是解决Node.js异步编程中回调地狱的问题
Promise本身是一个构造函数,要使用promise解决回调地狱的问题 需要使用new运算符创建Promise构造函数的实例对象
在创建对象的时候需要传入一个匿名函数,匿名函数中有两个参数 resolve,reject
这个方式其实还可以优化
异步函数
异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了
async 关键字
作用:
- 普通函数定义前加async关键字 普通函数变成异步函数
- 异步函数默认返回promise对象
- 在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了promise中的resolve方法
- 在异步函数内部使用throw关键字抛出程序异常
- 调用异步函数再链式调用then方法获取异步函数执行结果
- 调用异步函数再链式调用catch方法获取异步函数执行的错误信息
await关键字
- await关键字只能出现在异步函数中
- await promise await后面只能写promise对象 写其他类型的API是不不可以的
- await关键字可是暂停异步函数向下执行直到promise返回结果
//使用 promisify 改造函数
//promisify的作用就是将函数转换成promise对象
//const promisify = require("util").promisify;
//const fn = promisify(需要转换的函数名);
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (true) {
resolve({name: '张三'})
}else {
reject('失败了')
}
}, 2000);
});
promise.then(result => console.log(result); // {name: '张三'})
.catch(error => console.log(error); // 失败了)
const fn = async () => {};
async function fn () {}
// 改造现有异步函数api 让其返回promise对象 从而支持异步函数语法
const promisify = require('util').promisify;
// 调用promisify方法改造现有异步API 让其返回promise对象
const readFile = promisify(fs.readFile);