Node.js

 nodejs到底是什么?

 nodejs是一个开源的,跨平台的JavaScript运行环境;是JavaScript运行的平台,类似于浏览器。不是框架,不是库,也不是语言。通俗来说,就是一款应用程序,它可以运行JavaScript。

 运行平台:一般就是指一个代码的运行环境;作用是:开发者可以使用指定的编程语言,基于某个环境开发特定的应用。平台就是为编程语言赋能,让编程语言具备实际的能力。

   这里强调一下:Node 它本身不是一门语言,而是一个开发环境,基于谷歌的 V8 引擎,能让 JavaScript 可以运行在服务器的一个环境。赋予 JavaScript 更多除了浏览器脚本以外的能力,如 IO 操作等。

比如:

对于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 中,我们可以使用 statstatSync 来查看资源的详细信息
语法:
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、响应报文

  • 响应行 

          

  1. HTTP/1.1:HTTP协议版本号
  2. 200:响应状态码 404 Not Found 500 Internal Server Error 还有一些状态码,参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
  3. 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.  防止命名冲突
  2.  高复用性
  3.  高维护性
 1、暴露数据

模块暴露数据的方式有两种:

  1. module.exports = value
  2. exports.name = value

 2、导入模块

require 使用的一些注意事项:

  1. 对于自己创建的模块,导入时路径建议写 相对路径 ,且不能省略 ./ 和 ../
  2. jsjson 文件导入时可以不用写后缀,c/c++编写的 node 扩展文件也可以不写后缀,但是一 般用不到
  3. 如果导入其他类型的文件,会以 js 文件进行处理
  4. 如果导入的路径是个文件夹,则会 首先 检测该文件夹下 package.json 文件中 main 属性对应 的文件, 如果存在则导入,反之如果文件不存在会报错。 如果 main 属性不存在,或者 package.json 不存在,则会尝试导入文件夹下的 index.jsindex.json , 如果还是没找到,就会报错
  5. 导入 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;
}

 

posted @ 2023-04-21 14:48  聂丽芳  阅读(71)  评论(0编辑  收藏  举报