爆肝将近 10 万字讲解 Node.Js 详细教程

1. Node.Js 环境概述

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,用于在服务器端运行 JavaScript。它使用了一个事件驱动、非阻塞式I/O的模型,使得其轻量且高效。Node.js 的包管理器 npm 是全球最大的开源库生态系统。Node.js 能够响应大量的并发请求,适合运用在高并发、I/O密集、少量业务逻辑的场景。

1.1 安装 NodeJs 环境

NodeJs 下载路径:https://nodejs.org/en

在这里插入图片描述

安装完成后可以在命令行中输入 node -v 和 npm -v 检查是否安装成功?

在这里插入图片描述

1.2 globalThis 和 global 变量

Node.js 中不能使用 BOM 和 DOM 的 API,可以使用 console 和定时器 API。

Node.js 中的顶级对象是 global,也可以用 globalThis 访问顶级对象。

<ref *1> Object [global] {
  global: [Circular *1],
  clearInterval: [Function: clearInterval],
  clearTimeout: [Function: clearTimeout],
  setInterval: [Function: setInterval],
  setTimeout: [Function: setTimeout] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  },
  queueMicrotask: [Function: queueMicrotask],
  performance: Performance {
    nodeTiming: PerformanceNodeTiming {
      name: 'node',
      entryType: 'node',
      startTime: 0,
      duration: 36.77699999511242,
      nodeStart: 0.7232999950647354,
      v8Start: 2.644999995827675,
      bootstrapComplete: 26.97859999537468,
      environment: 13.895999997854233,
      loopStart: -1,
      loopExit: -1,
      idleTime: 0
    },
    timeOrigin: 1698760750401.373
  },
  clearImmediate: [Function: clearImmediate],
  setImmediate: [Function: setImmediate] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  }
}

console.log(globalThis === global) // true

2. Buffer 缓冲区

Buffer 中文译为『缓冲区』,是一个类似于 Array 的对象,用于表示固定长度的字节序列。换句话说,Buffer 就是一段固定长度的内存空间,用于处理二进制数据。

2.1 alloc() 和 allocUnsafe()

alloc: 这是 Node.js 中的一个函数,用于在堆中分配内存。它通常用于分配一块具有确定大小的连续内存块。node alloc 函数确保了分配的内存是初始化的,也就是说,分配的内存区域都被初始化为零。这使得它非常适合分配需要清零的内存,例如用于密码学目的的内存。然而,由于它涉及到清零操作,node alloc 的性能可能会略低于 allocUnsafe

allocUnsafe: 这是 V8 JavaScript 引擎(Node.js 的默认 JavaScript 引擎)中的一个函数。它用于在 V8 的堆内存中分配内存,不需要进行初始化。这意味着分配的内存区域可能包含以前分配并释放的垃圾数据。因此,使用 allocUnsafe 分配的内存可能存在数据泄露的风险,特别是在处理敏感数据时。然而,由于不需要进行初始化操作,allocUnsafe 的性能通常会比 node alloc 高。

node allocallocUnsafe 都是用于分配内存的函数,但它们的使用和安全性有所不同。node alloc 提供了初始化的内存分配,适合需要清零的场景,但性能略低。而 allocUnsafe 则提供了未初始化的内存分配,性能较高,但可能存在数据泄露的风险。选择使用哪个函数取决于你的具体需求和安全性要求。

let buf = Buffer.alloc(10);
console.log(buf)
// <Buffer 00 00 00 00 00 00 00 00 00 00>
let buf_2 = Buffer.allocUnsafe(10000);
console.log(buf_2)
// <Buffer 00 06 f5 ff 5b 02 00 00 70 a9 35 f6 5b 02 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ... 9950 more bytes>

2.2 Buffer.from() 创建 Buffer 实例

let buf = Buffer.from("i love you")
console.log(buf)
// <Buffer 69 20 6c 6f 76 65 20 79 6f 75>

Buffer.from() 是 Node.js 的 Buffer 类的一个静态方法,用于创建一个新的 Buffer 实例从指定的 ArrayBuffer,或者在给定的类型数组中。通过

