HTTP
设置 HTTP 响应报文
HTTP报文常见属性:
const http = require('http');
const server = http.createServer((request, response) => {
// 设置请求状态码 2xx 4xx 5xx
response.statusCode = 200;
// 设置请求描述 了解即可
response.statusMessage = 'hello'
// 指定响应体的MIME类型
response.setHeader('Content-Type', 'text/html; charset=utf-8');
// 指定响应内容的编码方式,如gzip或deflate,用于内容压缩。
response.setHeader('Content-Encoding', 'gzip');
// 用于实现跨源资源共享(CORS),指定哪些网站可以访问该资源。
response.setHeader('Access-Control-Allow-Origin', '*'); // 允许所有来源,实际应用中可能需要指定具体来源
// 指示浏览器如何缓存响应
response.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); // 禁止缓存
// 一个实体标签,用于验证资源是否有变化
response.setHeader('ETag', 'W/"abcd1234"');
// 表示资源最后一次修改的时间。
response.setHeader('Last-Modified', new Date().toUTCString());
// 指示服务器软件的信息
response.setHeader('Server', 'MyCustomServer/1.0');
// 设置Cookie,用于会话管理等。
response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
// 指定响应过期时间,之后浏览器将不再缓存此响应。
response.setHeader('Expires', new Date(Date.now() + 86400000).toUTCString()); // 一天后过期
response.end('Hello Http Server');
});
server.listen(9000, () => {
console.log('服务已启动');
});
加载 HTML 文件内容
HTML 文件如下
<!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>
<style>
td {
padding: 20px 40px;
}
table tr:nth-child(odd) {
background: pink;
}
table tr:nth-child(even) {
background: red;
}
table,
td {
border-collapse: collapse;
}
</style>
</head>
<body>
<table border="1">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<script>
//获取所有的 td
let tds = document.querySelectorAll("td");
//遍历
tds.forEach((item) => {
item.onclick = function () {
this.style.background = "#ffffff";
};
});
</script>
</body>
</html>
通过 FS 模块
const http = require('http');
const fs = require('fs');
const server = http.createServer((request, response) => {
const table = fs.readFileSync(__dirname + `/table.html`);
response.end(table);
});
server.listen(9000, () => {
console.log('服务已启动');
});
response.end
不仅可以接收字符串作为参数,发送文本数据给客户端,还能够接收 Buffer 对象来发送二进制数据,如图片、音频或视频文件等,需要注意的是使用 Buffer 时,确保你的内容类型(Content-Type)设置与实际数据类型相匹配,以便浏览器或客户端能够正确处理。
网页资源加载基本过程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>标题</title>
<link rel="stylesheet" href="./css/index.css" />
</head>
<body>
<h1>这是一个HTML页面</h1>
<hr />
<img width="100%" src="./images/h5.png" alt="" />
<script src="./js/index.js"></script>
</body>
</html>
- 请求HTML文档:浏览器首先向服务器发送请求以获取
HTML
文档。这一步通常通过URL
完成。 - 解析HTML:浏览器开始解析收到的
HTML
文档,构建DOM
(文档对象模型)。解析过程中,浏览器会遇到以下类型的资源:
- CSS:当遇到
link
标签引用外部CSS
文件或style
标签包含内联样式时,浏览器会开始加载CSS
(如果是外部链接,则发送CSS
请求)。CSS
的加载通常与HTML
解析并行进行,但CSS
的解析(构建CSSOM
,即CSS
对象模型)可能等待CSS
文件完全加载后才进行,以确保正确应用样式。内联样式会立即被处理。 - IMG 图片:遇到
img
标签时,浏览器会立即发送请求下载图片,但不会等待图片加载完成就继续解析HTML,以加快页面内容的显示。图片加载通常是异步的。 - JavaScript:当浏览器遇到
script
标签时,一般分为同步脚本和异步脚本: -
- 同步脚本:默认情况下,
JavaScript
的加载和执行会阻塞HTML
的解析。浏览器会暂停构建DOM
,等待脚本下载并执行完毕。这是因为JavaScript
有可能修改DOM
结构或CSSOM
。
- 同步脚本:默认情况下,
-
- 异步脚本:如果
script
标签指定了async
属性,脚本会在下载的同时允许HTML
解析继续,且脚本下载完成后会异步执行,不保证执行顺序。如果使用defer属性(仅适用于外部脚本),脚本同样不会阻塞HTML
解析,但所有带有defer
的脚本会在HTML
解析完成后,DOMContentLoaded
事件触发之前,按照在HTML
中出现的顺序执行。
- 异步脚本:如果
- 渲染流程:在解析
HTML
和加载CSS
的过程中,浏览器开始构建渲染树,它是DOM
和CSSOM
的合并产物,表示哪些节点需要如何显示。一旦渲染树构建完成,并且相关资源(如图片)准备就绪,浏览器会开始渲染页面。 - 重排与重绘:在页面加载和
JavaScript
修改DOM/CSSOM
时,可能会触发布局重排(Reflow)
和样式重绘(Repaint)
,以反映页面结构或样式的改变。
加载 HTML 文件内容(优化一)
当结构为资源都在响应文件夹下时
<!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>
<link rel="stylesheet" href="../css/index.css">
</head>
<body>
<table border="1">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<script src="../js/index.js"></script>
</body>
</html>
目录结构
├─css
├─html
├─js
└─服务.js
启动服务.js
后
const http = require('http');
const fs = require('fs');
const server = http.createServer((request, response) => {
const table = fs.readFileSync(__dirname + `/html/table.html`);
response.end(table);
});
server.listen(9000, () => {
console.log('服务已启动');
});
会发现css样式和js点击功能失效。
这是由于响应报文均返回html所导致。
调整代码为以下即可:
//导入 http 模块
const http = require('http');
const fs = require('fs');
//创建服务对象
const server = http.createServer((request, response) => {
//获取请求url的路径
let {pathname} = new URL(request.url, 'http://127.0.0.1');
console.log('pathname', pathname);
if(pathname === '/'){
let html = fs.readFileSync(__dirname + '/html/10_table.html');
response.end(html);
}else if(pathname === '/css/index.css'){
let css = fs.readFileSync(__dirname + '/css/index.css');
response.end(css);
}else if(pathname === '/js/index.js'){
let js = fs.readFileSync(__dirname + '/js/index.js');
response.end(js);
}else{
response.statusCode = 404;
response.end('<h1>404 Not Found</h1>')
}
});
//监听端口, 启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
});
加载HTML文件内容(优化二)
//导入 http 模块
const http = require('http');
const fs = require('fs');
const path = require('path');
//创建服务对象
const server = http.createServer((request, response) => {
//获取请求url的路径
let { pathname } = new URL(request.url, 'http://127.0.0.1');
const filePath = __dirname + pathname;
fs.readFile(filePath, (err, data) => {
if (err) {
response.statusCode = 500;
return;
}
console.log('data', data);
response.end(data);
})
加载HTML文件内容(优化三)
const http = require('http');
const fs = require('fs');
const path = require('path');
//声明一个变量
let mimes = {
html: 'text/html',
css: 'text/css',
js: 'text/javascript',
png: 'image/png',
jpg: 'image/jpeg',
gif: 'image/gif',
mp4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json'
}
const server = http.createServer((request, response) => {
// 拼接路径
let url = new URL(request.url, 'http://127.0.0.1');
// 根目录
let root = __dirname + '/page';
// 拼接文件路径 css js html ...
let filePath = root + url.pathname;
// 读取文件
fs.readFile(filePath, (err, data) => {
if (err) {
console.error(err);
// 设置字符集
response.setHeader('content-type', 'text/html;charset=utf-8');
switch (err.code) {
case 'ENOENT':
response.statusCode = 404;
response.end('<h1>404 Not Found</h1>')
case 'EPERM':
response.statusCode = 403;
response.end('<h1>403 Forbidden</h1>');
default:
response.statusCode = 500;
response.end('<h1>Internal Server Error</h1>');
};
return;
};
// 获取文件后缀名
let ext = path.extname(filePath).slice(1);
// 匹配类型
let type = mimes[ext];
if (type) {
if (ext === 'html') {
response.setHeader('content-type', type + ';charset=utf-8');
} else {
response.setHeader('content-type', type);
}
} else {
//没有匹配到
response.setHeader('content-type', 'application/octet-stream');
};
response.end(data);
})
});
server.listen('9000', () => {
console.log('服务已经启动....');
})
GET与POST请求场景
GET
- 在地址栏直接输入url访问
- 点击a链接
- link标签引入css
- script标签引入js
- img标签引入脱
- from标签中的method为get
- ajax中的get请求
POST
- form标签中的method为post
- ajax的post请求