持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
今天继续卷node!
Node.js中模块加载机制
1.模块查找规则-当模块拥有路径但没后缀时
require('./xxx.js')
require('./xxx')
require方法根据模块路径查找模块,如果是完整路径,直接引入模块
如果模块后缀省略,先找同名JS文件再找同名JS文件夹
如果找到了同名文件夹,找文件夹中的index.js文件
如果文件夹中没有index.js就会去当前文件夹中的package.js文件中查找main选项中的入口文件
2.模块查找规则-当模块没有路径且没有后缀时
require('xxx')
Node.js会假设它是系统模块
Node.js会去node_modules文件夹中
首先看是否有该名字的文件夹
再看是否有该名字的文件夹
如果是文件夹看里面是否有index.js
如果没有index,js查看该文件夹中的package.json中的mian选项确定模块入口文件
否则找不到会报错
Node.js全局对象global
在浏览器中全局对象是window,在Node中全局对象是global
Node中全局对象下有以下方法,可以在任何地方使用,global可以省略
console.log() 在控制台输出
setTimeout() 设置超时定时器
clearTimeout() 清除超时定时器
setInterval() 设置间歇定时器
clearInterval() 清除间歇定时器
例子
global.console.log('global对象下的console.log方法')
global.setTimeout(function(){
console.log('123');
},2000)
//控制台输出结果
global对象下的console.log方法
123
当去掉global时依旧可以输出,说明global就是全局变量
编辑
编辑
服务器端基础概念
网站的组成
网站应用程序主要分为两大部分:客户端和服务器端
客户端:在浏览器中运行的部分,就是用户看到并与之交互的界面程序。使用HTML、CSS、JS构建。
服务器端:在服务器中运行的部分,负责存储数据和处理应用逻辑
Node网站服务器
能够提供网站访问服务的机器就是网站服务器,它能够接受客户端的请求,能够对请求做出响应
IP地址
互联网中设备的唯一标识
IP是Internet Protocol Address的简写,代表互联网协议地址
域名
由于IP地址难以记忆,所以产生了域名的概念,所谓域名就是平时上网所使用的网址
www.xxxx.com => xxx.xxx.xxx.xxx/
虽然在地址栏输入的是网址,但是最终还是会将域名转换为ip才能访问到网站服务器地址
端口
端口是计算机与外界通讯交流的出口,用来区分服务器电脑中提供不同的服务
URL
统一资源定位符,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式,我们平时所说的网页地址指的即是URL
URL的组成
传输协议://服务器IP或域名:端口/资源所在位置标识
http:超文本传输协议,提供了一种发布和接收HTML页面的方法
开发过程中客户端和服务器说明
在开发阶段,客户端和服务器端使用同一台电脑,即开发人员电脑
本机的域名:localhost
本地IP:127.0.0.1
创建web服务器
//用于创建网站服务器的模块
const http = require('http');
//app对象就是网站服务器对象
const app =http.createServer();
//当客户端有请求来的时候
app.on('request',(req,res)=>{
res.end('<h2>hello user</h2>');
});
app.listen(3000);
console.log('服务器启动成功!');
编辑
HTTP协议
HTTP协议的概念
超文本传输协议(HyperText Transfer Protocol)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准
报文
在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式。
请求报文
请求方式(RequestMethod)
GET 请求数据
POST 发送数据
请求地址(Request URL)
app.on('request',(req,res) =>{
req.headers //获取请求报文
req.url //获取请求地址
req.method //获取请求方法
});
例子
//用于创建网站服务器的模块
const http = require('http');
//app对象就是网站服务器对象
const app =http.createServer();
//当客户端有请求来的时候
app.on('request',(req,res)=>{
//获取请求方式
//req.method
// console.log(req.method);
//获取请求地址
//req.url
// console.log(req.url);
//获取请求报文信息
//req.headers
console.log(req.headers['accept']);
if(req.url =='/index' ||req.url == '/'){
res.end('Weclome to 主页')
}else if(req.url == '/list'){
res.end('欢迎来到 list页面')
}else{
res.end('not found');
}
// if(req.method == 'POST'){
// res.end('post')
// }else if(req.method=="GET"){
// res.end('get')
// }
});
app.listen(3000);
console.log('服务器启动成功!');
响应报文
1.HTTP状态码
200 请求成功
404 请求的资源没被找到
500 服务器端错误
400 客户端请求语法错误
2.内容类型
text/html
text/css
application/javascript
image/jpeg
application/json
例子
//用于创建网站服务器的模块
const http = require('http');
//app对象就是网站服务器对象
const app =http.createServer();
//当客户端有请求来的时候
app.on('request',(req,res)=>{
//res.writeHead()可以设置状态码和响应头信息
res.writeHead(200,{
'content-type':'text/html;charset=utf8'
});
});
app.listen(3000);
console.log('服务器启动成功!');
HTTP请求与响应处理
请求参数
客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作。
GET请求参数
参数被放置在浏览器地址栏中,例如:http://localhost:3000/?name=zhangsan&age=20
const url =require('url')
console.log(req.url);
//第一个参数是要解析的url地址,第二个参数将查询参数解析成对象形式
let {query,pathname}=url.parse(req.url,true);
console.log(query.name);
console.log(query.age);
if(pathname =='/index' || pathname == '/'){
res.end('<h2>Weclome to 主页</h2>')
}else if(pathname == '/list'){
res.end('欢迎来到list页面')
}else{
res.end('not found');
}
POST请求参数
参数被放置在请求体中进行传输
获取POST参数需要使用data事件和end事件
使用querystring系统模块将参数转换为对象格式
例子
//用于创建网站服务器的模块
const http = require('http');
//用于处理url地址
const url = require('url')
//app对象就是网站服务器对象
const app =http.createServer();
//当客户端有请求来的时候
//处理请求参数模块
const querystring = require('querystring');
app.on('request',(req,res)=>{
//post参数是通过事件的方式接受的
//data 当请求参数传递的时候触发data事件
//end 当参数传递完成的时候触发end事件
let postParams ='';
req.on('data',params =>{
postParams += params;
});
req.on('end',() =>{
console.log(querystring.parse(postParams));
});
res.end('ok')
});
app.listen(3000);
console.log('服务器启动成功!')
静态资源
服务端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、image文件
例子:
这里展示的是后端代码,前端自行创建,前端统一存放在public
//用于创建网站服务器的模块
const http = require('http');
const url =require('url');
const fs =require('fs');
const path =require('path');
const mime = require('mime');
const app =http.createServer();
app.on('request',(req,res)=>{
//获取用户的请求路径
let pathname=url.parse(req.url).pathname;
pathname=pathname == '/'?'/default.html' :pathname;
//拼接路径的时候最好使用上系统模块path下面的join方法
let realPath=path.join(__dirname,'public'+pathname)
//mime的getType可以根据路径返回资源的类型
let type=mime.getType(realPath)
//读取文件
fs.readFile(realPath,(error,result)=>{
//如果文件读取失败
if(error!=null){
res.writeHead(404,{
'content-type':'text/html;charset=utf8'
})
res.end('文件读取失败');
return;//终止
}
res.writeHead(200,{
'content-type':type
})
res.end(result)
})
});
app.listen(3000);
console.log('服务器启动成功!');
动态资源
相同的请求地址不同的响应资源
路由
路由是指根据客户端请求地址与服务器程序代码的对应关系。简单的说,就是请求什么就响应什么。
编辑
例子
//1.引入系统模块http
//2.创建网站服务器
//3.为网站服务器对象添加请求事件
//4.实现路由
// 1.获取客户端的请求方式
// 2.获取客户端的请求地址
const http = require('http')
const url = require('url')
const app =http.createServer();
app.on('request',(req,res)=>{
//获取请求方式
const method = req.method.toLowerCase();//toLowerCase把方法转换成小写
//获取请求地址
const pathname=url.parse(req.url).pathname;
res.writeHead(200,{
'content-type':'text/html;charset=utf8'
})
if(method == 'get'){
if(pathname =='/'||pathname=='/index'){
res.end('欢迎铁汁!!!')
}else if(pathname == '/list'){
res.end('欢迎来到列表页')
}else {
res.end('不存在呀!')
}
}else if(method == 'post'){
}
})
app.listen(3000);
console.log('服务器启动了...');
同步与异步
同步API:只有当前API执行完成后,才能继续执行下一个API
异步API:当前API的执行不会阻塞后续代码的执行
异步的例子:
console.log('前');
setTimeout(()=>{
console.log('最后');
},2000)
console.log('后');
同步和异步的区别1(获取返回值)
同步API可以从返回值中拿到API执行的结果,但是异步API是不可以的
异步API不会阻塞后续代码执行,这里的例子虽然没有写return,但是默认会返回undefined
function getMsg(){
setTimeout(()=>{
return {
msg:'hello node.js'
}
},2000)
}
const msg =getMsg();
console.log(msg);
//结果是undefined
异步API的返回值要怎么拿?
答案就是回调函数
回调函数
自己定义函数让别人去调用
回调函数的简单例子
function getData(call){
call('123') //这里的123是实参
}
getData((n)=>{//n表示形参
console.log(n);
console.log('回调函数被调用了');
})
同步和异步的区别2(代码执行顺序)
同步API从上到下依次执行,前面代码会阻塞后面代码的执行
例子:
for (var i=0;i<100000;i++){
console.log(i);
}
console.log('for后面的代码');
异步API不会等待API执行完成后再向下执行代码
例子
console.log('代码开始执行');
setTimeout(()=>{
console.log('2s');
},2000)
setTimeout(()=>{
console.log('0');
},0)
console.log('代码执行结束');
/*
代码开始执行
代码执行结束
0
2s
*/
node.js中首先会执行同步代码,再执行异步代码,最后执行回调函数
编辑
Node.js中的异步API
如果异步API后面代码执行依赖当前异步API的执行结果,但实际上后续代码在执行的时候异步API还没有返回结果,怎么解决呢?
最直接的方式就是将后续代码放进当前异步代码的回调里,但这样会造成回调地狱,怎么解决呢?
const fs= require('fs');
//回调嵌套回调,这就是回调地狱
fs.readFile('./1.txt','utf8',(err,result1)=>{
console.log(result1);
fs.readFile('./2.txt','utf8',(err,result2)=>{
console.log(result2);
fs.readFile('./3.txt','utf8',(err,result3)=>{
console.log(result3);
})
})
});
//结果依次输出1,2,3
Promise
Promise出现是为了解决Node.js异步编程中回调地狱的问题
Promise是个构造函数
例子:
const fs = require('fs');
let promise =new Promise((resolve,reject)=>{
fs.readFile('./11.txt','utf8',(err,result)=>{
if(err != null){
reject(err)
}else {
resolve(result)
}
});
});
//promise提供了链式编程的写法
promise.then((result)=>{
console.log(result);
//结果输出1
}).catch((err)=>{
console.log(err);
//输出错误信息
})
Promise解决回调地狱
const fs = require('fs');
function p1(){
return new Promise((res,rej)=>{
fs.readFile('./1.txt','utf8',(err,result)=>{
res(result);
})
})
}
function p2(){
return new Promise((res,rej)=>{
fs.readFile('./2.txt','utf8',(err,result)=>{
res(result);
})
})
}
function p3(){
return new Promise((res,rej)=>{
fs.readFile('./3.txt','utf8',(err,result)=>{
res(result);
})
})
}
p1().then((r1)=>{
console.log(r1);
return p2();
}).then((r2)=>{
console.log(r2);
return p3();
}).then((r3)=>{
console.log(r3);
})
异步函数
异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了
async关键字
1.普通函数定义前加async关键字 普通函数变成异步函数
2.异步函数默认返回promise对象
3.在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了resolve方法
4.在异步函数内部使用throw关键字抛出程序异常
5.调用异步函数链式调用then方法获取异步函数执行结果
6.调用异步函数链式调用catch方法获取异步函数执行的错误信息
await关键字
1.await关键字只能出现在异步函数中
2.await promise await后面只能写promise对象,写其他类型的API是不可以的
3.await关键字可以暂停异步函数向下执行,直到promise返回结果
例子:
//1.在普通函数定义的前面加入async关键字 普通函数就变成异步函数
//2.异步函数默认的返回值是promise对象
//3.在异步函数内部使用throw关键字进行错误抛出
//await关键字
//1.它只能出现在异步函数中
//2.await promise 可以暂停异步函数的执行 等待promise对象返回结果后再向下执行
// async function fn(){
// //throw代替reject了输出错误信息
// throw '搞错咯!';//throw执行后后面的代码就不执行
// return 123;
// }
// //console.log(fn());//异步函数默认返回值Promise { undefined }
// fn().then((data)=>{
// console.log(data);
// }).catch((err)=>{
// console.log(err);
// })
async function p1(){
return 'p1';
}
async function p2(){
return 'p2';
}
async function p3(){
return 'p3';
}
async function run(){
//await 可以让下面的函数依次执行
let r1=await p1()
let r2=await p2()
let r3=await p3()
console.log(r1);
console.log(r2);
console.log(r3);
}
run()
用异步函数来解决之前的回调地狱
const fs =require('fs');
const promisify=require('util').promisify;//可以对现有的node.js当中的异步API进行包装,让它返回promise对象,然后才能支持异步函数的写法
const readFile=promisify(fs.readFile);
async function run () {
let r1 = await readFile('./1.txt','utf8')
let r2 = await readFile('./2.txt','utf8')
let r3 = await readFile('./3.txt','utf8')
console.log(r1);
console.log(r2);
console.log(r3);
}
run()
\