Buffer.from(arrayBuffer[, byteOffset[, length]])
paramdetaildefault
arrayBuffer一个 ArrayBuffer 或共享 ArrayBuffer 对象,将创建 Buffer 对象表示的原始二进制数据
byteOffset(可选)从 ArrayBuffer 中开始读取数据的偏移量(以字节为单位)默认值为 0
length(可选)要读取的字节数默认值为 arrayBuffer.length - byteOffset
let buf1 = Buffer.from(new ArrayBuffer(10));
let buf2 = Buffer.from(new ArrayBuffer(10), 2); 
// 从偏移 2 开始的数据
let buf3 = Buffer.from(new ArrayBuffer(10), 2, 5); 
// 从偏移 2 开始读取 5 个字节的数据

<Buffer 69 20 6c 6f 76 65 20 79 6f 75> 8 bit 8 位存储,两个 16 进制数,00 ~ ff(0 ~ 255)

let buf = Buffer.from("i love you")

buf.forEach(element => {
    console.log(element)
	// 105 32 108 111 118 101 32 121 111 117
});

let buff = Buffer.from([105, 32, 108, 111, 118, 101, 32, 121, 111, 117])
console.log(buff.toString())
// i love you

2.3 UTF-8 编码中文字符 buffer

let buf = Buffer.from("你好")
console.log(buf)
// <Buffer e4 bd a0 e5 a5 bd>

Buffer类在 Node.js 中用于处理二进制数据,其本身并不直接支持 UTF-8 编码。但是,你可以使用Buffer类来存储和操作 UTF-8 编码的字符串。

例如,如果你有一个 UTF-8 编码的字符串,你可以使用Buffer类的构造函数创建一个Buffer对象:

let buf = Buffer.from('你好,世界!', 'utf-8');

在这个例子中,‘你好,世界!’ 是 UTF-8 编码的字符串,Buffer.from 方法用于创建一个新的 Buffer 对象。第二个参数 ‘utf-8’ 指定了字符串的编码格式。

你也可以使用 Buffer.allocUnsafe() 或 Buffer.from() 来创建一个新的 Buffer 对象,并通过 toString() 方法将 Buffer 对象转换为 UTF-8 编码的字符串:

let buf = Buffer.allocUnsafe(13);  
buf.fill('你好,世界!', 0, 13);  
let str = buf.toString('utf-8');  
console.log(str); // '你好,世界!'

在这个例子中,我们使用 Buffer.allocUnsafe() 创建了一个新的 Buffer 对象,并使用 fill() 方法将字符串填充到 Buffer 中。然后,我们使用 toString() 方法将 Buffer 对象转换为 UTF-8 编码的字符串。

3. fs 模块读写文件

fs 是 Node.js 的一个内置模块,它是 Node.js 文件系统模块,用于在服务器端操作文件。它提供了一系列的方法和属性,可以满足用户对文件的操作需求。例如,可以读取文件、写入文件、删除文件等。

要在 JavaScript 代码中使用 fs 模块来操作文件,需要先导入 fs 模块。可以使用 const fs = require(‘fs’); 来导入该模块。

3.1 fs 模块常用方法和属性

fs.readFile(path[, options], callback):异步地读取文件的内容。path 是文件路径,options 可选,通常不需要指定,callback 是回调函数,它会在文件读取完成后被调用,并包含两个参数:err(错误对象)和 data(文件内容)。

fs.writeFile(path, data[, options], callback):异步地将数据写入文件。path 是文件路径,data 是要写入的数据,options 可选,通常不需要指定,callback 是回调函数,它会在文件写入完成后被调用,并包含两个参数:err(错误对象)和 data(写入的数据)。

fs.unlink(path, callback):异步地删除文件。path 是要删除的文件路径,callback 是回调函数,它会在文件删除完成后被调用,并包含一个参数:err(错误对象)。

fs.mkdir(path[, options], callback):异步地创建目录。path 是要创建的目录路径,options 可选,通常不需要指定,callback 是回调函数,它会在目录创建完成后被调用,并包含一个参数:err(错误对象)。

fs.rename(oldPath, newPath, callback):异步地重命名文件或目录。oldPath 是要重命名的文件或目录的路径,newPath 是新的文件或目录的路径,callback 是回调函数,它会在重命名完成后被调用,并包含一个参数:err(错误对象)。

