文章目录
Node.JS
Node.js学习指南
- 服务端开发
- 底层平台
- 周边生态
学习前提
JavaScript、ES6
本文章主要是关于Node.js基础、Express、Koa2、MongoDB、MySQL、以及全栈项目和即时通讯和基于Mocha做测试,基于最新版V16.13和官网+周边生态,从而实现编写Restful Api接口和动态web网站的开发,即时通讯应用的开发
一、Node.js基础
1.认识Node.js
Node.js是一个javascript运行环境。它让javascript可以开发后端程序,实现几乎其他后端语言实现的所有功能,可以与PHP、Java、Python、.NET、Ruby等后端语言平起平坐。
Nodejs是基于V8引擎,V8是Google发布的开源JavaScript引擎,本身就是用于Chrome浏览器的js解释部分,但是Ryan Dahl 这哥们,鬼才般的,把这个V8搬到了服务器上,用于做服务器的软件。
nodejs的特性
- Nodejs语法完全是js语法,只要你懂js基础就可以学会Nodejs后端开发
- NodeJs超强的高并发能力,实现高性能服务器
- 开发周期短、开发成本低、学习成本低
游览器环境和node环境
Node.js 可以解析JS代码(没有浏览器安全级别的限制)提供很多系统级别的API
- 文件的读写 (File System)
- 进程的管理 (Process)
- 网络通信 (HTTP/HTTPS)
- …
2.开发环境搭建
建议下载长期稳定版本
node中文官网
node官网
下载完成,如果安装不了,看是否是以下原因
安装步骤是傻瓜式的
查看是否有node环境(在任意文件路径下打开终端或Powershell)
node -v
接下来在node环境下运行一下js代码
3. 模块、包、commonJS
3.1、为什么要有模块化开发?
3.2、CommonJS规范
3.3、 modules模块化规范写法
我们可以把公共的功能 抽离成为一个单独的 js 文件 作为一个模块,默认情况下面这个模块里面的方法或者属性,外面是没法访问的。如果要让外部可以访问模块里面的方法或者属性,就必须在模块里面通过 exports 或者 module.exports 暴露属性或者方法。
体验一下
m1.js
function _print(){
console.log('print-m1')
}
module.exports=_print
m2.js
function _print(){
console.log('print-m2')
}
module.exports=_print
m3.js(测试暴露多个)
function _print(){
console.log('1暴露多个:print-m3')
}
function _print2(){
console.log('2暴露多个:print-m3')
}
module.exports={
_print,
_print2
}
// 或
// exports._print=_print
// exports._print2=_print2
index.js(入口文件)`
// 入口文件
var module_m1=require(`./m1`)//导入m1
var module_m2=require(`./m2`)//导入m2
var module_m3=require(`./m3`)//导入m3
//调用
module_m1()
module_m2()
// 调用暴露多个中的其中某个方法
module_m3._print()
module_m3._print2()
效果
4.Npm&Yarn
4.1、npm使用
使用npm安装第三方库
npm init
npm install 包名 –g (uninstall,update)
npm install 包名 --save-dev (uninstall,update)
npm list -g (不加-g,列举当前目录下的安装包)
npm info 包名(详细信息) npm info 包名 version(获取最新版本)
npm install md5@1(安装指定版本)
npm outdated( 检查包是否已经过时)
"dependencies": { "md5": "^2.1.0" } ^ 表示 如果 直接npm install 将会 安md5
2.*.* 最新版本
"dependencies": { "md5": "~2.1.0" } ~ 表示 如果 直接npm install 将会 安装
md5 2.1.* 最新版本
"dependencies": { "md5": "*" } * 表示 如果 直接npm install 将会 安装 md5
最新版本
- 在一个项目文件夹下创建一个记录包名和下载安装第三方包信息的文件
这样就会根据配置生成一个package.json文件
package.json
{
"name": "test",
"version": "1.0.0",
"description": "ceshi",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "admin",
"license": "MIT"
}
- 下载第三方库(包)
npm install 包名
npm i 包名
可以看到package.json文件增加了一条记录
{
"name": "test",
"version": "1.0.0",
"description": "ceshi",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "admin",
"license": "MIT",
"dependencies": {
"md5": "^2.3.0"//新记录
}
}
node_modules依赖包文件夹
package-lock.json文件是为了锁定某个文件具体的版本,防止由于我们依赖版本更新导致我们新版本出现问题
- 列举
npm list -g (不加-g,列举当前目录下的安装包)
- 查看包详细信息
npm info 包名(详细信息) npm info 包名 version(获取最新版本)
- 检查包是否已经过时
npm outdated( 检查包是否已经过时)
最新版本
npm install md5@1(安装旧版本)
npm outdated md5
过期版本,已经有最新版本
4.2、全局安装nrm
NRM (npm registry manager)是npm的镜像源管理工具,有时候国外资源太慢,使用这个就可以快速地在 npm 源间切换
手动切换方法: npm config set registry https://registry.npm.taobao.org
- 安装 nrm
在命令行执行命令,npm install -g nrm,全局安装
,npm install -g nrm
- 使用 nrm
执行命令 nrm ls 查看可选的源。 其中,带*的是当前使用的源,上面的输出表明当前源是官方源
nrm ls
- 切换 nrm
如果要切换到taobao源,执行命令nrm use taobao。
nrm use taobao
nrm ls 查看可选的源,可以看到已经切换到了taobao
- 测试速度
nrm test
- 扩展:
npm install -g cnpm --registry=https://registry.npmmirror.com
4.3、yarn使用
- 安装
npm install -g yarn
- 命令
对比npm:
速度超快: Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化
资源利用率,因此安装速度更快。
超级安全: 在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。
开始新项目
yarn init
添加依赖包
yarn add [package]
yarn add [package]@[version]
yarn add [package] --dev
升级依赖包
yarn upgrade [package]@[version]
移除依赖包
yarn remove [package]
安装项目的全部依赖
yarn install
- 初始化(开始新项目)
yarn init
和npm步骤一直
4. 添加/下载安装依赖包
yarn add [package]
- 删除依赖包
yarn remove [package]
检查一下,可以看到已经删除md5这个依赖包
5.ES模块化写法
通过上面的npm init初始化创建一个package.json
npm init
//接下来按照步骤填写信息即可
在package.json写入"type": “module”,这样就是模块化了,默认是common
{
"name": "aaa",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "s",
"license": "ISC"
}
moduleA.js
const moduleA={
getNama(){
return 'my name is moduleA'
}
}
export default moduleA
moduleB.js
const moduleB={
getNama(){
return 'my name is moduleB'
}
}
export {
moduleB
}
index.js
import moduleA from "./module/moduleA.js";
import {moduleB} from "./module/moduleB.js";
console.log(moduleA.getNama())
console.log(moduleB.getNama())
6. 内置模块
6.1 http模块
要使用 HTTP 服务器和客户端,则必须 require(‘http’)
Server.js
var http=require("http")
//引入http模块
// 创建服务器
http.createServer((req,res)=>{
// req 接受游览器传递的参数
// res 返回渲染的内容
res.write("hello http") //往游览器写入
res.end()//结束
}).listen(2180,()=>{
console.log("server start")
})
Server.js
var http=require("http")
//引入http模块
// 创建服务器
http.createServer((req,res)=>{
// req 接受游览器传递的参数
// res 返回渲染的内容
// res.write("hello http") //往游览器写入
res.end("[1,2,3]")//结束
}).listen(2180,()=>{
console.log("server start")
})
这样访问就会返回一个json格式的
var http = require("http");
//引入http模块
// 创建服务器
http
.createServer((req, res) => {
// req 接受游览器传递的参数
// res 返回渲染的内容
// res.write("hello http") //往游览器写入
// res.end("[1,2,3]")//结束
res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" });
res.write(`
<html>
<h1>hello h1 </h1>
<h1>你好 一级标题 </h1>
<html>
`);
res.end();
})
.listen(2180, () => {
console.log("server start");
});
也能返回标签
根据访问的url处理对应的返回信息
server.js
var http = require("http");
//引入http模块
// 创建服务器
http
.createServer((req, res) => {
// req 接受游览器传递的参数
// res 返回渲染的内容
// res.write("hello http") //往游览器写入
// res.end("[1,2,3]")//结束
// 根据访问的url设置对应的响应头
res.writeHead(renderStatus(req.url), {
"Content-Type": "text/html;charset=utf-8",
});
console.log(req.url);
// 根据访问的url设置对应的返回信息
res.write(renderHTML(req.url));
res.end();
})
.listen(2180, () => {
console.log("server start");
});
// 根据访问的url设置对应的响应头Status
function renderStatus(url) {
var urlArr = ["/home", "/api/home","/list","/api/list"];
return urlArr.includes(url) ? 200 : 404;
}
// 根据访问的url设置对应的返回信息
function renderHTML(url) {
switch (url) {
// 返回h5
case "/home":
return `
<html>
<h1>/home访问返回 </h1>
<html>
`;
case "/list":
return `
<html>
<h1>/list访问返回 </h1>
<html>
`;
// 返回数据
case "/api/home":
return `
["home1","home2","home3"]
`;
// 返回json
case "/api/list":
return `
{name:'列表数据1'}
`;
// 处理404
default:
return `
<html>
<h1>404 not found</h1>
<html>
`;
}
}
页面
数据
json
状态码
对server.js代码进行拆分,使用模块化开发
renderHTML.js
// 根据访问的url设置对应的返回信息
function renderHTML(url) {
switch (url) {
// 返回h5
case "/home":
return `
<html>
<h1>/home访问返回 </h1>
<html>
`;
case "/list":
return `
<html>
<h1>/list访问返回 </h1>
<html>
`;
// 返回数据
case "/api/home":
return `
["home1","home2","home3"]
`;
// 返回json
case "/api/list":
return `
{name:'列表数据1'}
`;
// 处理404
default:
return `
<html>
<h1>404 not found</h1>
<html>
`;
}
}
module.exports={
renderHTML
}
renderStatus.js
// 根据访问的url设置对应的响应头Status
function renderStatus(url) {
var urlArr = ["/home", "/list","/api/list"];
return urlArr.includes(url) ? 200 : 404;
}
exports.renderStatus=renderStatus
在server.js引入使用
server.js
var http = require("http");
//引入http模块
var moduleRenderHTML=require("./module/renderHTML")
var moduleRenderStatus=require("./module/renderStatus")
// 创建服务器
http
.createServer((req, res) => {
// req 接受游览器传递的参数
// res 返回渲染的内容
// res.write("hello http") //往游览器写入
// res.end("[1,2,3]")//结束
// 根据访问的url设置对应的响应头
res.writeHead(moduleRenderStatus.renderStatus(req.url), {
"Content-Type": "text/html;charset=utf-8",
});
console.log(req.url);
// 根据访问的url设置对应的返回信息
res.write(moduleRenderHTML.renderHTML(req.url));
res.end();
})
.listen(2180, () => {
console.log("server start");
});
启动一下,然后效果和未拆分前是一样的
6.2 小插件(nodemon/node-dev自动重启服务器)
另外介绍俩个自动重启服务器的插件,类似与热刷新(热部署),代码保存后,会自动启动服务器,这俩个都可以实现
npm i -g nodemon //全局安装nodemon
nodemon 文件 //启动
npm i -g node-dev//全局安装node-dev
node-dev 文件 //启动
6.3 url模块
6.3.1 parse
根据上面http模块的使用,会遇到一个问题,就是我们通过路径传参,这个地址就不匹配了,为了解决这一问题,下面讲解一下url模块,它可以帮助我们对这种情况进行解析,把路径取出来,再判断即可
server.js
var http = require("http");
//引入http模块
var url=require('url')
// 引入url模块
var moduleRenderHTML=require("./module/renderHTML")
var moduleRenderStatus=require("./module/renderStatus")
// 创建服务器
http
.createServer((req, res) => {
// req 接受游览器传递的参数
// res 返回渲染的内容
// res.write("hello http") //往游览器写入
// res.end("[1,2,3]")//结束
// 根据访问的url设置对应的响应头
res.writeHead(moduleRenderStatus.renderStatus(req.url), {
"Content-Type": "text/html;charset=utf-8",
});
console.log(url.parse(req.url));
// url.parse解析
// 根据访问的url设置对应的返回信息
res.write(moduleRenderHTML.renderHTML(req.url));
res.end();
})
.listen(2180, () => {
console.log("server start");
});
在访问刚才的路径,会得到一个对象,可以看到对象里有个pthname属性,这样就可以提取出来我们的url
取到之后对上方代码传递参数进行一下修改
var http = require("http");
//引入http模块
var url=require('url')
// 引入url模块
var moduleRenderHTML=require("./module/renderHTML")
var moduleRenderStatus=require("./module/renderStatus")
// 创建服务器
http
.createServer((req, res) => {
// req 接受游览器传递的参数
// res 返回渲染的内容
// res.write("hello http") //往游览器写入
// res.end("[1,2,3]")//结束
// 根据访问的url设置对应的响应头
console.log(url.parse(req.url));
var pathname= url.parse(req.url).pathname
// 取到我们的url(路径),改变一下下方传递的参数
res.writeHead(moduleRenderStatus.renderStatus(pathname), {
"Content-Type": "text/html;charset=utf-8",
});
// 根据访问的url设置对应的返回信息
res.write(moduleRenderHTML.renderHTML(pathname));
res.end();
})
.listen(2180, () => {
console.log("server start");
});
这样就可以正常的访问了
上面是url模块的一个提取路径的一个小用法,那么接下来就通过这个url模块处理一下传递过来的参数!
url.parse的第二个参数设为true的话,会返回我们一个对象,这样我们就可以通过对象属性的方式拿到这个参数
// 根据访问的url设置对应的响应头
var urlObj=url.parse(req.url,true)
console.log(urlObj);
可以通过urlObj.query获取对象 ,urlObj.属性名获取对象对应属性的属性值
6.3.2 format
上面的parse方法可以将我们的地址转换成一个对象,同样format方法也可以将我们对象转换为url(地址)
// 例如这是一个转换后的对象
const urlObject = {
protocol: "https:",
slashes: true,
auth: null,
host: "www.baidu.com:443",
port: "443",
hostname: "www.baidu.com",
hash: "#tag=110",
search: "?id=8&name=mouse",
query: { id: "8", name: "mouse" },
pathname: "/ad/index.html",
path: "/ad/index.html?id=8&name=mouse",
};
const parsedObj = url.format(urlObject);
console.log(parsedObj);
6.3.3 resolve
resolve可以处理url,可拼接可替换
var a = url.resolve('/one/two/three', 'four') ( 注意最后加/ ,不加/的区别 )
var b = url.resolve('http://example.com/', '/one')
var c = url.resolve('http://example.com/one', '/two')
console.log(a + "," + b + "," + c)
6.4 url模块新版用法
nodejs api文档
可以看到上方我们学习的是一些旧版api的用法,那么接下来我们来学习一下新版的用法
var http = require("http");
//引入http模块
var url = require("url");
// 引入url模块
var moduleRenderHTML = require("./module/renderHTML");
var moduleRenderStatus = require("./module/renderStatus");
// 创建服务器
http
.createServer((req, res) => {
// req 接受游览器传递的参数
// res 返回渲染的内容
// res.write("hello http") //往游览器写入
// res.end("[1,2,3]")//结束
// 根据访问的url设置对应的响应头
// 旧版api
// var urlObj = url.parse(req.url, true);
// console.log(urlObj);
// var pathname = url.parse(req.url).pathname;
// 新版api
const myURL=new URL(req.url,'http://127.0.0.1:2180')
console.log(myURL)
let pathname=myURL.pathname
// 取到我们的url(路径),改变一下下方传递的参数
res.writeHead(moduleRenderStatus.renderStatus(pathname), {
"Content-Type": "text/html;charset=utf-8",
});
// 根据访问的url设置对应的返回信息
res.write(moduleRenderHTML.renderHTML(pathname));
res.end();
})
.listen(2180, () => {
console.log("server start");
});
可以看到只是使用方法的api改动了,其他还是一样
6.4.1 searchParams
// 新版api
const myURL=new URL(req.url,'http://127.0.0.1:2180')
console.log(myURL)
for(var [key,val] of myURL.searchParams){
console.log(key,val)
}
其他方法可参考官方api文档
6.5 querystring模块
6.5.1 parse
可以将前端传递的参数从字符串转换为一个对象
var str ='name=ZhangSan&age=23'
// 假如str参数是我们接收前端传递过来的参数,接下来我们将这个解析为对象结构
var querystring =require('querystring')
// 引入querystring内置模块
var obj=querystring.parse(str)
console.log(obj)
6.5.2 stringify
同样querystring也有一个stringify方法,可以将一个对象转换为url参数
var myObj={ name: 'ZhangSan', age: '23' }
// 接下来我们反过来,将一个对象转换为url参数
var mystr=querystring.stringify(myObj)
console.log(mystr)
6.5.3 escape/unescape
转义替换,用于处理特殊符号字符等,unescape用来解码的
escape使用
const querystring = require('querystring')
var str1 = 'id=3&city=北京&url=https://www.baidu.com'
var escaped = querystring.escape(str1)
console.log(escaped)
可以看到字符串的特殊字符已经被编码了
unescape解码
const querystring = require('querystring')
var str2 =
'id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fwww.baidu.com'
var unescaped = querystring.unescape(str2)
console.log(unescaped)
6.6 http模块补充
6.6.1 接口:jsonp
jsonp.js
var http = require("http");
//引入http模块
var url=require('url')
// 创建服务器
http
.createServer((req, res) => {
var urlobj=url.parse(req.url)
switch(urlobj.pathname){
case '/api/ab':
res.end(`jsonp_fun(${
JSON.stringify({
name:'zhangsan',
age:'24'
})
// 返回一个jsonp
})`);
break
default:
res.end('404')
}
})
.listen(2180, () => {
console.log("server start");
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>jsonp接口调用</title>
</head>
<body>
<script>
// jsonp接口调用
var o_script=document.createElement("script")
o_script.src="http://localhost:2180/api/ab"
document.body.appendChild(o_script)
// 定义一个和我们node返回的jsonp方法同名函数
function jsonp_fun(obj){
console.log(obj)
}
</script>
</body>
</html>
利用live server运行一下我们的index.html
改变动态
var http = require("http");
//引入http模块
var url=require('url')
// 创建服务器
http
.createServer((req, res) => {
var urlobj=url.parse(req.url,true)
console.log(urlobj.query.callback)
switch(urlobj.pathname){
case '/api/ab':
res.end(`${urlobj.query.callback}(${
JSON.stringify({
name:'zhangsan',
age:'24'
})
// 返回一个jsonp
})`);
break
default:
res.end('404')
}
})
.listen(2180, () => {
console.log("server start");
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>jsonp接口调用</title>
</head>
<body>
<script>
// jsonp接口调用
var o_script=document.createElement("script")
o_script.src="http://localhost:2180/api/ab?callback=test"
document.body.appendChild(o_script)
// 定义一个和我们node返回的jsonp方法同名函数
function test(obj){
console.log(obj)
}
</script>
</body>
</html>
6.6.2 跨域:CORS
处理跨域
var http = require("http");
//引入http模块
var url=require('url')
// 创建服务器
http
.createServer((req, res) => {
var urlobj=url.parse(req.url,true)
console.log(urlobj.query.callback)
res.writeHead(200, {
'content-type': 'application/json;charset=utf-8',
// core头 *允许所有域请求
'Access-Control-Allow-Origin': '*'
})
switch(urlobj.pathname){
case '/api/ab':
res.end(JSON.stringify({
name:'zhangsan',
age:24
}))
// 返回一个json数据
;
break
default:
res.end('404')
}
})
.listen(2180, () => {
console.log("server start");
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
fetch(`http://localhost:2180/api/ab`).then(res=>res.json()).then(res=>{
console.log(res)
})
</script>
</body>
</html>
6.6.3 模拟get
node可以做服务端为前端提供代码html片段、返回json接口、返回jsonp接口、同样也可以做客户端去请求其他地址的接口,聚合再返回给前端等,也可以理解为中间层,相当于网关、中间件,也叫转发
为了更好的模拟真实的项目环境,这样我们以猫眼电影信息做个案例,由于前端直接请求会产生跨域,那么我们通过node做转发再返回给前端
node转发代码
var http = require("http");
//引入http模块
var https = require("https");
//引入https模块
var url=require('url')
// 创建服务器
http
.createServer((req, res) => {
var urlobj=url.parse(req.url,true)
console.log(urlobj.query.callback)
res.writeHead(200, {
'content-type': 'application/json;charset=utf-8',
// core头 *允许所有域请求
'Access-Control-Allow-Origin': '*'
})
switch(urlobj.pathname){
case '/api/ab':
// 作为客户端,去猫眼要数据(转发)
http_get(res)//把res对象传过去
break
default:
res.end('404')
}
})
.listen(2180, () => {
console.log("server start");
});
// get转发方法
function http_get(response) {
// 注意,由于转发的是https协议的,所以上方引入了https模块
var data=""
https.get(`https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=中山&ci=113&channelId=4`,(res)=>{
res.on("data",(chunk)=>{
// 以数据流的方式
data+=chunk
})
res.on("end",()=>{
console.log(data)
// 完整的返回
response.end(data)
// 返回给前端
})
})
}
前端页面以及请求后端返回数据的地址
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script> fetch(`http://localhost:2180/api/ab`).then(res=>res.json()).then(res=>{
console.log(res)
})
</script>
</body>
</html>
6.6.4 模拟post:服务器提交
上面使用了get做转发,这次我们使用node作为客户端post方式做转发,获取到数据然后返回给前端,下面我们以小米优品为案例
node做post转发代码
var http = require("http");
//引入http模块
var https = require("https");
//引入https模块
var url=require('url')
// 创建服务器
http
.createServer((req, res) => {
var urlobj=url.parse(req.url,true)
console.log(urlobj.query.callback)
res.writeHead(200, {
'content-type': 'application/json;charset=utf-8',
// core头 *允许所有域请求
'Access-Control-Allow-Origin': '*'
})
switch(urlobj.pathname){
case '/api/ab':
// 作为客户端,去小米要数据(转发)
http_post(res)//把res对象传过去
break
default:
res.end('404')
}
})
.listen(2180, () => {
console.log("server start");
});
// get转发方法
function http_post(response) {
// 注意,由于转发的是https协议的,所以上方引入了https模块
var data=""
let option={
hostname:'m.xiaomiyoupin.com',//域名
port:"443",//端口
path:'https://www.xiaomiyoupin.com/mtop/market/search/placeHolder',//路径
method:"POST",//请求方式
// 注意传递的参数格式是json还是url
headers:{
"Content-Type": "application/json"
// 'Content-Type': 'application/x-www-form-urlencoded',
}
}
var req= https.request(option,(res)=>{
res.on("data",(chunk)=>{
// 以数据流的方式
data+=chunk
})
res.on("end",()=>{
console.log(data)
// 完整的返回
response.end(data)
// 返回给前端
})
})
// 传递的参数
req.write(JSON.stringify([{},{"ypClient":3}]))//json格式的
// req.write("name=zhangsan&age=21")//'Content-Type': 'application/x-www-form-urlencoded',
req.end()
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 获取数据
fetch(`http://localhost:2180/api/ab`).then(res=>res.json()).then(res=>{
console.log(res)
})
</script>
</body>
</html>
持续更新中
总结
如果这篇【文章】有帮助到你💖,希望可以给我点个赞👍,创作不易,如果有对前端端或者对python感兴趣的朋友,请多多关注💖💖💖,咱们一起探讨和努力!!!
👨🔧 个人主页 : 前端初见