NodeJs
Node.js简介
- Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时
- Node.js没有web容器
- Node.js的特点(面试必考)
- 单线程
- 非阻塞I/O
- 事件驱动
-
单线程
NodeJs不为每个用户创建一个新的线程,而仅仅使用一个线程,当有用户连接了,就触发了一个内部事件,通过非阻塞I/O,事件驱动机制,让Node.js程序宏观上也是并行的,使用Node.js,一个8GB内存的服务器,可以同时处理超过4万用户的连接 -
非阻塞I/O
- Node.js中采用了非阻塞型I/O机制,因此在执行了访问数据库的代码之后,将立即转而执行其后面的代码,把数据库返回结果的处理代码放在回调函数中,从而提高程序的执行效率
- 当某个I/O执行完毕后,将以事件的形式通知执行I/O操作的线程,线程执行这个事件的回调函数,为了处理异步I/O,线程必须有事件循环,不断的检查有没有未处理的事件,依次予以处理
- 事件驱动
在Node中,客户端请求建立连接,提交数据等行为,会触发相应的事件,在Node中,在一个时刻,只能执行一个回调函数,但是在执行一个事件回调函数的中途,可以转而处理其他事件(比如,新用户连接了),然后返回继续执行原事件的回调函数.这种处理机制,称谓"事件环"机制
Hello world
// 引包-特殊的功能模块
const http = require("http");
// 创建服务器,参数是一个回调函数,表示如果有请求进来,要做什么
const server = http.createServer((req, res) => {
res.writeHead(200, { "content-type": "text/html;chartset=UTF-8" });
res.end("Hello World !");
});
server.listen(3000, (err) => {
if (err) {
throw err;
}
console.log("启动服务器成功");
});
- res.writeHead(参数一,参数二)
参数一:状态码
参数二:响应头对象
Node.js没有Web容器的概念
node.js本质上上没有web容器的
,在学习PHP中,web容器放在apache中,举例:将app.html放置在apache中,可以通过地址栏http://localhost:3000/app.html访问该页面,而在node.js中不存在像apache这种web容器,不能通过地址栏访问对应的页面地址来访问页面,只能通过请求返回对应的页面数据
Node.js本质上是没有web容器的,不存在把一个页面做好后放到一个文件夹里,Node.js可以直接呈现它
- 路径(url)与文件无关
Node.js呈现一个页面
const http = require("http");
const fs = require("fs");
const path = require("path");
const server = http.createServer((req, res) => {
fs.readFile(path.join(__dirname, "index.html"), (err, data) => {
if (err) {
throw err;
}
res.writeHead(200, { "content-type": "text/html;chartset=UTF-8" });
res.end(data);
});
});
server.listen(3000, (err) => {
if (err) {
throw err;
}
console.log("启动服务器成功");
});
- fs模块读取页面内容,然后将该内容返回给客户端
我们在浏览器地址栏输入不同的地址,我们可以得到不同的页面展示,在apache中我们如果想要访问app.html页面,我们必须老老实实的输入http://localhost:3000/app.html,而在node.js中可以直接完成顶层路由设计,输入的URL跟我们绑定的页面没有直接关系,通过URL根本判断不了我们文件存在哪个地方,这种映射关系由我们在node中的相关逻辑代码来实现
页面中含有图片
- 我们在返回的页面中引入一张图片
- 发现图片加载失败,因为图片也是要发送请求的,而我们没有处理该请求的相关逻辑
- 在代码中写相关逻辑
导入外部的样式表(或者js文件)
- 导入外部的样式表或者js文件都是要发送请求的
- 晚上index.css请求的相关逻辑代码
Node.js虽然没有像Apache那样简单,需要自己写相关代码实现访问静态文件,但这个模式可以使我们写出非常具有扩展性的路由设计,顶层路由设计
http模块简单讲解
- 服务器监听可以第二个参数可以传ip
server.listen(3000, "127.0.0.1", (err) => {
if (err) {
throw err;
}
console.log("启动服务器成功");
});
- 设置响应头 text.html和text/plain (
告诉浏览器如何处理返回的数据
)
res.end(data) data不许是一个字符串或者Buffer
url模块
req.url简介
- 在地址栏中输入url(不带锚点#)
- 控制台输出(
/ 代表根路径
)
- 在地址栏中输入url(带锚点#)
- 小结:
地址栏输入的url(不管有没有锚点),在服务器的req.url都不带锚点
识别req.url
- 用到两个模块,
url模块
和querystring模块
- querystring:查询字符串,是url问号?后面的东西(不包括问号),所以需要先将url中问号后面的字符串提取出来,这个时候就需要用到url模块
- 最重要的是
url.parse()/querystring.parse()
demo1 地址栏输入
- 在地址栏中输入
- 控制台输出
因为req.url不包含hash(锚点),所以parse出来的对象,hash属性为null
demo2 表单提交
- 本地(不在服务器上)表单提交
- node服务器输入urlObj
- 然后通过querystring模块通过querystring.parse()得到一个对象,里面包含name和age属性
Node.js服务器的缺点
- 缓存问题:通常第二次访问同一文件,会返回304,而我们现在刷新,返回的状态码永远是200
这个时候就需要cookies打标记,然后检测文件的lastModify属性(最后更改信息),判断是否需要判断状态码304
即304状态码不是自动的,需要自己写相关逻辑代码(apache返回的状态码304是自动的,不需要写相关逻辑代码)
Apache服务器和Node服务器各有 优缺点,Apache内部完成了大量代码判断和逻辑,不需要开发人员写逻辑代码,Node服务器虽然需要开发人员写相关逻辑代码判断,但也实现了路由顶层设计,更加灵活
Post请求
- get请求的参数在url上,可以通过url模块和querystring模块取到参数对象
相比较get请求,post请求比较复杂,因为node.js认为,使用post请求时,数据量会比较多,为了追求数据极致的效率,它将数据拆分为了众多小的数据块(chunk),然后通过特定的事件,将这些小数据块有序传递给回调函数
demo
- 本地创建一个表单页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form action="http://127.0.0.1:3000/dopost" method="POST">
<label>姓名</label>
<input type="text" name="name" />
<label>年龄</label>
<input type="text" name="age" />
<input type="submit" />
</form>
</body>
</html>
- 创建服务器
const http = require("http");
const url = require("url");
const server = http.createServer((req, res) => {
if (req.url === "/dopost" && req.method.toLocaleLowerCase() === "post") {
var data = ""; // 空串
// 接收数据
req.on("data", (chunk) => {
data += chunk;
});
// 数据完成接收
req.on("end", () => {
console.log(data); // 字符串
});
}
});
server.listen(3000, "127.0.0.1", (err) => {
if (err) {
throw err;
}
console.log("启动服务器成功");
});
- 最重要的是两个事件:
data事件和end事件
get请求和post请求提交的数据都是字符串
,所以上面案例中data最终也是字符串- 在ajax中ge/post请求也是字符串,和node服务对应起来了
- 利用querystring模块将data字符串转换成对象
文件上传 formidable包
- formidable是用于上传文件或者视频的第三方包
- formidable实例的parse方法内部帮助我们实现了数据的收集
- fields:所有的表单域数据
- files:所有的文件域数据
- formidable实例.parse(参数一,参数二)
- 参数一: req,请求报文
- 参数二:回调函数
2.1 参数2.1: err 错误优先
2.2 参数2.2: fields 所有的表单域数据
2.3 参数2.3: files 所有的文件域数据
- 在本地表单页面中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form
action="http://127.0.0.1:3000/dopost"
enctype="multipart/form-data"
method="POST"
>
<label>姓名</label>
<input type="text" name="name" />
<label>年龄</label>
<input type="text" name="age" />
<label>文件</label>
<input type="file" name="file" />
<input type="submit" />
</form>
</body>
</html>
- 在node服务器中
- 此时我们为什么看到的fiels是一个空对象呢,是因为上传的文件还没到node服务器,只是保存在本地c盘的缓存中,
我们需要将文件从本地缓存放置到服务器中,只需要在服务器中设置保存上传文件的地址即可
- files对象最重要的是path属性和name属性
发现上传的文件没有后缀名,且文件名是一个hash值(path属性)(不是原文件名name属性)
- 实现上传文件重命名 fs.rename()
表单post提交enctype="multipart/form-data"属性的作用
-
表单不设置该属性
-
设置了该属性,文件以碎片化方式传输