fs.readdir(path[, options], callback):异步地读取目录的内容。path 是目录路径,options 可选,通常不需要指定,callback 是回调函数,它会在目录读取完成后被调用,并包含两个参数:err(错误对象)和 files(目录中的文件名列表)。

fs.rmdir(path, callback):异步地删除目录。path 是要删除的目录路径,callback 是回调函数,它会在目录删除完成后被调用,并包含一个参数:err(错误对象)。

请注意,这些方法都是异步的,意味着它们不会立即完成。它们通常在回调函数中提供结果,而不是在返回值中提供。因此,需要使用回调函数来处理异步操作的结果。

3.2 fs 流式读写 和 普通读写

const fs = require("fs")

const ws = fs.createWriteStream("helloworld.txt")
ws.write("hello world")
ws.end()

在这里插入图片描述

Node.Js 流式读写和普通读写的应用场景

普通读写:适用于小数据量、单次读写操作。例如,读取文件内容并在控制台输出,或者将数据写入文件。

const fs = require("fs")

fs.readFile("helloworld.txt", (err, data) => {
    if (err) {
        console.log(err)
    } else {
        console.log(data.toString())
    }
})

流式读写:适用于处理大量数据、高并发场景。例如,从网络中读取数据,或者将数据写入到网络中。在流式读写中,数据是按块读取或写入的,而不是一次性加载到内存中,这使得它能够处理大量数据,同时减少内存消耗。

const fs = require("fs")

const rs = fs.createReadStream("helloworld.txt")

rs.on('data', chunk => {
    console.log(chunk.toString())
})

3.3 fs 流式拷贝 和 普通拷贝

在Node.js中,文件系统(fs)模块提供了流式拷贝和普通拷贝两种方式。

普通拷贝(fs.readFile和fs.writeFile)是同步的,它们在执行期间会阻塞其他操作,直到整个文件被读取或写入完毕。这种方式适用于小型文件的拷贝,因为它相对简单且易于理解。但是,对于大型文件,普通拷贝可能会导致性能问题,因为它需要一次性将整个文件读入内存或写入磁盘。

流式拷贝(fs.createReadStream和fs.createWriteStream)则是异步的,它们使用了流(Stream)的概念。流是一种可以用于读取或写入数据的通道,它们可以以较小的数据块为单位进行读写,而不需要一次性读取整个文件。这种方式适用于大型文件的拷贝,因为它可以避免一次性读入整个文件而导致的内存占用问题。

流式拷贝

const fs = require('fs')
const process = require('process')

const rs = fs.createReadStream("assets/test.mp4")
const ws = fs.createWriteStream("test_new.mp4")

rs.on("data", chunk => {
    console.log(chunk)
    ws.write(chunk)
    // 65486 more bytes
})

rs.on('end', () => {
    console.log(process.memoryUsage())
})

process.memoryUsage() 是 Node.js 中的一个函数,用于返回当前 Node.js 进程的内存使用情况。这个函数返回一个对象,其中包含了 Node.js 进程使用的各种内存资源的使用情况。这些值可以帮助你了解你的 Node.js 进程的内存使用情况,以便于优化你的代码或诊断内存泄漏等问题。

返回的对象属性

objdetail
rssResident Set Size,这是进程在主内存中(即 RAM)占用的空间量,以字节为单位。
heapTotalV8 引擎已申请的堆内存总量,以字节为单位。
heapUsedV8 引擎已使用的堆内存量,以字节为单位。
external进程使用的外部内存,以字节为单位。
{
  rss: 20463616,	// 20463616 / 1024 / 1024 = 19 MB
  heapTotal: 4866048,
  heapUsed: 4144648,
  external: 281354,
  arrayBuffers: 11146
}

普通拷贝示例

const fs = require('fs');  
  
fs.readFile('source.txt', 'utf8', (err, data) => {  
  if (err) throw err;  
  
  fs.writeFile('destination.txt', data, (err) => {  
    if (err) throw err;  
    console.log('File has been saved!');  
  });  
});

3.4 fs stat() 状态信息

const fs = require("fs")

