已经有很长一段时间没有写过文章了,这次也算又重新回归社区。亲爱的掘友们,我回来了!哈哈, 废话少说,今天来聊一聊我们前端最容易入手的后端语言
node.js
。其实相对node.js
是早有耳闻,只是迫于没有系统学习,之前总是一知半解,甚至不知道到底该怎么用?用在哪里?最近一段时间,在大神的带领下,现在算是入了一点门儿,故写下此篇笔记,如文中有错,还望各位路过的大神帮忙指点一二。小女子在这厢有礼了!
一、简介
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。
-nodejs官网的介绍
“简单的说 Node.js 就是运行在服务端的 JavaScript。”
1. 用途分析:
- 中间层(安全,性能高,降低主服务器的复杂度):
node.js可作为在客户端和服务端的中间层,用于搭界两端,避免主服务器直接暴露在外,也保证了主服务器的安全性;并且中间层可做一系列的定制,提高访问速度,降低主服务器的复杂度等等。
- 小型服务
node.js可作为小型服务搭建,使用更便捷
- 工具类开发
node.js计算速度较快,可用于开发各种打包,转编译等工具,例如:webpack,gulp,babel...
2. 前端学习的优势:
- 便于前端入手,语法和前台js一样
- 性能高(使用chrome 的 v8引擎)
- 更加利于和前端代码整合
3. 快速上手:
-
下载地址:
中文:nodejs.cn/
-
命令行使用:
可查看下载版本:
node -v
输入node
回车后,可在命令行直接进行计算,如下:
先输入node
,告诉命令行你要在node 环境下执行任务:
node
查看当前已经下载的node
版本:
nvm list
修改当前使用的node
版本(从下载列表中选择你需要的版本):
nvm use v14.0.0
最后查看当前版本:
node -v
下载其他版本的node:
nvm install v16.0.0
下载最稳定版本node:
n stable
拓展:
node 下载完成后会自带一个npm ,用来下载各类的库,工具等等。
二、系统自带模块方法
来介绍几个比较常用系统自带的系统模块,直接引用后即可使用。
介绍模块之前,我们先来一个小测试,用来测试我们的js文件是否可以正常执行:
console.log("node测试")
控制台输出:
测试完成,控制台可正常显示,可进行下一步操作。
接下来正式来介绍我们的模块。
1. http模块:
这个模块是
node
里面用的最多,最基础的模块,用于启动我们的服务,监听相应的端口。
//直接引入即可
const http = require("http");
// 启动服务,并监听相应端口
http.createServer((req,res)=>{
console.log("请求来了");
}).listen(8000) // 8000 监听的端口号
接下来,去控制台执行此文件:
然后在浏览器手动输入我们监听的端口号:
localhost:8000
返回控制台,查看监听结果:
此时我们已经可以成功启动服务器,并且监听相应的端口
在
http.createServer
中有两个参数request
和response
,后面我们会多次用到,现在来看一下,这两个里面都可以返回写什么?
- request:简写:req:
const http = require("http");
http.createServer((req,res)=>{
console.log(req);
}).listen(8000)
里面包含众多参数,以至于我无法用截图来展示。
先介绍两个req.url
,ruq.method
const http = require("http");
http.createServer((req,res)=>{
// req.url 拿到当前端口后面的path
// req.method 拿到当前请求的方式
console.log(req.url,req.method)
}).listen(8000)
浏览器输入:
http://localhost:8000/root/abc
查看控制台输出:
- response : res
先来看看里面都有什么
const http = require("http");
http.createServer((req,res)=>{
console.log(res)
}).listen(8000)
如同res一样,内容数不胜数
我们今天先讲其中的一个方法,
res.write()
,往浏览器写入内容:
const http = require("http");
http.createServer((req,res)=>{
res.write("abc") // 写入浏览器,只限字符串
console.log("请求来了");
res.end() //结束,浏览器才会正常执行
console.log(res)
}).listen(8000)
接下来,去控制台执行此文件,在浏览器手动输入如下地址:
http://localhost:8000
查看浏览器展示效果,已经写入浏览器:
2. path模块:
用于解析路径,拿到对应的文件名,扩展名以及路径等。
path.dirname()
获取目录名称
const http = require("http");
const path = require("path");
http.createServer((req,res)=>{
console.log(path.dirname(req.url));
}).listen(8000)
先在控制台启动服务器,运行上面的js
代码,然后浏览器输入如下地址:
http://localhost:8000/root/build/a.text
返回查看控制台执行:
path.basename()
获取文件名
const http = require("http");
const path = require('path');
http.createServer((req,res)=>{
console.log(path.basename(req.url))
}).listen(8000)
先控制台启动服务器,运行上面的js代码,然后浏览器输入如下地址:
http://localhost:8000/root/build/a.text
返回查看控制台执行:
path.extname()
获取文件扩展名
const http = require("http");
const path = require("path");
http.createServer((req,res)=>{
console.log(path.extname(req.url))
}).listen(8000)
先控制台启动服务器,运行上面的js
代码,然后浏览器输入如下地址:
http://localhost:8000/root/build/a.text
返回查看控制台执行:
path.resolve()
用于解析一系列混乱的路径,拿到最终的路径
const http = require("http");
const path = require("path");
http.createServer((req,res)=>{
console.log(path.resolve("__dirname","build")) //拿到绝对路径
console.log(path.resolve('root/a/b','../c','build',"..","strict")) //解析路径
}).listen(8000)
控制台启动服务器,运行上面的js代码,浏览器输入如下地址
http://localhost:8000
查看控制台执行
3. url
模块
用于返回解析完成的
url
路径
const url = require("url");
let str="http://localhost:8080/root/a/b?a=1&b=8";
console.log(url.parse(str,true))
控制台启动服务器,运行上面的js代码,查看解析结果
第二个参数true
可以将url
中的query
参数转换成json
格式
4. node自带的querystring
模块已经废弃
,进行参数格式转换
可以下载替代品,使用方法也是一样的:
cnpm i querystringify
querystring.parse()
将url的参数解析成json
const querystring = require("querystring");
console.log(querystring.parse("a=12&b=5&c=2"))
控制台启动服务器,运行上面的js代码,查看解析结果
querystring.stringify()
将json
反解析成以&
连接的格式
const querystring= require("querystring");
let json={"a":5,"b":8};
console.log(querystring.stringify(json))
控制台启动服务器,运行上面的js
代码,查看解析结果
5. assert
模块
assert
断言,检测异常代码,当代码块中遇到assert
时会进行检测,如果条件正确,不做任何处理,如若错误,直接抛出异常,不再向下执行。
const assert = require ('assert');
assert(5<3,'出错了')
console.log("123")
控制台启动服务器,运行上面的js代码,查看解析结果
assert.deepEqual()
深度相等判断,只比对值,不比对类型,相等与运算中的"=="
const assert = require("assert");
const boo = 55;
assert.deepEqual(boo,"55","出错了")
控制台启动服务器,运行上面的js代码,查看解析结果
assert.deepStrictEqual()
深度严格相等判断,相等与运算中的"==="
const assert = require("assert");
const boo ={a:1,b:2,c:["555"]}
assert.deepStrictEqual(boo,{a:1,b:2,c:[555]},"出错了")
方法可用于函数调用,传参时的判断,或者大型计算得出结果判断,避免因为参数错误,导致程序出错。
6. fs
模块,可用于读取文件,写入文件,将资源文件拷贝至目标文件等。
fs.readFile()
读取文件
- 第一参数为文件位置及文件名
- 第二个参数为回调函数
读取本地data
文件夹的2.text
本地的2.text文件
const fs = require("fs");
// readFile()方法有两个参数,一个是错误的err,一个是正确的data
fs.readFile("./data/2.txt",(err,data)=>{
if(err){
console.log("失败了")
}else{
console.log("成功了",data.toString())
}
})
控制台启动服务器,运行上面的js代码,查看解析结果
下面来试试读取图片,并写入浏览器
html
部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<p>我有图片</p>
<img src="./data/timg.jpeg">
</body>
</html>
js
部分:
const http = require("http");
const fs = require('fs');
let server=http.createServer((req,res)=>{
console.log(req.url)
fs.readFile(`./${req.url}`,(err,buffer)=>{
//处理请求,成功or失败
if(err){
res.writeHead(404);
res.write('Not Found6',err);
console.log(err)
res.end();
}else{
res.write(buffer);
res.end();
}
})
});
server.listen(8000);
控制台启动服务器,运行上面的
js
代码,浏览器输入如下地址:
http://localhost:8000/img.html
查看解析结果
fs.writeFile()
写入文件
- 第一个参数是添加的位置以及文件名
- 第二个参数是写入的内容
- 第三个参数是回调函数
写入本地
data
文件夹的1.text
const fs = require("fs");
fs.writeFile('./data/1.txt',`abc`,(err)=>{
if(err){
console.log("失败了")
}else{
console.log("成功了")
}
})
控制台启动服务器,运行上面的
js
代码,查看解析结果
查看我的本地文件:
实现一个拷贝,从2.text
拷贝内容到1.text
const fs = require("fs");
( (source,target)=>{
let result = fs.readFileSync(source,'utf8');
fs.writeFileSync(target,result)
})('./data/2.txt','./data/1.txt');
控制台启动服务器,运行上面的js
代码,查看两个文件
7. 流操作文件: createReadStream
createWriteStream
在上面我们介绍了如何读取和写入资源,就像下面这样:
fs.readFile('www/1.html'),(err,buffer)=>{
res.write(buffer);
});
它的工作模式是:先把文件读取到内存中,然后再扔过去,这样的操作有两个明显的缺点
- 占用内存
- 资源使用不均匀
createReadStream
createWriteStream
推荐使用流操作,一边读,一边写,资源均衡
示例:
1.text:
12121212121212121
1212121
121212
121212
121212
121
stream.js:
const fs = require('fs');
// 读取流
let rs = fs.createReadStream('./1.text');
//写入流
let ws = fs.createWriteStream('./2.text');
rs.pipe(ws);
ws.on('error', (err) => {
console.log(err);
});
ws.on('finish', () => {
console.log('写入成功');
});
在命令行执行:
node stream.js
在文件夹会冒出一个2.text
的文件,文件内容同1.text
,成功!
压缩
尝试读取1.text,然后进行压缩,然后输出2.text.gz 文件
const fs = require('fs');
const zlib = require('zlib');
// 读取流
let rs = fs.createReadStream('1.text');
let gz = zlib.createGzip();
//写入流
let ws = fs.createWriteStream('2.text.gz');
rs.pipe(gz).pipe(ws);
ws.on('error', (err) => {
console.log(err);
});
ws.on('finish', () => {
console.log('写入成功');
});
压缩成功!
将读取内容写入服务器
const http = require('http');
const zlib = require('zlib');
const url = require('url');
const fs = require('fs');
http.createServer((req, res) => {
let { pathname } = url.parse(req.url, true);
let rs = fs.createReadStream('www' + pathname);
rs.on('error', err => {
res.writeHead(404);
res.write("not found");
res.end();
})
res.setHeader('content-encoding', 'gzip');
let gz=zlib.createGzip()
rs.pipe(gz).pipe(res);
}).listen('8080');
写入服务器成功,且已经执行过压缩
代码优化,处理边界错误:
const http = require('http');
const zlib = require('zlib');
const url = require('url');
const fs = require('fs');
http.createServer((req, res) => {
let { pathname } = url.parse(req.url, true);
let filepath ='www' + pathname
// 先检查文件状态,在执行操作
fs.stat(filepath, (err,stat) => {
if (err) {
res.writeHead(404);
res.write("not found");
res.end();
} else {
let rs = fs.createReadStream(filepath);
rs.on('error', err => { });
res.setHeader('content-encoding', 'gzip');
let gz=zlib.createGzip()
rs.pipe(gz).pipe(res);
}
})
}).listen('8080');
此时,当输入一个不存在的地址时,也不会导致浏览器崩溃
8. 推荐一个启动器 forever
使我们应用一直在运行,即使是崩了也会自动重启,关闭网页,关闭终端,甚至是关机也不会受影响。
下载:
npm i forever -g
启动(告诉forever 你需要启动的文件):
forever start xxx.js
查看当前启动的服务列表:
forever list
手动重启服务:
forever restart xxx.js
停止指定服务:
forever stop xxx.js
停止所有forever
开启的服务(谨慎使用
):
forever stopall
三、实现一个get
和一个post
请求的登录 + 处理请求
1. 来实现一个get
请求的登录
html
部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8000/abc" method="get">
用户名 <input type="tet" name="usernam"><br/>
密码 <input type="password" name="pass"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
js
部分:
const http = require("http");
const querystring = require("querystring");
http.createServer((req,res)=>{
console.log(req.url);
let [url,query] = req.url.split("?");
console.log(url,query);
let get =querystring.parse(query);
console.log(get)
}).listen(8000)
控制台启动服务器,运行上面的js代码,查看前台页面,并将用户名密码提交:
查看控制台数据:
2. 来实现一个post
请求的登录
html
部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8000/abc" method="post">
用户名: <input type="text" name="username"><br/>
密码: <input type="password" name="pass" ><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
js
部分:
const http = require("http");
const querystring = require("querystring");
//post 因为数据比较大,数据会分好几次发送回来,接收的时候也是分段来接收,此处用数组来保存
let arr=[];
let server=http.createServer((req,res)=>{
// 接收到数据
req.on('data',buffer=>{
arr.push(buffer)
});
// 接收完毕
req.on('end',()=>{
let buffer = Buffer.concat(arr);
let post = querystring.parse(buffer.toString())
console.log(post)
})
});
server.listen(8000);
控制台启动服务器,运行上面的js代码,查看前台页面,并将用户名密码提交:
查看控制台返回数据:
3. 整合get
和 post
请求
html
部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="http://localhost:8000/abc" method="post">
用户名 <input type="tet" name="usernam"><br/>
密码 <input type="password" name="pass"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
js
部分:
const http = require("http");
const querystring = require("querystring");
http.createServer((req,res)=>{
// console.log(req.method)
if(req.method==='GET'){
// console.log(req.url)
let [url,query] = req.url.split("?");
let get = querystring.parse(query);
console.log(url,get)
}else{
let arr=[];
req.on("data",(buffer)=>{
// console.log(buffer)
arr.push(buffer)
});
req.on("end",()=>{
let buffer= Buffer.concat(arr);
let post = querystring.parse(buffer.toString());
url= req.url
console.log(url,post)
})
}
}).listen(8000)
控制台启动服务器,运行上面的js代码,查看前台页面,并将用户名密码提交:
查看控制台返回数据:
四、实现一个简单的登录&注册的前后端数据交互
现在我们整合上面所提到的方法,来实现一个前后端页面交互,包括接口调用以及页面正常交互动作。
html
部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./jquery-1.7.2.js"></script>
</head>
<body>
用户名 <input type="tet" id="user"><br/>
密码 <input type="password" id="pass"><br/>
<input type="button" id="btn1" value="注册">
<input type="button" id="btn2" value="登录">
<script>
$(function(){
$("#btn1").click(()=>{
$.ajax({
url:"/reg",
dataType:"json",
data:{
username:$("#user").val(),
password:$("#pass").val()
}
}).then((json)=>{
if(json.error){
alert(json.msg)
}else{
alert("注册成功")
}
},error=>{
alert("注册失败,请刷新重试")
})
});
$("#btn2").click(()=>{
$.ajax({
url:"/login",
dataType:"json",
data:{
username:$("#user").val(),
password:$("#pass").val()
}
}).then((json)=>{
if(json.error){
alert(json.msg)
}else{
alert("登录成功")
}
},error=>{
alert("登录失败,请刷新重试")
})
});
})
</script>
</body>
</html>
展现效果:
js
部分:
const http = require('http');
const querystring = require('querystring');
const url = require('url');
const fs = require('fs');
let users ={}
http.createServer((req,res)=>{
let path ='',get={},post={};
console.log(req.method)
if(req.method === 'GET'){
let {pathname,query}= url.parse(req.url,true)
path = pathname;
get = query;
console.log()
complete()
}else{
let arr =[];
path = req.url
req.on("data",buffer=>[
arr.push(buffer)
]);
req.on('end',()=>{
let buffer = Buffer.concat(arr)
post = querystring.parse(buffer.toString())
// complete()
})
}
function complete(){
// console.log(path,get,post)
// 注册
if(path =='/reg'){
let {username,password} = get;
if(users[username]){
res.write(JSON.stringify({error:1,msg:'此用户名已存在'}));
res.end()
}else{
users[username] = password;
res.write(JSON.stringify({error:0,msg:'注册成功'}));
res.end()
}
}else if(path == '/login'){
// 登录
let {username,password} = get;
if(users[username]){
if(users[username] !== password){
res.write(JSON.stringify({error:1,msg:'密码错误'}));
res.end()
}else{
res.write(JSON.stringify({error:0,msg:'登录成功'}));
res.end()
}
}else{
res.write(JSON.stringify({error:1,msg:'此用户不存在'}));
res.end()
}
}else{
fs.readFile(`./${path}`,(err,buffer)=>{
if(err){
res.writeHead(404);
res.write('Not Found');
res.end()
}else{
res.write(buffer);
res.end()
}
})
}
}
}).listen('8000')
控制台启动服务器,运行上面的js代码,查看前台页面,并将用户名密码提交:
到此我们就把node
的一些常用模块进行了了解,只学习原生模块是远远不够的,后面我去准备一下node
的框架,敬请期待吧!