Dg's Blog-专注健康快乐每一天 - 鼎g
type: channel
link: https://zhougewk8.cn/author/1/
description: 鼎g
items: React项目中tsconfig.json详细配置
items: ESLint配置项.eslintrc文件详细配置
items: 网易云音乐、bilibili、微信、支付宝运动步数-云任务助手
items: 实现图片懒加载基于Intersection Observer Api
items: 简述TCP三次握手和四次挥手面试回答
items: 前端-基础面试题汇总(含Vue、React)
items: 如花美眷也敌不过似水流年
items: 基于React在TypeScript环境下路由前置守卫封装
items: React、Vue项目中Token无感刷新原理-实用大多数项目
items: 封装属于自己的ajax请求-2021/11/14

React项目中tsconfig.json详细配置
type: item
date: 2023-02-08T00:07:06+08:00
description: tsconfig.json配置项详解compilerOptions: 编译器的选项，如语言版本、目标 JavaScript 版本、生成的 sourcemap 等。include: 指定需要编译的文件路径或文件夹路径。exclude: 指定不需要编译的文件路径或文件夹路径。files: 指定需要编译的文件列表。extends: 指定继承自另一个 tsconfig.json 文件。compileOnSave : 指定是否在保存时编译文件。buildOnSave: 指定是否在保存时编译文件。target：编译目标 JavaScript 版本，可以是 “ES3”，“ES5” 或 “ES2015” 等。module：指定模块系统，可以是 “CommonJS”，“AMD” 或 “System” 等。sourceMap：是否生成 sourcemap 文件。outDir：编译输出目录。rootDir：设置项目的根目录。strict：是否开启严格类型检查。noImplicitAny：是否禁止隐式 any 类型。lib：指定要包含在编译中的库文件，如 “es2015”。paths: 指定模块路径别名。baseUrl: 指定基础目录。jsx: 指定 JSX 的处理方式。allowJs: 是否允许编译 JavaScript 文件。checkJs: 是否检查 JavaScript 文件。declaration: 是否生成声明文件。declarationMap: 是否生成声明文件的 sourcemap。emitDecoratorMetadata: 是否支持装饰器。experimentalDecorators: 是否支持实验性装饰器。listEmittedFiles: 是否列出所有输出的文件。listFiles: 是否列出所有编译过的文件。locale: 指定本地化语言。mapRoot: 指定 sourcemap 文件的根目录。moduleResolution: 指定模块解析策略。noEmit: 是否禁止输出 JavaScript 代码。noEmitHelpers: 是否禁止输出辅助函数。noEmitOnError: 是否在发生错误时禁止输出 JavaScript 代码。noImplicitReturns: 是否禁止隐式返回。noUnusedLocals: 是否检查未使用的局部变量。noUnusedParameters: 是否检查未使用的参数。preserveConstEnums: 是否保留 const 枚举。pretty: 是否格式化输出的 JavaScript 代码。removeComments: 是否移除注释。skipLibCheck: 是否跳过检查库文件。sourceRoot: 指定源文件的根目录。suppressExcessPropertyErrors: 是否禁止过多属性错误。suppressImplicitAnyIndexErrors: 是否禁止隐式 any 类型索引错误。typeRoots: 指定类型声明文件的根目录。types: 指定需要包含在编译中的类型声明文件。watch: 是否监视文件变化并重新编译。里面包含了一些常用的tsconfig选项以及注解：{ "compilerOptions": { "allowUnreachableCode": true, // 不报告执行不到的代码错误。 "allowUnusedLabels": false, // 不报告未使用的标签错误 "alwaysStrict": false, // 以严格模式解析并为每个源文件生成 "use strict"语句 "baseUrl": ".", // 工作根目录 "experimentalDecorators": true, // 启用实验性的ES装饰器 "jsx": "react", // 在 .tsx文件里支持JSX "sourceMap": true, // 是否生成map文件 "module": "commonjs", // 指定生成哪个模块系统代码 "noImplicitAny": false, // 是否默认禁用 any "removeComments": true, // 是否移除注释 "types": [ //指定引入的类型声明文件，默认是自动引入所有声明文件，一旦指定该选项，则会禁用自动引入，改为只引入指定的类型声明文件，如果指定空数组[]则不引用任何文件 "node", // 引入 node 的类型声明 ], "paths": { // 指定模块的路径，和baseUrl有关联，和webpack中resolve.alias配置一样 "src": [ //指定后可以在文件之直接 import * from 'src'; "./src" ], }, …

ESLint配置项.eslintrc文件详细配置
type: item
date: 2023-02-07T17:06:00+08:00
description: ESLint 配置文件关于 ESLint 介绍，以及在项目中如何配置，请参考WebStorm + React 项目，配置 ESLint。配置文件ESLint 支持几种格式的配置文件，如果同一个目录下有多个配置文件，ESLint 只会使用一个。优先级顺序如下：JavaScript - 使用 .eslintrc.js 然后输出一个配置对象。YAML - 使用 .eslintrc.yaml 或 .eslintrc.yml 去定义配置的结构。JSON -使用 .eslintrc.json 去定义配置的结构，ESLint 的 JSON 文件允许 JavaScript 风格的注释。Deprecated -使用 .eslintrc，可以使 JSON 也可以是 YAML。package.json - 在 package.json 里创建一个 eslintConfig 属性，在那里定义你的配置。配置规则格式规则格式是<规则名称>: <告警级别>，告警级别分为三种:"0"表示忽略问题，等同于"off";"1"表示给出警告，等同于"warn";"2"表示直接报错，等同于"error"。详细规则说明/** * AlloyTeam ESLint 规则 - React * * 包含所有 ESLint 规则，以及所有 eslint-plugin-react 规则 * 使用 babel-eslint 作为解析器 * * @fixable 表示此配置支持 --fix * @off 表示此配置被关闭了，并且后面说明了关闭的原因 */ module.exports = { extends: [ './index.js', ], plugins: [ 'react' ], rules: { // 布尔值类型的 propTypes 的 name 必须为 is 或 has 开头 // @off 不强制要求写 propTypes 'react/boolean-prop-naming': 'off', // 一个 defaultProps 必须有对应的 propTypes // @off 不强制要求写 propTypes 'react/default-props-match-prop-types': 'off', // 组件必须有 displayName 属性 // @off 不强制要求写 displayName 'react/display-name': 'off', // 禁止在自定义组件中使用一些指定的 props // @off 没必要限制 'react/forbid-component-props': 'off', // 禁止使用一些指定的 elements // @off 没必要限制 'react/forbid-elements': 'off', // 禁止使用一些指定的 propTypes // @off 不强制要求写 propTypes 'react/forbid-prop-types': 'off', // 禁止直接使用别的组建的 propTypes // @off 不强制要求写 propTypes 'react/forbid-foreign-prop-types': 'off', // 禁止使用数组的 index 作为 key // @off 太严格了 'react/no-array-index-key': 'off', // 禁止使用 children 做 props 'react/no-children-prop': 'error', // 禁止使用 dangerouslySetInnerHTML // @off 没必要限制 'react/no-danger': 'off', // 禁止在使用了 dangerouslySetInnerHTML 的组建内添加 children 'react/no-danger-with-children': 'error', // 禁止使用已废弃的 api 'react/no-deprecated': 'error', // 禁止在 componentDidMount 里面使用 setState // @off 同构应用需要在 didMount 里写 setState 'react/no-did-mount-set-state': 'off', // 禁止在 componentDidUpdate 里面使用 setState 'react/no-did-update-set-state': 'error', // 禁止直接修改 this.state 'react/no-direct-mutation-state': 'error', // 禁止使用 findDOMNode 'react/no-find-dom-node'…

网易云音乐、bilibili、微信、支付宝运动步数-云任务助手
type: item
date: 2022-05-14T16:46:00+08:00
description: [scode type="green"] ::aru:proud:: 助手介绍[/scode]网上很多代挂助手挺好玩的，但是又觉得用别人的不习惯，那只能为理想买单啦，于是花了一周的饭钱188 买了oneTool助手的授权自己搭建了一下，虽然api还使用的别人的 但是最起码有了心理安慰 文章最后有惊喜登录地址：http://test.97dk.top开箱即用、不用付费直接白嫖还不快冲！vip卡密每人限1个哈,有肉大家一起吃！[scode type="green"] ::aru:frown:: 功能介绍[/scode][tabs][tab name="网易云音乐" active="true"]功能：每日签到，每日云贝，打卡300首歌，音乐人任务-可以领取一年黑胶会员[/tab][tab name="哔哩哔哩"]功能：每日礼包，每日观看，每日分享，漫画任务等等各种功能[/tab][tab name="运动步数"]功能：微信运动、支付宝运动、可以指定部署范围（不用登录微信或者支付宝）[/tab][tab name="爱奇艺"]功能：每日签到，每日三抽，积分任务、VIP日常任务、VIP其他任务[/tab][tab name="QQ空间"]功能：自动点赞，自动发说说，自动评论，腾讯视频自动签到[/tab][/tabs][scode type="blue"] ::aru:shy2:: 免费卡密[/scode]

实现图片懒加载基于Intersection Observer Api
type: item
date: 2022-05-06T21:40:00+08:00
description: 1.为什么使用图片懒加载？为了解决 一进入页面，所有的图片都会一次性加载，这可能会造成网络资源和降低用户的体验。比如某宝App打开之后会加载很多商品预览图片,每个图片都是一个请求,那么刚进入页面就发很多请求会造成性能的浪费，从用户的角度来看会给用户一种卡顿的感觉极其影响购物体验所以使用 图片懒加载，只有当用户滚动页面，真正的浏览的到这些列表项 ，才开始加载那些图片2.原理3.原生Api代码实现[scode type="green"]Intersection ObserverMDN官方文档[/scode]利用浏览器提供的 intersectionObserverApi,监听图片元素是否进入到可视区域，进入之后才真正的去设置图片元素的 src属性进行图片加载。还额外针对图片加载是否出错做了处理。如果图片加载出错，显示约定好的图片。图片不直接设置src，通过data-src属性设置图片地址,直接设置一进页面就会加载当图片进入可视区后在进行加载通过IntersectionObserver监听进入可视区域如果进入可视区域后将data-src中存储的地址赋值给src原生demo： </bodyReact封装组件：type Props = { src: string className?: string } /** * 拥有懒加载特性的图片组件 * @param {String} props.src 图片地址 * @param {String} props.className 样式类 */ const Image = ({ src, className }: Props) => { // 记录图片加载是否出错的状态 const [isError, setIsError] = useState(false) // 记录图片是否正在加载的状态 const [isLoading, setIsLoading] = useState(true) // 对图片元素的引用 类型设置 const imgRef = useRef<HTMLImageElement | null>(null) // 组件收到src后 先储存起来 // 建立监听 useEffect(() => { const io = new IntersectionObserver(([{ isIntersecting, target }]) => { // 如果isIntersecting = true 代表进入了可视区域 if (isIntersecting) { // 获取Dom元素 ! 非空断言 const imgDom = imgRef.current! // 进入之后将data-src的值赋值给src imgDom.src = imgDom.dataset.src! // 设置之后结束特定监听 io.unobserve(imgDom) } //组件卸载时 触发全部停止监听 return () => { io.disconnect() } }) // 开启监听 io.observe(imgRef.current!) }) return ( {/* 正在加载时显示的内容 */} {isLoading && ( <Icon type="iconphoto" /> )} {/* 加载出错时显示的内容 */} {isError && ( <Icon type="iconphoto-fail" /> )} {/* 加载成功时显示的内容 */} {!isError && ( setIsLoading(false)} onError={() => setIsError(true)} /> )} ) }

简述TCP三次握手和四次挥手面试回答
type: item
date: 2022-05-03T16:55:00+08:00
description: 简述TCP三次握手和四次挥手面试回答[scode type="green"]本文转自 猿人谷 长期维护版本：https://yuanrengu.com/2020/77eef79f.html[/scode][scode type="lblue"]目前把下面总结好的理解并且牢记完全可以应对面试官。[/scode]三次握手四次挥手是前端常见面试题之一，也被一些面试官作为热身题。很多小伙伴说这个问题刚开始回答的挺好，但是后面越回答越冒冷汗，最后就歇菜了。希望大家带着如下问题进行阅读，收获会更大。1.请说出三次握手的作用以及过程2.ISN(Initial Sequence Number)是固定的吗？3.三次握手过程中可以携带数据吗？4.什么是半连接队列？5.三次握手可以携带参数吗？6.四次挥手的过程首先看一张图来理解一下整个三次握手过程一、三次握手1.三次握手的作用作用：确定双方的接收、发送能力是否正常，指定自己的初始化序列号，为后续可靠传输做准备1.2 三次握手的过程刚开客户端处于Closed状态，服务端处于Listen状态第一次握手：客户端向服务端发送SYN报文，并指明客户端初始化序列号ISN(c),此时客户端进入SYN_send状态/等待第二次握手：服务端收到SYN报文后，会以自己的SYN作为应答，并指明自己的初始化序列号ISN，同时将客户端的ISN+1作为Ack的值，表示收到客户端的SYN，同时进入SYN_RCVD状态/接收第三次：客户端收到SYN后，发送一个ACK报文，也是将服务端的ISN+1作为ACK的值，表示收到服务端的SYN报文。此时客户端处于established状态，服务端收到ACK后，也处于established状态，此时双方成功建立连接2.ISN是固定的吗？答案：不是固定的，三次握手的一个重要功能就是交换双方的ISN，以便让对方知道接收到的输入如何按序列号进行组装，如果ISN是固定的，攻击者很容易推断出后续的确认号，所以ISN是动态生成的3.什么是半连接队列？服务端第一次收到SYN后，就会处于SYN_RCVD,此时双方还没完全建立连接，服务端会把这种状态放在一个队列中，我们把这这种队列称为半连接队列，等三次握手完毕后，建立的连接就会放在全连接队列中，如果队列满了就会产生丢包的情况。4.三次握手可以携带参数吗？第三次握手时可以携带参数，第一次和第二次不能携带，举例：第一次就可以携带参数的话，假如有人恶意攻击服务器，攻击者根本不在乎服务器的接收、发送能力是否正常，如果每次都在第一次握手中的SYN报文中携带大量数据，重复的发SYN报文的话，那么服务器会花费大量时间、内存空间接收这些报文。所以得出的结论，如果在第一次握手中携带数据，那么服务器更容易收到攻击对于在第三次携带数据的话，此时客户端已经处于established状态，对于客户端来说已经建立连接，并且确定服务端的发送和接收能力时正常的，所以携带参数完全没问题。二、四次挥手第一次挥手：客户端发送一个FIN报文，报文中指定一个序列号，此时客户端进入FIN_WAIT_1状态，表示停止在发送数据第二次挥手：服务端收到FIN报文后，会回应一个ACK报文，把客户端的序列号+1作为ACK的值，此时服务端处于Close_WAIT状态，表示服务端同意关闭请求，客户端进入FIN_WAIT2状态 等待服务端发出连接释放报文第三次挥手：如果服务端也想断开连接了，和第一次挥手一样发出FIN报文，此时服务端处于LAST_ACK状态，表示发出连接释放报文，等待客户端确认第四次挥手：客户端收到FIN报文后，一样发送一个ACK报文，且把服务器的序列号+1作为ACK的值。此时客户端进入TIME_WAIT状态，需要经过等待计时器的时间2MSL后仍然没有回复，才会进入closed状态，而服务端收到ACK报文后直接进入Closed状态

前端-基础面试题汇总(含Vue、React)
type: item
date: 2022-05-02T22:59:00+08:00
description: 一、移动web部分1、前端页面有哪三层构成，分别是什么？作用是什么？1、结构层：由 HTML 标记语言负责创建，仅负责语义的表达。解决了页面“内容是什么”的问题。 2、表示层：由CSS负责创建，解决了页面“如何显示内容”的问题。 3、行为层：由脚本（js）负责。解决了页面上“内容应该如何对事件作出反应”的问题2、主流的浏览器分别是什么内核？Trident内核：（国产的绝大部分浏览器）IE，360，搜狗 Gecko内核：Firefox，NetScape6及以上 Presto内核：Opera7及以上 Webkit内核：（国产大部分双核浏览器其中一核）Safari（苹果），Chrome3、如何居中一个浮动元素?父元素和子元素同时左浮动，然后父元素相对左移动50%，再然后子元素相对右移动50%，或者子元素相对左移动-50%也就可以了。 Test Float Element Center 4、行内元素有哪些？块级元素有哪些？空元素（void）有哪些？1、行内元素：a，b，span，img，input，strong，label，button，select，textarea，em 2、块级元素：div，ul（无序列表），ol，li，dl（自定义列表），dt（自定义列表项），dd（自定义列表项的定义），p，h1-h6，blockquote（块引用） 3、空元素（void）：即没有内容的HTML元素。br（换行），hr（水平分割线），meta，link，input，img5、BFC（Block Formatting Context，块级格式化上下文）1、是什么：决定了元素如何对其内容进行定位，以及与其他元素的关系和相互作用。简言之，就是一个特殊的块，内部的元素和外部的元素不会相互影响。BFC内的盒子会在垂直方向上一个接一个地放置，垂直方向上也会发生外边距重叠。 2、应用场景：自适应布局（BFC不与float box重叠）、清除浮动（计算BFC的高度时，内部的浮动元素也被计算在内）、防止外边距重叠 3、如何触发BFC：float属性（不为none）、overflow属性（不为visible）、position属性（absolute，fixed）、display属性（inline-block，table-cell，table-caption，flex，inline-flex）6、CSS选择器的优先级!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 7、初始化CSS样式的意义1、为什么要初始化CSS样式：因为浏览器的兼容问题，不同浏览器对有些标签的默认值是不同的，如果没有对CSS初始化往往会出现浏览器之间的页面显示差异。8、HTML5新增了哪些属性1. 语义化标签: header nav section article aside footer 2. 多媒体标签: video audio 3. input类型: number search email tel date file time url 4. 本地离线存储 localStorage 长期存储数据,改变浏览器数据不会丢失 sessionStorage 浏览器关闭数据会丢失 5. 自定义属性 data-* 6. 画布 Canvas 7. webscoket 双向通信协议9、CSS3新增了哪些属性1. 圆角 border-radius 2. 盒子模型 box-sizing 3. 阴影 box-shadow 盒子阴影 text-shadow 文字阴影 4. 过渡 transition 5. 2D转换transform translate(平移) scale(缩放) skew(斜切) rotate(旋转) transform-origin 控制转换中心点 6. 3D转换 perspective(透视距) transform-style(3D控件效果) 7. 渐变 linear-gradient radial-gradient 8. 弹性布局 flex 9. 媒体查询 @media screen and () {...} 10. 边框图片 border-image 11. 自定义动画 @keyframes animation 12. 颜色 新增RGBA HSLA模式 13. 背景 background-size background-origin background-clip10、对HTML语义化的理解1、用正确的标签做正确的事情 2、HTML语义化让页面的内容结构化，结构更清晰，便于对浏览器、搜索引擎解析 3、即使在没有样式CSS情况下也以一种文档格式显示，并且是易于阅读的 4、搜索引擎的爬虫也依赖于HTML标记来确定上下文和各个关键字的权重，利于SEO …

如花美眷也敌不过似水流年
type: item
date: 2022-05-02T22:26:00+08:00
description: [timeline title="经典语录" type="small" start="梦开始的地方" end="新的一年开始"][item date="2022-04-15"] PUAPUAPUAPUAPUAPUAPUA。[/item][item date="2022-04-15"] 正常的社会并不是黑白分明、非此即彼的，有时善与善也会发生冲突。[/item][item date="2022-03-12"] 若不接受自己的卑微与渺小，我们很难发自内心地去尊重他人。[/item][item date="2022-02-14"] 人的野心是没有穷尽的，所以，不要成为纯粹的工具人。[/item][item date="2022-01-12"] 为什么害怕独处？因为人在独处的时候，总是会听到心灵深处的声音。[/item][item date="2021-12-12"] 我们只需要活好今天，因为永恒由每个今天构成，每个今天都可以触摸永恒。[/item][item date="2021-11-13"] 不要对人性抱以过高的期待，永远要警惕人性深处的幽暗，法治的前提就是对人性败坏的假设。[/item][item date="2021-10-14"] 人最大的痛苦，就是无法跨越“知道”和“做到”的鸿沟。[/item][item date="2021-9-12"] 低级的快乐来自放纵，高级的快乐来自克制。[/item][item date="2021-8-12"] 个知识越贫乏的人，越是拥有一种莫名奇怪的勇气和自豪感。 [/item][/timeline]

基于React在TypeScript环境下路由前置守卫封装
type: item
date: 2022-02-17T20:18:00+08:00
description: import { getToken } from '@/utils/tokenApi' import { Redirect, Route, RouteProps, useLocation } from 'react-router-dom' interface PrivateRouteType extends RouteProps { component: any } // children传入组件 component标签传组件 function PrivateRoute({ children, component: Component, ...rest }: PrivateRouteType) { const location = useLocation() return ( <Route {...rest} render={() => { // 查看本地是否有token 获取token const token = getToken() // 如果有 就去传递过来的组件 if (token.token) { return children ? children : <Component></Component> } else { // 没有就去login state/from 记录拦截下来的地址 用于回调 return ( <Redirect to={{ pathname: '/login', // state传参 state: { form: location.pathname }, }} ></Redirect> ) } }} /> ) } export default PrivateRoute

React、Vue项目中Token无感刷新原理-实用大多数项目
type: item
date: 2022-02-14T20:32:00+08:00
description: // 添加请求拦截器 request.interceptors.request.use( function (config) { // 在发送请求之前做些什么 const token = getToken().token if (token) { config.headers!.Authorization = `Bearer ${token}` } return config }, function (error) { // 对请求错误做些什么 return Promise.reject(error) } ) // 添加响应拦截器 request.interceptors.response.use( function (response) { // 对响应数据做点什么 return response }, async function (error: AxiosError<{ message: string }>) { console.dir(error, '99999') // 无感刷新回答： // 后台返回401， // 响应拦截器中处理 => 判断是否是有token // 如果有 说明token过期了 => 尝试用refresh_token 发请求刷新token // 刷新成功， 保存到本地以及仓库中 并重新发请求 // 刷新失败， 说明refresh_token 也过期了 则跳转到登录页 // 理解 ：用户发起一个请求， // 情况1：请求成功正常拿到数据，正常渲染 // 情况2: 没有返回结果 说明服务器有误，提示用户 // 情况3: 后端返回401 说明token过期,请求拦截器中判断返回结果是否为401， // 如果是401则，尝试利用refresh_token发送一个刷新token请求 // 拿回新token 基于新的token在发送上一个响应错误的请求, // 如果没有拿到新的token 则说明refresh_token也过期,跳转登录页 // 如果本地根本没有token，请直接拦截到登录页 // 对响应错误做点什么 // !----------------情况1：服务器繁忙------------ if (!error.response) { // 如果没有响应信息 Toast.show({ content: '服务器繁忙,请稍后重试', duration: 3000, }) return Promise.reject(error) } // !----------------情况2：token过期---------------- const token = getToken() // 判断是否为401 if (error.response.status === 401) { // 是的话判断本地有没有refresh_token // 如果有token 发送请求获取新的token if (token.refresh_token) { try { // 获取新的token const res = await axios({ method: 'put', url: baseURL + '/authorizations', headers: { Authorization: 'Bearer ' + token.refresh_token, }, }) // 拿到新token 存入仓库 store.dispatch( savetoken({ // token为最新获取的token token: res.data.data.token, // refresh_token是从本地获取的 refresh_token: token.refresh_token, }) ) // 存入成功后 重新发送错误请求 return request.request(error.config) } catch (error) { // !----------------情况3：refresh_token过期---------------- // 如果失败说明 refresh_token也过期 重新登录 Toast.show({ content: '登录信息过期,请重新登录', }) // 清除现有的所有的token store.dispatch(LogOut()) // 携带当前路径 直接跳转 history.replace({ pathname: '/login', state: { form: history.location.pathname, }, }) } return Promise.reject(error) } else { // !----------------情况4：根本没有token----------------…

封装属于自己的ajax请求-2021/11/14
type: item
date: 2021-11-14T15:30:00+08:00
description: function itheima(options) { // 创建构造函数 const xhr = new XMLHttpRequest(); // 打印形参 console.log(options); // 打印 看看 // console.log(options.params); // 如果查询参数有值 判断查询还是新增 if (options.params) { // console.log(options.params); // 定义数组 装着对象的值 let qs = []; // 遍历对象获取key for (let key in options.params) { // 获取键 // console.log(key); // 获取值 // console.log(options.params[key]); // 将键值对拼接 添加到数组中 qs.push(`${key}=${options.params[key]}`); } // 打印看看qs数组的值 console.log(qs); if (qs.length > 0) { // 看看join 转换后的拼接的字符串 // console.log(qs.join('&')); // 把 options.url的值 赋给 xhr.open() // options.url = options.url + `?${qs.join('&')}`; // 简化写法 options.url += `?${qs.join('&')}`; // 打印看看 // console.log(options.url); } } // 将形参中的值 请求方法 请求地址 放入请求中 xhr.open(options.method, options.url); // 判断请求类型 // 先把用户输入的请求方式 转为大写字母 并且 不是GET请求 并且 有data新增对象 if (options.method.toUpperCase() !== 'GET' && options.data) { // 1.判断是否为 FormData if (options.data instanceof FormData) { console.log(options.data); xhr.send(options.data); } // 2.判断data是否为字符串类型 else if (typeof options.data === 'string') { // 定义请求类型 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send(options.data); } // 3.判断data是否为对象类型 else if (typeof options.data === 'object') { // 定义请求类型 xhr.setRequestHeader('Content-Type', 'application/json'); // xhr.send(options.data); xhr.send(JSON.stringify(options.data)); } } else { xhr.send(); } // 发送请求 // 监听结果 xhr.addEventListener('load', function () { // console.log(JSON.parse(xhr.response)); // 将结果字符串转为对象 const res = JSON.parse(xhr.response); // 返回给 success 函数 传实参 options.success(res); }); }