fs.stat("test_new.mp4", (err, stat) => {
    if (err) {
        console.log(err)
    } else {
    	console.log(stat)
        console.log(stat.isFile())
    }
})
obj methodreturn value type
stats.size文件大小,以字节为单位。
stats.mtime文件修改时间,是一个 Date 对象。
stats.ctime文件创建时间,是一个 Date 对象。
stats.atime文件访问时间,是一个 Date 对象。
stats.birthtime文件的出生时间,是一个 Date 对象(在某些系统上可能不可用)。
stats.uid文件的用户 ID。
stats.gid文件的组 ID。
stats.mode文件的权限模式。
stats.ino文件的 inode 号码。
stats.dev文件的设备号码。
stats.nlink文件的硬链接数量。
stats.isFile()如果这是一个文件,返回 true。
stats.isDirectory()如果这是一个目录,返回 true。
stats.isBlockDevice()如果这是一个块设备,返回 true。
stats.isCharacterDevice()如果这是一个字符设备,返回 true。
stats.isSymbolicLink()如果这是一个符号链接,返回 true(只在 Unix 系统中有效)。
stats.isFIFO()如果这是一个 FIFO(命名管道),返回 true。
stats.isSocket()如果这是一个套接字,返回 true。
Stats {
  dev: 810007100,
  mode: 33206,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  blksize: 4096,
  ino: 1407374883944336,
  size: 57077833,
  blocks: 111488,
  atimeMs: 1698839970416.1738,
  mtimeMs: 1698839970416.1738,
  ctimeMs: 1698839970416.1738,
  birthtimeMs: 1698839007835.6604,
  atime: 2023-11-01T11:59:30.416Z,
  mtime: 2023-11-01T11:59:30.416Z,
  ctime: 2023-11-01T11:59:30.416Z,
  birthtime: 2023-11-01T11:43:27.836Z
}

3.5 __dirname 执行路径

在Node.js中,__dirname是一个全局变量,表示当前正在执行的脚本所在的目录路径。它是一个字符串(String)类型的值,包含了当前脚本所在的目录路径。

这个变量在Node.js中非常有用,因为它可以帮助你在脚本中引用当前目录或子目录中的文件或模块。通过使用__dirname变量,你可以构建相对路径来引用其他文件或模块,或者执行一些与当前目录相关的操作。

console.log(__dirname)

4. http 模块网络编程

HTTP(Hypertext Transfer Protocol)是一种用于在网络上传输数据的协议,它定义了客户端与服务器之间的通信规则。HTTP协议基于请求/响应模型,客户端向服务器发送请求,服务器响应请求并返回数据。HTTP协议使用明文的方式传输内容,因此不适合传输敏感信息。HTTP协议的版本主要有HTTP/1.0和HTTP/1.1,其中HTTP/1.1是目前最常用的版本。

HTTP协议的特点是简单、灵活、无连接和无状态。简单是指协议的结构简单,易于理解和实现;灵活是指协议具有良好的扩展性,可以在不改变核心协议的情况下添加新的功能;无连接是指每次请求都需要建立连接,而无状态是指服务器不会为每个请求保持状态。

4.1 request 和 response

在计算机科学和网络编程中,request 和 response 是HTTP协议中的核心概念。它们分别表示客户端向服务器发送的请求和服务器对请求的响应。

HTTP请求(request)是由客户端(通常是Web浏览器)向服务器发送的,用于请求访问网页或资源。

方法(GETPOSTPUTDELETE等)
请求URL(资源的路径和名称)
请求头(header),包含关于请求的附加信息,如内容类型(Content-Type)、字符集(Charset)等
请求体(body),包含要发送的数据,如表单数据或JSON数据等

HTTP响应(response)是由服务器对客户端发送的请求进行响应的结果。

状态码(status code),表示请求的处理结果,如200表示成功,404表示未找到资源等
响应头(header),包含关于响应的附加信息,如内容类型(Content-Type)、字符集(Charset)等
响应体(body),包含服务器返回的数据,如网页内容、JSON数据等

在Web开发中,HTTP请求和响应是实现客户端与服务器之间通信的关键。通过发送HTTP请求来获取网页或资源,并接收HTTP响应来获取结果。

4.2 创建 http server

const http = require("http")

const server = http.createServer((request, response) => {
    console.log(request.method)
    response.setHeader("content-type", "text/html;charset=utf-8")
    response.end("唤醒手腕")
})

