axios 封装 http 请求详解
前言
Axios 是一个基于 Promise 的 HTTP 库,它的概念及使用方法本文不过多赘述,请参考:axios传送门
本文重点讲述下在项目中是如何利用 axios 封装 http 请求。
一、预设全局变量
在 /const/preset.js 中配置预先设置一些全局变量
window.$env = process.env.NODE_ENV === 'development' ? 'DEV' : 'PROD'// 默认开发环境
let config = {baseURL: location.origin,httpBaseURL: location.origin + '/api',webBaseURL: location.origin + location.pathname,vipAddress: '/necp/mapp/sc', // 后端微服务的统一入口
}// 生产环境
if (window.$env !== 'DEV') {if (location.href.indexOf('/ecs/') > -1) {config.baseURL = location.href.replace(/\/ecs.+/, '')config.httpBaseURL = config.baseURL}
}// 文件资源请求路径
config.fileUrl = config.httpBaseURL + config.vipAddress + 'file/download'window.$globals = config
在 main.js 中引入
import Vue from 'vue'
import './const/preset'
// ...
// 把 vue 示例挂载到 window 下
window.$vm = new Vue({render: h => h(App),router
}).$mount('#app')
因为生产环境部署的差异,http 请求的 baseURL 并非都是统一的,所以不单独配置默认的 axios.defaults.baseURL,而是通过此文件预设的变量进行设置。
全局预设变量中的 config.httpBaseURL 将添加到请求的 URL 中,对于代码中的 location.href.indexOf(‘/ecs/’) > -1 判断只是举例,可根据实际需求决定是否需要。
二、http 请求封装
1.配置全局 axios 默认值
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
axios.defaults.timeout = 60000
axios.defaults.crossDomain = true
此三条配置分别对应以下作用:
- 发送POST请求时,设置请求头的 Content-Type 字段为 ‘application/json;charset=UTF-8’ ,以便服务器正确解析请求的数据。
- 发送请求默认的超时时间为 60s。
- 允许跨域请求。
提示:覆盖默认超时时间,可在 axios 发送请求的参数 config 对象中设置 timeout 属性即可
2.配置请求拦截器
请求拦截器是在发送请求前执行的函数,它可以用于修改请求的配置或者在请求发送前进行一些操作。最常用的功能就是使用请求拦截器实现身份验证。
一个常见的实现是用户登录之后,服务端会响应用户的登录信息,并且把用户的身份认证 token 存储到 cookie 中,然后在请求拦截器中将 cookie 中获取到的 token 设置到请求头中,每次发送请求都会携带上此 token 发送到服务端,服务端再获取请求头的 token 来判断用户是否登录状态或者登录已过期,作出不同的响应。
axios.interceptors.request.use(config => {const token = cookie.get(TOKEN_COOKIE_KEY)if (token) {config.headers[TOKEN_REQ_KEY] = token}return config},error => {return Promise.reject(error)}
)
3.配置响应拦截器
响应拦截器是在接收到响应后执行的函数,它可以用于修改响应的数据或者在接收到响应后进行一些操作。
响应拦截器主要作用包括修改响应数据、错误处理、统一处理响应等功能,因把响应数据及错误的处理都放在了发送请求的回调中,所以只定义了最简单的响应拦截器。
axios.interceptors.response.use(response => {return response
}, error => {return Promise.reject(error)
})
4.发送请求的 request 函数
此函数接收四个参数:请求方法,请求的 api 接口,请求参数,请求的 config 配置项,返回一个 Promise 的实例。此函数完成了正常响应处理、异常处理、重复请求取消等功能。
4.1 拼接完整的请求 url
const apiInterceptor = api => {if (api.startsWith('http')) { // 自定义请求路径return api.slice(4)}if (api.startsWith('_SC_')) { // 项目统一的api前缀api = $globals.vipAddress + api.slice(4)}return $globals.httpBaseURL + api
}const request = async (method = 'post', api, params = {}, config = {}) => {// 省略...let url = apiInterceptor(api)let opts = {method,url,headers: config.headers || {},withCredentials: config.withCredentials || true // 跨域请求时是否需要使用凭证}// 省略...
}
调用 apiInterceptor 函数来拼接完整的请求 url,如果 api 是以 http 开头,则表示自定义 api 的请求路径,否则请求路径使用 preset.js 中预设的全局变量来拼接完整的 url。
4.2 参数处理
const jsonObj2FormData = jsonObj => {let formData = new FormData()Object.keys(jsonObj).forEach(key => {if (jsonObj[key] instanceof Array) {formData.append(key, JSON.stringify(jsonObj[key]))} else {formData.append(key, jsonObj[key])}})return formData
}
// 省略...if (config.formDataFormat) {opts.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'params = jsonObj2FormData(params)
}
if (method == 'post') {opts.data = params
} else {opts.params = params
}
- 服务端有部分接口接收的参数要求 FormData 格式,这时候需要将参数序列化,并且修改请求头的 Content-Type。
- 发送 get/post 请求时,接收参数的对象的 key 不一样。
4.3 正常响应处理
使用 axios(opts) 发起请求,得到的是一个 Promise,在 then 的第一个参数中传入一个正常的响应处理函数,这个函数接收响应拦截器中返回的 response 作为参数。
return new Promise((resolve, reject) => {axios(opts).then(response => {let res = response.dataif (config.customHandler) { // 自定义响应处理if (config.responseAll) return resolve(response)return resolve(res)}if (res) {if (res.code === 000) { // 登录超时$vm.$toast.error(res.message)$vm.$store.dispatch('REMOVE_USER') // 移除 cookie、session、storage 存储的信息reject(res.message)if (window.self === window.top) {$vm.$router.push('/login') // 跳转登录页}} else if (res.code === 200) {resolve(res.data)} else {$vm.$toast.error(res.message || '接口异常, 请稍后重试')reject(res)}} else {$vm.$toast.error('接口无返回内容')}})
})
提示:$vm 指向全局的 Vue 实例,$toast 则是将 element 的 Message 组件实例挂载到了 Vue 的原型上
- 如果调用 request 函数传入了
config.customHandler = true
,表示自定义响应处理,并且config.responseAll = true
时,会把响应拦截器中得到的 response 直接返回,这个参数主要用于调用服务端响应字节流的接口时使用。 - 后端响应的数据结构如下图,并且登录过期接口的 http 响应状态码是 200,但是响应的数据格式中的 code 值为特定值,所以要特殊处理此类情况,清空存储在客户端的客户信息,跳转到登录页。
- 当响应的数据中与服务端约定响应正常的 code 为 200,此时把 data 作为 Promise.resolve 的值
4.4 异常处理
异常处理在 axios(opts).then() 的第二个参数中传入处理函数,这个函数接收响应拦截器中返回的 Promise.reject(error) 作为参数。
异常处理主要针对 http 响应状态码不等于 200 的情况,包括常见的请求超时,404请求资源不存在,50X 服务器异常等情况。
axios(opts).then(response => {// 省略...
}, error => {// 如果自定义处理if (config.customHandler) {reject(error)return}// 请求超时if (error.code == 'ECONNABORTED' && error.message.indexOf('timeout') > -1) {$vm.$toast.error(`请求超时,接口地址:${url}`)reject(error)return}if (error.response) {// 401未登录或登录失效if (error.response.status === 401) {reject(error)if (window.self === window.top) {$vm.$router.push('/login')}return}switch (error.response.status) {case 404:$vm.$toast.error(`请求的资源不存在,异常服务接口地址:${url}`)breakcase 408:$vm.$toast.error('请求超时')breakcase 500:$vm.$toast.error('服务异常')breakcase 502:$vm.$toast.error(error.message || '服务未响应')breakcase 503:$vm.$toast.error(error.message || '服务暂不可访问')breakdefault:$vm.$toast.error(error.response.statusText || '服务异常, 请稍后重试')}} else {$vm.$toast.error(error.response.statusText || '未知错误, 请稍后重试')}reject(error)})
4.5 取消请求
在一些特定情况下,比如用户快速点击提交表单,短时间内同时触发同一个请求多次,我们可以借助 axios.cancelToken 来取消前几次请求,只保留最后一次请求。
主要实现的原理如下:
- 每次调用 request 函数时,根据传入的 method + api + JSON.stringify(config) 作为当前请求的标识 key,如果配置了
config.cancelTokenWidthParams = true
,时,在 key 后面拼接 JSON.stringify(params) 作为 key。 - HTTP_CANCEL_MAP 每一项的 key 为每个请求的 ‘唯一标识 + _ + 时间戳’,每一项 value 设置为 axios.CancelToken 构造函数传入的 executor 函数的参数,也就是 cancel 函数,调用 checkHttpCancel 函数传入 key 判断是否为重复请求,是重复请求则调用 cancel() 取消请求。
- 调用 request 函数时,配置 opts.cancelToken,使用 new 调用 CancelToken 的构造函数来创建 cancel token
- 请求响应成功和失败时都需要从 HTTP_CANCEL_MAP 中删除 reqUniqueKey 对应的 cancelToken
const CANCEL_TOKEN = axios.CancelToken
const HTTP_CANCEL_MAP = $globals.httpCancelMap = new Map()
const IS_CANCELED_MSG = 'canceled'const checkHttpCancel = reqKey => {HTTP_CANCEL_MAP.forEach((v, k) => {if (k.slice(0, -14) === reqKey) {v()HTTP_CANCEL_MAP.delete(k)}})
}const request = async (method = 'post', api, params = {}, config = {}) => {let reqKey = method + api + JSON.stringify(config)if (config.cancelTokenWidthParams) reqKey += JSON.stringify(params)let reqUniqueKey = reqKey + '_' + new Date().getTime()checkHttpCancel(reqKey)// 省略...opts.cancelToken = new CANCEL_TOKEN(c => HTTP_CANCEL_MAP.set(reqUniqueKey, c))// ...axios(opts).then(response => {HTTP_CANCEL_MAP.delete(reqUniqueKey)// ...}, error => {HTTP_CANCEL_MAP.delete(reqUniqueKey)if (axios.isCancel(error)) {reject(new Error(IS_CANCELED_MSG))return}// ...})
})
注意
- 此项目使用的 axios 版本为 0.21.1,从 v0.22.0 开始,Axios 支持以 fetch API 方式—— AbortController 取消请求,CancelToken API被弃用
- 可以使用同一个 cancel token 取消多个请求
三、完整的 http.js
import axios from 'axios'
import { TOKEN_REQ_KEY, TOKEN_COOKIE_KEY } from '@/const/common'
import { session, cookie, jsonObj2FormData } from '@/util/common'axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
axios.defaults.timeout = 120000
axios.defaults.crossDomain = trueaxios.interceptors.request.use(config => {const token = cookie.get(TOKEN_COOKIE_KEY)if (token) {config.headers[TOKEN_REQ_KEY] = token}return config},error => {return Promise.reject(error)}
)axios.interceptors.response.use(response => {return response
}, error => {return Promise.reject(error)
})const CANCEL_TOKEN = axios.CancelToken
const HTTP_CANCEL_MAP = $globals.httpCancelMap = new Map()
const IS_CANCELED_MSG = 'canceled'const checkHttpCancel = reqKey => {HTTP_CANCEL_MAP.forEach((v, k) => {if (k.slice(0, -14) === reqKey) {v()HTTP_CANCEL_MAP.delete(k)}})
}const apiInterceptor = api => {if (api.startsWith('http')) { // 自定义请求路径return api.slice(4)}if (api.startsWith('_SC_')) { // 项目统一的api前缀api = $globals.vipAddress + api.slice(4)}return $globals.httpBaseURL + api
}const request = async (method = 'post', api, params = {}, config = {}) => {let reqKey = method + api + JSON.stringify(config)if (config.cancelTokenWidthParams) reqKey += JSON.stringify(params)let reqUniqueKey = reqKey + '_' + new Date().getTime()checkHttpCancel(reqKey)return new Promise((resolve, reject) => {if (config.loading) $vm.$loading.show()let url = apiInterceptor(api)let opts = {method,url,headers: config.headers || {},withCredentials: config.withCredentials || true // 跨域请求时是否需要使用凭证}if (config.formDataFormat) {opts.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'params = jsonObj2FormData(params)}if (config.timeout) opts.timeout = config.timeoutif (config.extends) opts = Object.assign(opts, config.extends) // 如果有并列层级的参数扩展if (method == 'post') {opts.data = params} else {opts.params = params}opts.cancelToken = new CANCEL_TOKEN(c => HTTP_CANCEL_MAP.set(reqUniqueKey, c))if (config.responseType) opts.responseType = config.responseType// 发起 axios 请求axios(opts).then(response => {HTTP_CANCEL_MAP.delete(reqUniqueKey)if (config.loading) $vm.$loading.close()let res = response.dataif (config.customHandler) { // 自定义响应处理if (config.responseAll) return resolve(response)return resolve(res)}if (res) {if (res.code === 000) { // 登录超时$vm.$toast.error(res.message)$vm.$store.dispatch('REMOVE_USER') // 移除 cookie、session、storage 存储的信息reject(res.message)if (window.self === window.top) {$vm.$router.push('/login') // 跳转登录页}} else if (res.code === 200) {resolve(res.data)} else {$vm.$toast.error(res.message || '接口异常, 请稍后重试')reject(res)}} else {$vm.$toast.error('接口无返回内容')}}, error => {HTTP_CANCEL_MAP.delete(reqUniqueKey)if (axios.isCancel(error)) {reject(new Error(IS_CANCELED_MSG))return}if (config.loading) $vm.$loading.close()// 如果自定义处理if (config.customHandler) {reject(error)return}// 请求超时if (error.code == 'ECONNABORTED' && error.message.indexOf('timeout') > -1) {$vm.$toast.error(`请求超时,接口地址:${url}`)reject(error)return}if (error.response) {// 401未登录或登录失效if (error.response.status === 401) {reject(error)if (window.self === window.top) {$vm.$router.push('/login')}return}switch (error.response.status) {case 404:$vm.$toast.error(`请求的资源不存在,异常服务接口地址:${url}`)breakcase 408:$vm.$toast.error('请求超时')breakcase 413:$vm.$toast.error('请求实体大小超过服务器最大限制')breakcase 500:$vm.$toast.error('服务异常')breakcase 502:$vm.$toast.error(error.message || '服务未响应')breakcase 503:$vm.$toast.error(error.message || '服务暂不可访问')breakdefault:$vm.$toast.error(error.response.statusText || '服务异常, 请稍后重试')}} else {$vm.$toast.error(error.response.statusText || '未知错误, 请稍后重试')}reject(error)})})
}export default {get: (api, params = {}, config = {}) => {return request('get', api, params, config)},post: (api, params = {}, config = {}) => {return request('post', api, params, config)},image: id => {return `${$globals.fileUrl}?fileId=${id}`},isCanceled: error => {if (error && error.message === IS_CANCELED_MSG) return truereturn false}
}
- http.image 方法仅用于返回文件的请求完整 url,使用场景为比如 <img> 标签中的 src 的值
- http.isCanceled 方法用于判断当前请求是否取消,如果有请求未取消并且出现全局 loading 加载未关闭的情况,可根据此标志来判断是否关闭
四、封装成插件并挂载到原型
/plugins/http/install.js
import httpService from '@/service/http'export default {install: Vue => {Vue.prototype.$http = httpService}
}
五、管理 api
例如,根据业务可划分为文档,评论等模块,在 service 目录下分别创建对应的模块存放 api 的 js 文件,对 api 进行统一管理。
强烈建议给每个 api 备注功能,提高可维护性
/service/comment.js
/*** @name 获取评论列表* @param {Object} params 请求参数对象*/
export const getCommentListPromise = params => {params = Object.assign({page: 0, // 页码pageSize: 5, // 每页数量}, params)return $vm.$http.get('_SC_/comment/findCommentList', params)
}
在 Comment.vue 页面中使用
import { getCommentListPromise } from '@/service/comment'
async findCommentList() {const data = await getCommentListPromise()console.log(data)
}
总结
本文主要讲述了如何使用 axios 进行 http 封装的详细过程,及在项目中如何使用封装的 http 请求,请求拦截器和响应拦截器都是比较简单,没有处理很多的逻辑,逻辑处理基本是集中在 request 函数中。
相关文章:
axios 封装 http 请求详解
前言 Axios 是一个基于 Promise 的 HTTP 库,它的概念及使用方法本文不过多赘述,请参考:axios传送门 本文重点讲述下在项目中是如何利用 axios 封装 http 请求。 一、预设全局变量 在 /const/preset.js 中配置预先设置一些全局变量 window.…...
牛客2024年愚人节比赛(A-K)
比赛链接 毕竟是娱乐场,放平心态打吧。。。 只有A一个考了数学期望,其他的基本都是acmer特有的脑筋急转弯,看个乐呵即可。 A 我是欧皇,赚到盆满钵满! 思路: 我们有 p 1 p_1 p1 的概率直接拿到一件实…...
loadbalancer 引入与使用
在消费中pom中引入 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> 请求调用加 LoadBalanced 注解 进行服务调用 默认负载均衡是轮训模式 想要切换…...
Yolov5封装detect.py面向对象
主要目标是适应摄像头rtsp流的检测 如果是普通文件夹或者图片,run中的while True去掉即可。 web_client是根据需求创建的客户端,将检测到的数据打包发送给服务器 # YOLOv5 🚀 by Ultralytics, GPL-3.0 license """ Run inf…...
入门级深度学习主机组装过程
一 配置 先附上电脑配置图,如下: 利用公司的办公电脑对配置进行升级改造完成。除了显卡和电源,其他硬件都是公司电脑原装。 二 显卡 有钱直接上 RTX4090,也不能复用公司的电脑,其他配置跟不上。 进行深度学习&…...
python爬虫之selenium4使用(万字讲解)
文章目录 一、前言二、selenium的介绍1、优点:2、缺点: 三、selenium环境搭建1、安装python模块2、selenium4新特性3、安装驱动WebDriver驱动选择驱动安装和测试 基础操作1、属性和方法2、单个元素定位通过id定位通过class_name定位一个元素通过xpath定位…...
【ARM 嵌入式 C 头文件系列 22 -- 头文件 stdint.h 介绍】
请阅读【嵌入式开发学习必备专栏 】 文章目录 C 头文件 stdint.h定长整数类型最小宽度整数类型最快最小宽度整数类型整数指针类型最大整数类型 C 头文件 stdint.h 在 C 语言中,头文件 <stdint.h> 是 C99 标准的一部分,旨在提供一组明确的整数类型…...
LabVIEW专栏三、探针和断点
探针和断点是LabVIEW调试的常用手段,该节以上一节的"测试耗时"为例 探针可以打在有线条的任何地方,打上后,经过这条线的所有最后一次的数值都会显示在探针窗口。断点可以打在程序框图的所有G代码对象,包括结构…...
Transformer模型-softmax的简明介绍
今天介绍transformer模型的softmax softmax的定义和目的: softmax:常用于神经网络的输出层,以将原始的输出值转化为概率分布,从而使得每个类别的概率值在0到1之间,并且所有类别的概率之和为1。这使得Softmax函数特别适…...
记录一下做工厂的打印pdf程序
功能:在网页点击按钮调起本地的打印程序 本人想到的就是直接调起方式,网上大佬们说用注册表的形式来进行。 后面想到一种,在电脑开机时就开启,并在后台运行,等到有人去网页里面进行触发,这时候就有个问题&a…...
Linux网络编程一(协议、TCP协议、UDP、socket编程、TCP服务器端及客户端)
文章目录 协议1、分层模型结构2、网络应用程序设计模式3、ARP协议4、IP协议5、UDP协议6、TCP协议 Socket编程1、网络套接字(socket)2、网络字节序3、IP地址转换4、一系列函数5、TCP通信流程分析 第二次更新,自己再重新梳理一遍… 协议 协议:指一组规则&…...
Python读取Excel根据每行信息生成一个PDF——并自定义添加文本,可用于制作准考证
文章目录 有点小bug的:最终代码(无换行):有换行最终代码无bug根据Excel自动生成PDF,目录结构如上 有点小bug的: # coding=utf-8 import pandas as pd from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import letter from reportlab.pdfbase import pdf…...
http: server gave HTTP response to HTTPS client 分析一下这个问题如何解决中文告诉我详细的解决方案
这个错误信息表明 Docker 客户端在尝试通过 HTTPS 协议连接到 Docker 仓库时,但是服务器却返回了一个 HTTP 响应。这通常意味着 Docker 仓库没有正确配置为使用 HTTPS,或者客户端没有正确配置以信任仓库的 SSL 证书。以下是几种可能的解决方案࿱…...
Flume学习笔记
视频地址:https://www.bilibili.com/video/BV1wf4y1G7EQ/ 定义 Flume是一个高可用的、高可靠的、分布式的海量日志采集、聚合和传输的系统。 Flume高最要的作用就是实时读取服务器本地磁盘的数据,将数据写入HDFS。 官网:https://flume.apache.org/releases/content/1.9.0/…...
数据库系统概论(超详解!!!) 第三节 关系数据库标准语言SQL(Ⅳ)
1.集合查询 集合操作的种类 并操作UNION 交操作INTERSECT 差操作EXCEPT 参加集合操作的各查询结果的列数必须相同;对应项的数据类型也必须相同 查询计算机科学系的学生及年龄不大于19岁的学生。SELECT *FROM StudentWHERE Sdept CSUNIONSELECT *FROM StudentWHERE Sage&l…...
与谷歌“分家”两年后,SandboxAQ推出统一加密管理平台
3月27日,SandboxAQ宣布其AQtive Guard平台现已全面可用(GA),适用于所有行业,以防范人工智能驱动和量子攻击的威胁。前者是在两年前3月从谷歌母公司Alphabet分拆出来的初创公司,并在当时获得了“九位数”的融…...
【卫星家族】 | 高分六号卫星影像及获取
1. 卫星简介 高分六号卫星(GF-6)于2018年6月2日在酒泉卫星发射中心成功发射,是高分专项中的一颗低轨光学遥感卫星,也是我国首颗精准农业观测的高分卫星,具有高分辨率、宽覆盖、高质量成像、高效能成像、国产化率高等特…...
XML与Xpath
XML与Xpath XML是一种具有某种层次结构的文件,Xpath则是解析这种文件的工具 接下来将会解释XML文件的结构和Xpath的基本使用,并且用Java语言进行操作展示。 XML结构 XML(可扩展标记语言)文件具有一种层次结构,由标签…...
【c++20】CPP-20-STL-Cookbook 学习笔记
Cpp20-STL-Cookbook-src简单的阅读笔记。c++20更好用了,比如STL 包含了一些这样的辅助函数,比如 make_pair() 和make_tuple() 等。 这些代码现在已经过时了,但是为了与旧代码兼容,会保留这些代码。比如 可以声明是一个std的string:Sum s1 {1u, 2.0, 3, 4.0f }?...
Python 之 Flask 框架学习
毕业那会使用过这个轻量级的框架,最近再来回看一下,依赖相关的就不多说了,直接从例子开始。下面示例中的 html 模板,千万记得要放到 templates 目录下。 Flask基础示例 hello world from flask import Flask, jsonify, url_fora…...
精品丨PowerBI负载测试和容量规划
当选择Power BI作为业务报表平台时,如何判断许可证的选择是否符合业务需求,价格占了主导因素。 Power BI的定价是基于SKU和服务器内核决定的,但是很多IT的负责人都不确定自己公司业务具体需要多少。 不幸的是,Power BI的容量和预期…...
【算法-PID】
算法-PID ■ PID■ 闭环原理■ PID 控制流程■ PID 比例环节(Proportion)■ PID 积分环节(Integral)■ PID 微分环节(Differential) ■ 位置式PID,增量式PID介绍■ 位置式 PID 公式■ 增量式 PI…...
ros rosbag使用记录
rosbag: 1. rosbag record -a 记录当前所有消息(较少用)2. rosbag record -O bag_name.bag /topic 记录指定消息3. rosbag info 查阅bag文件信息4. rosbag play 播放bag文件内容5. python script 查看bag文件内容参考: 1. rosbag record -a 记…...
WebKit结构揭秘:探秘网页渲染的魔法之源
一、WebKit之心:渲染引擎的魔力 WebKit的渲染引擎是其核心所在,它犹如一位技艺高超的魔法师,将HTML、CSS和JavaScript的魔法咒语转化为绚丽的网页画面。它解析代码,绘制页面,让网页内容跃然屏上,展现出无尽…...
VSCode美化
今天有空收拾了一下VSCode,页面如下,个人觉得还是挺好看的~~ 1. 主题 Noctis 色彩较多,有种繁杂美。 我使用的是浅色主题的一款Noctis Hibernus 2. 字体 Maple Mono 官网:Maple-Font 我只安装了下图两个字体,使…...
Runes 生态一周要览 ▣ 2024.3.25-3.31|Runes 协议更新 BTC 减半在即
Runes 生态大事摘要 1、Casey 发布了 Runes 协议文档 RUNES HAVE DOCS,Github 代码库更新到 ord 0.17.0 版本,Casey 表示符文是一个“严肃”的代币协议。 2、Casey 公布了第一个硬编码的创世符文「UNCOMMONGOODS」 3、4月7日香港沙龙|聚焦「…...
瘦身Spring Boot应用(thinJar)
瘦身Spring Boot应用(thinJar) 简介 我们使用Spring Boot提供的spring-boot-maven-plugin打包Spring Boot应用,可以直接获得一个完整的可运行的jar包,把它上传到服务器上再运行就极其方便。 但是这种方式也不是没有缺点。最大的缺点就是包太大了&…...
备战蓝桥杯---贪心刷题1
话不多说,直接看题: 本质是一个数学题: 我们令xi<0表示反方向传递,易得我们就是求每一个xi的绝对值之和min,我们令平均值为a爸。 易得约束条件: x1-x2a1-a,x2-x3a2-a..... 解得x1x1-0,x2x1-((n-1)*a-a2-...an)。…...
《数据结构学习笔记---第九篇》---循环队列的实现
文章目录 1.循环队列的定义 2.循环队列的判空判满 3.创建队列并初始化 4.入队和出队 5. 返回队尾队首元素 6.释放循环队列 1.循环队列的定义 定义:存储队列元素的表从逻辑上被视为一个环。 我们此次实现的循环队列,采用顺序表 typedef struct {int…...
前端调试工具之Chrome Elements、Network、Sources、TimeLine调试
常用的调试工具有Chrome浏览器的调试工具,火狐浏览器的Firebug插件调试工具,IE的开发人员工具等。它们的功能与使用方法大致相似。Chrome浏览器简洁快速,功能强大这里主要介绍Chrome浏览器的调试工具。 打开 Google Chrome 浏览器,…...
北京网站建设公司电话/网络营销的特点和优势
进程间通信一、管道创建管道父子进程的管道单向通信父子间的双向通信管道Shell中的管道通信匿名管道与命名管道管道特点二、消息队列不足三、共享内存四、信号量五、信号六、Socket创建Socket的系统调用通信方式TCP协议通信的Socket编程模型UDP协议通信的Socket编程模型本地进程…...
校园网站cms/网络营销推广方案范文
先看效果图 首先安装一下插件 yarn add react-transition-group把你需要有动画效果的页面用CSSTransition包裹起来 import { CSSTransition } from react-transition-group; import { RouteComponentProps } from react-router-dom;const Album: FC<RouteComponentProps&…...
网站建设的设备/希爱力双效片副作用
在输入框中输入我们想要输入的信息就会出现其他与其相关的提示信息,这种效果在Android中是用AutoCompleteTextView实现的。 <AutoCompleteTextView android:layout_width"match_parent"android:layout_height"wrap_content"android:id&qu…...
重庆网站页面优化/女生seo专员很难吗为什么
《Kotlin核心编程》阅读笔记第八章 元编程程序和数据什么是元编程常见的元编程技术Kotlin的反射kotlin和Java 反射koltin的KClasskotlin的KCallable获取参数信息Kotlin 注解无处不在的注解精准控制注解位置获取注解信息第八章 元编程 Java的反射只是元编程的一种方式。 示例&a…...
wordpress默认密码恢复/最全bt搜索引擎入口
软件问题1.病毒,升级杀毒软件,进安全模式下杀毒。2.系统文件损坏,覆盖安装或重装系统。3.启动项问题,开始--运行--msconfig 除了ctfmon外 其余的全部去掉。硬件问题1.机箱电源功率不足,引起自动重启,更换高…...
网站内的链接怎么做的/5118
postgresql按照日期范围进行查询 按照日期查询通常有好几种方法: 按照日期范围查询有好几种方法,日期字段类型一般为: Timestamp without timezone 方法一: select * from user_info where create_date > 2015-07-01 and c…...