nodejs到底是什么?
nodejs是一个开源的,跨平台的JavaScript运行环境;是JavaScript运行的平台,类似于浏览器。不是框架,不是库,也不是语言。通俗来说,就是一款应用程序,它可以运行JavaScript。
运行平台:一般就是指一个代码的运行环境;作用是:开发者可以使用指定的编程语言,基于某个环境开发特定的应用。平台就是为编程语言赋能,让编程语言具备实际的能力。
比如:
对于js和浏览器:
浏览器和node,不同的运行平台提供不同的api
node适合做什么?
人员角度:node平台采用的是JavaScript,适合前端开发者使用
技术角度:node平台特点是非阻塞IO,适合处理高并发请求
结论:
node适合开发服务器端的中间层(BFF)
适合用于开发前端方向的各种工具(webpack vite babel)
开发桌面端应用(vscode postman figma)
node和Web的区别:
Web Api:DOM BOM AJAX Storage console 定时器
Node Api:fs url http util console 定时器 path
注意事项:
Node中不存在DOM(比如document对象)和BOM(比如window、history、navigator对象等)的API,Ajax对象等
Node中的顶级对象为global,也可以用globalThis访问顶级对象
Buffer
译为“缓冲区”,是一个类似于Array对象,用于表示固定长度的字节序列。换句话说,Buffer就是一段固定长度的内存空间,用于处理二进制数据。
特点:大小固定且无法调整;性能较好,可以直接对计算机内存进行操作;每个元素大小为1字节(byte)
计算机基础知识
内存:读写速度快,断电丢失数据
硬盘:读写速度慢,断电不丢失
程序一般保存在硬盘中,软件安装的过程就是将程序写入硬盘的过程;程序在运行时会加载进入内存,然后由CPU读取并执行程序
fs模块
可以实现与硬盘的交互。例如文件的创建、删除、重命名、移动;还有文件内容的写入、读取以及文件夹的相关操作。
1、文件写入
文件写入就是将 数据 保存到 文件 中,可以使用如下几个方法来实现。
1-1 writeFile 异步写入
语法: fs.writeFile(file, data[, options], callback)
参数说明: file 文件名 data 待写入的数据 options 选项设置 (可选) callback 写入回调
返回值: undefined
// require 是 Node.js 环境中的'全局'变量,用来导入模块 const fs = require('fs'); //将 『三人行,必有我师焉。』 写入到当前文件夹下的『座右铭.txt』文件中 fs.writeFile('./座右铭.txt', '三人行,必有我师焉。', err => { //如果写入失败,则回调函数调用时,会传入错误对象,如写入成功,会传入 null if(err){ console.log(err); return; } console.log('写入成功'); });
1-2. writeFileSync 同步写入
语法: fs.writeFileSync(file, data[, options])
参数与 fs.writeFile 大体一致,只是没有 callback 参数
返回值: undefined
try{ fs.writeFileSync('./座右铭.txt', '三人行,必有我师焉。'); }catch(e){ console.log(e); }
1-3 . appendFile / appendFileSync 追加写入
appendFile 作用是在文件尾部追加内容,appendFile 语法与 writeFile 语法完全相同
语法: fs.appendFile(file, data[, options], callback) fs.appendFileSync(file, data[, options])
返回值: 二者都为 undefined
fs.appendFile('./座右铭.txt','择其善者而从之,其不善者而改之。', err => { if(err) throw err; console.log('追加成功') }); fs.appendFileSync('./座右铭.txt','\r\n温故而知新, 可以为师矣');
1-4 createWriteStream 流式写入
语法: fs.createWriteStream(path[, options])
参数说明: path 文件路径 options 选项配置( 可选 )
返回值: Object
const fs = require("fs"); //创建写入流对象 const ws = fs.createWriteStream("./观书有感.txt"); //写入文件 ws.write("半亩方塘一鉴开\r\n"); ws.write("天光云影共徘徊\r\n"); ws.write("问渠哪得清如许\r\n"); ws.write("为有源头活水来\r\n"); //关闭通道 ws.close();
1-5 文件写入的场景
需要持久化保存数据的时候。比如:下载文件、安装软件、保存程序日志、 Git 编辑器保存文件、视频录制
2、文件读取
2-1 readFile 异步读取
语法: fs.readFile(path[, options], callback)
参数说明: path 文件路径 options 选项配置 callback 回调函数
const fs = require("fs"); fs.readFile("./观书有感.txt", (err, data) => { if (err) { console.log("读取失败", err); return; } console.log(data.toString()); });
2-2 readFileSync 同步读取
语法: fs.readFileSync(path[, options])
参数说明: path 文件路径 options 选项配置
返回值: string | Buffer
const data = fs.readFileSync("./观书有感.txt"); //data类型是buffer console.log(data.toString());
2-3 createReadStream 流式读取
读取大文件时可以提升读取效率
语法: fs.createReadStream(path[, options])
参数说明:
path 文件路径 options 选项配置( 可选 ) 返回值: Object
//创建读取流对象 let rs = fs.createReadStream('./观书有感.txt'); //每次取出 64k 数据后执行一次 data 回调 rs.on('data', data => { console.log(data); console.log(data.length); }); //读取完毕后, 执行 end 回调 rs.on('end', () => { console.log('读取完成') })
2-4 应用场景
电脑开机 程序运行 编辑器打开文件 查看图片 播放视频 播放音乐 Git 查看日志 上传文件 查看聊天记录
3、文件移动与文件重命名
在 Node.js 中,我们可以使用 rename 或 renameSync 来 移动或重命名 文件或文件夹
语法: fs.rename(oldPath, newPath, callback) fs.renameSync(oldPath, newPath)
参数说明: oldPath 文件当前的路径 newPath 文件新的路径 callback 操作后的回调
const fs = require("fs"); //rename fs.rename("./论语.txt", "./座右铭.txt", (err) => { if (err) { console.log("操作失败"); return; } }); //文件的移动 fs.rename("./data.txt", "../手写代码/data.txt", (err) => { if (err) { console.log("操作失败"); return; } });
4、文件的删除
在 Node.js 中,我们可以使用 unlink 或 unlinkSync 来删除文件
语法: fs.unlink(path, callback) fs.unlinkSync(path)
参数说明: path 文件路径 callback 操作后的回调
const fs = require("fs"); fs.unlink("./data.txt", () => { if (err) { console.log("删除失败"); return; } }); //调用rm方法删除 fs.rm("./data.txt", (err) => { if (err) { console.log("删除失败"); return; } });
5、文件夹操作
5-1 创建文件夹
在 Node.js 中,我们可以使用 mkdir 或 mkdirSync 来创建文件夹
语法: fs.mkdir(path[, options], callback) fs.mkdirSync(path[, options])
参数说明: path 文件夹路径 options 选项配置( 可选 ) callback 操作后的回调
const fs = require("fs"); //创建文件夹 fs.mkdir("./html", (err) => { if (err) { return; } }); //递归创建(recursive) fs.mkdir("./a/b/c", { recursive: true }, (err) => { if (err) { return; } });
5-2 读取文件夹
在 Node.js 中,我们可以使用 readdir 或 readdirSync 来读取文件夹
语法: fs.readdir(path[, options], callback) fs.readdirSync(path[, options])
参数说明: path 文件夹路径 options 选项配置( 可选 ) callback 操作后的回调
fs.readdir("./a", (err, data) => { if (err) { return; } console.log(data); });
5-3 删除文件夹
在 Node.js 中,我们可以使用 rmdir 或 rmdirSync 来删除文件夹
语法:
fs.rmdir(path[, options], callback)
fs.rmdirSync(path[, options])
参数说明:
path 文件夹路径 options 选项配置( 可选 ) callback 操作后的回调
//删除文件夹 fs.rmdir("./html", (err) => { if (err) { return; } console.log("删除成功"); }); //递归删除(不推荐) fs.rmdir("./a", { recursive: true }, (err) => { if (err) { return; } console.log("删除成功"); }); //建议使用 fs.rm("./a", { recursive: true }, (err) => { if (err) { return; } console.log("删除成功"); });
6、查看资源状态
在 Node.js 中,我们可以使用 stat 或 statSync 来查看资源的详细信息
语法:
fs.stat(path[, options], callback)
fs.statSync(path[, options])
参数说明:
path 文件夹路径 options 选项配置( 可选 ) callback 操作后的回调
const fs = require("fs"); //stat fs.stat("../代码/02_fs文件系统/资料/笑看风云.mp4", (err, data) => { if (err) { return; } console.log(data); });
结果值对象结构:
size 文件体积 birthtime 创建时间 mtime 最后修改时间 isFile 检测是否为文件 isDirectory 检测是否为文件夹等等
7、相对路径问题
fs 模块对资源进行操作时,路径的写法有两种:
相对路径(会随着当前运行工作目录的更改而改变文档写入的位置):
./座右铭.txt 当前目录下的座右铭.txt
座右铭.txt 等效于上面的写法
../座右铭.txt 当前目录的上一级目录中的座右铭.txt
绝对路径:
D:/Program Files windows 系统下的绝对路径
/usr/bin Linux 系统下的绝对路径
const fs = require("fs"); //相对路径 fs.writeFileSync("./index.html", "love"); //与上面写法一致 fs.writeFileSync("index.html", "love"); //绝对路径 fs.writeFileSync("C:/Users/13052/Desktop/node学习/index.html", "love"); fs.writeFileSync("/test.html", "love"); //此时在c盘的根目录创建了文件
8、__dirname
__dirname 与 require 类似,都是 Node.js 环境中的'全局'变量
__dirname 保存着 当前文件所在目录的绝对路径 ,可以使用 __dirname 与文件名拼接成绝对路径,不会随着工作目录的改变而改变
fs.writeFileSync(__dirname + "/test.html", "love");
练习:重命名文件夹下面的文件名
const fs = require("fs"); //读取code文件夹 const files = fs.readdirSync("./code"); files.forEach((item) => { //判断第一位是不是小于10 1-文件写入.js //拆分文件名 //[1,'文件写入.js'] let data = item.split("-"); let [num, name] = data; if (Number(num) < 10) { num = "0" + num; } //创建新的文件命 let newName = num + "-" + name; //重命名 fs.renameSync(`./code/${item}`, `./code/${newName}`); });
_path模块
path 模块提供了 操作路径 的功能,如下几个较为常用的几个 API:
HTTP协议
1、HTTP报文的组成
- 请求行
- 请求头
- 空行
- 请求体
2、HTTP请求行
- 请求方法(get、post、put、delete等)
- 请求 URL(统一资源定位器)
例如:http://www.baidu.com:80/index.html?a=100&b=200#logo
http: 协议(https、ftp、ssh等)
www.baidu.com 域名
80 端口号
/index.html 路径
a=100&b=200 查询字符串
#logo 哈希(锚点链接)
- HTTP协议版本号
3、请求头
格式:『头名:头值』
常见的请求头有:
4、HTTP请求体
请求体内容的格式是非常灵活的,与后端约定, (可以是空)==> GET请求, (也可以是字符串,还可以是JSON)===> POST请求
例如: 字符串:keywords=手机&price=2000
JSON:{"keywords":"手机","price":2000}
下图为某网站登录发送的请求体:
5、响应报文
- 响应行
- HTTP/1.1:HTTP协议版本号
- 200:响应状态码 404 Not Found 500 Internal Server Error 还有一些状态码,参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
- OK:响应状态描述
- 响应头
- 空行
- 响应体
响应体内容的类型是非常灵活的,常见的类型有 HTML、CSS、JS、图片、JSON
6、创建HTTP服务器
IP:32位二进制bite组成,标识网络设备,实现设备间的通信
局域网IP:路由器给下面的设备分发的IP,可以解决IP不够用的问题
IP分类:
端口:实现不同主机应用程序之间的通信
场景说明:
6-1 创建HTTP服务
操作步骤:
//1. 导入 http 模块 const http = require('http'); //2. 创建服务对象 create 创建 server 服务 // request 意为请求. 是对请求报文的封装对象, 通过 request 对象可以获得请求报文的数据 // response 意为响应. 是对响应报文的封装对象, 通过 response 对象可以设置响应报文 const server = http.createServer((request, response) => { response.end('Hello HTTP server'); }); //3. 监听端口, 启动服务 server.listen(9000, () => { console.log('服务已经启动, 端口 9000 监听中...'); });
测试:
浏览器请求对应端口
注意事项:
1.命令行 ctrl + c 停止服务
2. 当服务启动后,更新代码 必须重启服务才能生效
3. 响应内容中文乱码的解决办法
response.setHeader("content-type", "text/html;charset=utf-8"); response.end("你好");
4. 端口号被占用
1)关闭当前正在运行监听端口的服务 ( 使用较多 )
2)修改其他端口号
5. HTTP 协议默认端口是 80 。HTTPS 协议的默认端口是 443, HTTP 服务开发常用端口有 3000, 8080,8090,9000 等
6-2 获取HTTP请求报文
想要获取请求的数据,需要通过 request 对象
注意事项:
1. request.url 只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容
2. request.headers 将请求信息转化成一个对象,并将属性名都转化成了『小写』
3. 关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为『 / 』
4. 关于 favicon.ico:这个请求是属于浏览器自动发送的请求
6-3 练习
const http = require("http"); const server = http.createServer((request, response) => { //获取请求方法 let { method } = request; let { pathname } = new URL(request.url, "http://127.0.0.1"); response.setHeader("content-type", "text/html;charset=utf-8"); if (method === "GET" && pathname === "/login") { response.end("登录页面"); } else if (method === "GET" && pathname === "/reg") { response.end("注册页面"); } else { response.end("<h1>404 Not Found</h1>"); } }); server.listen(9000, () => { console.log("服务启动"); });
6-4 设置HTTP响应报文
const http = require("http"); const server = http.createServer((request, response) => { //响应状态码 response.statusCode = 404; //响应状态的描述 response.statusMessage = "error"; //设置响应头 response.setHeader("content-type", "text/html;charset=utf-8"); response.setHeader("Server", "Node.js"); response.setHeader("myHeader", "test"); //设置多个同名的响应头 response.setHeader("test", ["a", "b", "c"]); //设置响应体 response.write("i"); response.write("love"); response.end(" you"); }); server.listen(9000, () => { console.log("服务启动"); });
6-5 练习
生成table,隔行换色,点击单元格高亮显示
const http = require("http"); const server = http.createServer((request, response) => { response.end(` <!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: #aef; } table tr:nth-child(even){ background: #fcb; } 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> <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 = '#222'; } }) </script> </body> </html> `); }); server.listen(9000, () => { console.log("服务启动..."); });
6-6 练习优化:设置响应内容
const http = require("http"); const fs = require("fs"); const server = http.createServer((request, response) => { //读取文件内容 let html = fs.readFileSync(__dirname + "/10-table.html"); response.end(html); }); server.listen(9000, () => { console.log("服务启动"); });
6-7 网页资源的基本加载过程
网页资源的加载都是循序渐进的,首先获取 HTML 的内容, 然后解析 HTML 在发送其他资源的请求,如 CSS,Javascript,图片等。 理解了这个内容对于后续的学习与成长有非常大的帮助
练习扩展:将html、css、js代码分开
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"); if (pathname === "/") { let html = fs.readFileSync(__dirname + "/table.html"); response.end(html); } else if (pathname === "/index.css") { let css = fs.readFileSync(__dirname + "/index.css"); response.end(css); } else if (pathname === "/index.js") { let js = fs.readFileSync(__dirname + "/index.js"); response.end(js); } else { response.statusCode = 404; response.end(`<h1>404 Not Found</h1>`); } }); server.listen(9000, () => { console.log("服务启动中"); });
6-8 静态资源
静态资源是指 内容长时间不发生改变的资源 ,例如图片,视频,CSS 文件,JS文件,HTML文件,字体文 件等
动态资源是指 内容经常更新的资源 ,例如百度首页,网易首页,京东搜索列表页面等
/** * 创建一个 HTTP 服务,端口为 9000,满足如下需求 * GET /index.html 响应 page/index.html 的文件内容 * GET /css/app.css 响应 page/css/app.css 的文件内容 * GET /images/logo.png 响应 page/images/logo.png 的文件内容 */ const http = require("http"); const fs = require("fs"); const path = require("path"); const server = http.createServer((request, response) => { const { pathname } = new URL(request.url, "http://127.0.0.1"); let root = __dirname + "/pages"; let filePath = root + pathname; fs.readFile(filePath, (err, data) => { if (err) { response.setHeader("content-type", "text/html;charset=utf-8"); response.statusCode = 500; response.end("文件读取失败!!"); return; } response.end(data); }); }); server.listen(9000, () => { console.log("服务启动...."); });
1、网页中的路径
网页中的 URL 主要分为两大类:相对路径与绝对路径
1-1 绝对路径:绝对路径可靠性强,而且相对容易理解,在项目中运用较多
1-2 相对路径:在发送请求时,需要与当前页面 URL 路径进行 计算 ,得到完整 URL 后,再发送请求,学习阶段用的较多
例如当前网页 url 为 http://www.atguigu.com/course/h5.html
1-3 网页中使用url的场景
- a 标签 href
- link 标签 href
- script 标签
- src img 标签 src
- video audio 标签 src
- form 中的 action
- AJAX 请求中的 URL
2、设置资源类型(mime类型)
/** * 创建一个 HTTP 服务,端口为 9000,满足如下需求 * GET /index.html 响应 page/index.html 的文件内容 * GET /css/app.css 响应 page/css/app.css 的文件内容 * GET /images/logo.png 响应 page/images/logo.png 的文件内容 */ 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) => { const { pathname } = new URL(request.url, "http://127.0.0.1"); let root = __dirname + "/page"; let filePath = root + pathname; fs.readFile(filePath, (err, data) => { if (err) { response.setHeader("content-type", "text/html;charset=utf-8"); response.statusCode = 500; response.end("文件读取失败!!"); return; } //获取文件的后缀名 let ext = path.extname(filePath).slice(1); let type = mimes[ext]; if (type) { //html的类型才需要做utf-8的处理 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("服务启动...."); });
3、错误处理
//判断错误代号 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>); }
模块化
将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化 其中拆分出的 每个文件就是一个模块 ,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他 模块使用
模块化的好处:
- 防止命名冲突
- 高复用性
- 高维护性
1、暴露数据
模块暴露数据的方式有两种:
- module.exports = value
- exports.name = value
2、导入模块
require 使用的一些注意事项:
- 对于自己创建的模块,导入时路径建议写 相对路径 ,且不能省略 ./ 和 ../
- js 和 json 文件导入时可以不用写后缀,c/c++编写的 node 扩展文件也可以不写后缀,但是一 般用不到
- 如果导入其他类型的文件,会以 js 文件进行处理
- 如果导入的路径是个文件夹,则会 首先 检测该文件夹下 package.json 文件中 main 属性对应 的文件, 如果存在则导入,反之如果文件不存在会报错。 如果 main 属性不存在,或者 package.json 不存在,则会尝试导入文件夹下的 index.js 和 index.json , 如果还是没找到,就会报错
- 导入 node.js 内置模块时,直接 require 模块的名字即可,无需加 ./ 和 ../
3、导入模块的基本流程
require导入自定义模块的基本流程(实现原理)
- 将相对路径转为绝对路径,定位目标文件
- 缓存检测
- 读取目标文件代码
- 包裹为一个函数并执行(自执行函数)。通过 arguments.callee.toString() 查看自执行函数
- 缓存模块的值
- 返回 module.exports 的值
伪代码:
function require(file){ //1. 将相对路径转为绝对路径,定位目标文件 let absolutePath = path.resolve(__dirname, file); //2. 缓存检测 if(caches[absolutePath]){ return caches[absolutePath]; } //3. 读取文件的代码 let code = fs.readFileSync(absolutePath).toString(); //4. 包裹为一个函数 然后执行 let module = {}; let exports = module.exports = {}; (function (exports, require, module, __filename, __dirname) { const test = { name: '尚硅谷' } module.exports = test; //输出 console.log(arguments.callee.toString()); })(exports, require, module, __filename, __dirname) //5. 缓存结果 caches[absolutePath] = module.exports; //6. 返回 module.exports 的值 return module.exports; }
标签:Node,fs,const,err,js,path,http,response From: https://www.cnblogs.com/nielifang/p/16475680.html