server.listen(9000, ()=> {
    console.log("server run")
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/112439.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows安装tensorflow-gpu=1.14.0CUDA=10.0cuDNN=7.4 (多版本CUDA共存)

文章目录 0. 前置说明1. 查看版本对应关系2. 安装 cuda3. 安装 cudnn4. 添加环境变量5. 安装 tensorflow 0. 前置说明 本机&#xff08;Windows 11&#xff09;已安装CUDA 11.7 使用命令查看显卡驱动&#xff1a; nvidia-smi这里显示的CUDA Version: 11.7说明支持安装11.7版本…

python hashlib模块及实例

hashlib 模块密码加密密码撞库密码加盐 一&#xff0c;hashlib模块 hashlib模块是用来为字符串进行加密的模块&#xff0c;通过该作用就可以为用户的密码进行加密。 通过模块中的hash算法可以为任意长度的字符串加密成长度相同的一串hash值。该hash算法得到的hash值有一下几个…

汽车配件商城小程序制作 | 汽车配件售卖,高门槛但高利润

通过汽车配件商城小程序给别人的供货&#xff0c;利润可高达60%&#xff0c;但甚少有人关注汽车配件销售的行业。具体情况是怎么样的呢&#xff0c;下面给大家简单分析。 据数据显示&#xff0c;国内有4亿多辆汽车&#xff0c;这些汽车坏了要修&#xff0c;也要偶尔进行保养&am…

python实现MC协议(SLMP 3E帧)的TCP服务端(篇一)

python实现MC协议&#xff08;SLMP 3E帧&#xff09;的TCP服务端是一件稍微麻烦点的事情。它不像modbusTCP那样&#xff0c;可以使用现成的pymodbus模块去实现。但是&#xff0c;我们可以根据协议帧进行组包&#xff0c;自己去实现帧的格式&#xff0c;而这一切可以基于socket模…

清华大模型GLM

2022年,清华大学发布了一款具有重要意义的 GLM 大模型,它不仅在中文语言处理方面取得了显著的进展,还在英文语言处理方面表现出了强大的能力。GLM大模型区别于OpenAI GPT在线大模型只能通过API方式获取在线支持的窘境,GLM大模型属于开源大模型,可以本地部署进行行业微调、…

金Gien乐道 | 10月热点回顾

收获之秋&#xff0c;中电金信Q4开篇捷报不断 Q4开篇&#xff0c;中电金信迎来多个捷报。公司与青岛财通集团联合打造的核心业务系统&#xff08;一体化业务平台&#xff09;一期项目顺利投产上线并平稳运行&#xff1b;中标华南某全国性股份制商业银行新一代云原生分布式核心系…

B-5:网络安全事件响应

B-5:网络安全事件响应 任务环境说明: 服务器场景:Server2216(开放链接) 用户名:root密码:123456 1.黑客通过网络攻入本地服务器,通过特殊手段在系统中建立了多个异常进程,找出启动异常进程的脚本,并将其绝对路径作为Flag值提交; 通过nmap扫描我们发现开启了22端口,…

mfc140u.dll丢失怎么修复,mfc140u.dll文件有什么作用

今天我想和大家分享的是关于mfc140u.dll文件丢失的解决方法。在我们使用电脑的过程中&#xff0c;有时候会遇到一些错误提示&#xff0c;其中比较常见的就是“无法找到mfc140u.dll文件”。那么&#xff0c;这个文件是什么呢&#xff1f;它有什么作用呢&#xff1f; 首先&#…

springboot读取application.properties中文乱码问题

目录 1 前言&#xff1a; 2 本地环境中的解决方案&#xff08;以idea为例&#xff09; 3 全部解决方案 1 前言&#xff1a; 初用properties,读取java properties文件的时候如果value是中文&#xff0c;会出现乱码的问题。我们首先需要明了乱码问题的根源。在 Java 中&#x…

HNU-计算机网络-实验1-应用协议与数据包分析实验(Wireshark)

计算机网络 课程基础实验一 应用协议与数据包分析实验(Wireshark) 计科210X 甘晴void 202108010XXX 一、实验目的&#xff1a; 通过本实验&#xff0c;熟练掌握Wireshark的操作和使用&#xff0c;学习对HTTP协议进行分析。 二、实验内容 2.1 HTTP 协议简介 HTTP 是超文本…

WPF RelativeSource属性-目标对象类型易错

上一篇转载了RelativeSource的三种用法&#xff0c;其中第二种用法较常见&#xff0c;这里记录一下项目中曾经发生错误的地方&#xff0c;以防自己哪天忘记了&#xff0c;又犯了同样错误—WPF RelativeSource属性-CSDN博客 先回顾一下&#xff1a; 控件关联其父级容器的属性—…

Window下coturn服务器的搭建

Window下搭建coturn服务器&#xff1a; 准备材料&#xff1a; 1、安装Cygwin&#xff0c;地址&#xff1a;https://cygwin.com/install.html 由于Window无法直接部署coturn&#xff0c;因此需要下载安装Cygwin在Window上部署Linux虚拟环境。 在安装的时候需要安装几下packe…

当贝PadGO闺蜜机?多的是你不知道的玩法

一、当贝PadGO性能强在哪? 1、金属机身 当贝PadGO独有CD型底盘更有设计风格、后扶手设计更稳,且采用全金属的材质更有质感。并且在配色上还有熊猫白和唱片黑两种可以选择。屏幕采用AG磨砂类纸屏,自带纸张柔和效果,防眩光。并且拥有德国莱茵低蓝光、无频闪双重护眼认证,还可以…

【C语法学习】3 - fgetc()函数

文章目录 1 函数原型2 参数3 返回值4 比较5 示例5.1 示例15.2 示例2 1 函数原型 fgetc()&#xff1a;从指定流stream中读取一个字符&#xff0c;函数原型如下&#xff1a; int fgetc(FILE *stream)2 参数 fgetc()函数只有一个参数stream&#xff1a; 参数stream是一个指向F…

SpringBoot_第七章(读写分离)

这里列举了三种读写分离实现方案,分别是如下三种 1&#xff1a;MybatisPlus&#xff08;读写分离&#xff09; 1.1&#xff1a;首先创建三个数据库1主2从 表名是user表 1.2&#xff1a;代码实例 1&#xff1a;导入pom <!--MybatisPlus的jar 3.0基于jdk8--><depend…

gRPC源码剖析-Builder模式

一、Builder模式 1、定义 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的的表示。 2、适用场景 当创建复杂对象的算法应独立于该对象的组成部分以及它们的装配方式时。 当构造过程必须允许被构造的对象有不同的表示时。 说人话&#xff1a…

sql-50练习题11-15

sql-50练习题11-15 前言数据库表结构介绍学生表课程表成绩表教师表 1-1 查询没有学全所有课程的同学的信息1-2 查询至少有一门课与学号为01的同学所学相同的同学的信息1-3 查询和1号的同学学习的课程完全相同的其他同学的信息1-4 查询没学过张三老师讲授的任一门课程的学生姓名…

【自用】vmware workstation建立主机window与虚拟机ubuntu之间的共享文件夹

1.在windows中建立1个文件夹 在vmware中设置为共享文件夹 参考博文&#xff1a; https://zhuanlan.zhihu.com/p/650638983 2.解决&#xff1a; &#xff08;1&#xff09;fuse: mountpoint is not empty &#xff08;2&#xff09;普通用户也能使用共享目录 参考博文&#x…

第06章 索引的数据结构

第06章 索引的数据结构 1. 索引及其优缺点 1.1 索引概述 MySQL官方对索引的定义为&#xff1a;索引&#xff08;Index&#xff09;是帮助MySQL高效获取数据的数据结构。 **索引的本质&#xff1a;**索引是数据结构。你可以简单理解为“排好序的快速查找数据结构”&#xff…

Swagger + DOCWAY 一步导出为优雅完整的Markdown、Pdf接口文档

只要开发&#xff0c;只要写接口应该没人不知道Swagger&#xff0c;但DOCWAY可能知道的人不多&#xff0c;但知道用过后就离不开了&#xff0c;不管是作为多方联调的接口文档&#xff0c;还是交接给客户的文档&#xff0c;都是可以的&#xff0c;具体如何使用&#xff0c;详细步…