1. Что такое axios и каковы его характеристики
описывать
аксиос - этоpromise
изHTTP
Библиотека, которая может быть использована в浏览器
илиnode.js
середина. Эта статья посвящена XHR.
axios предоставляет два адаптера HTTP-запросов, XHR и HTTP. Ядром XHR является объект XMLHttpRequest на стороне браузера, ядром HTTP является метод http.request узла.
характеристика:
- Создание XMLHttpRequests из браузера
- Создать http-запрос из node.js
- API обещания поддержки
- Перехват запросов и ответов
- Преобразование данных запроса в данные ответа
- отменить запрос
- Автоматически преобразовывать данные JSON
- Поддержка клиентов для защиты XSRF
задний план
посколькуVue
Начиная с 2.0, Youda объявил об отменеvue-resource
Официальная рекомендация , которая, в свою очередь, рекомендуетaxios
. Сейчас axios
стало большинствомVue
Первый выбор разработчика, в настоящее время имеет 87,3 тысячи звезд на github.axios
Квалифицированное использование и базовая инкапсуляция Vue также стали неотъемлемой частью серии технологических стеков Vue. Если вы еще не знаете axios, рекомендуется сначала с ним ознакомиться.документация официального сайта axios.
основное использование
Установить
npm install axios -S
использовать
import axios from 'axios'
// 为给定ID的user创建请求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 上面的请求也可以这样做
axios.get('/user', {
params: {ID: 12345}})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
2. Зачем инкапсулировать аксиомы в проекте Vue
axios
API очень удобный и может использоваться непосредственно в проекте. Однако в больших проектах много http-запросов, и нужно различать окружение.
Каждый сетевой запрос имеет похожие части, которые необходимо обрабатывать следующим образом, что приведет к избыточности кода и повреждению проекта.可维护性
,扩展性
axios('http://www.kaifa.com/data', {
// 配置代码
method: 'GET',
timeout: 3000,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
},
// 其他请求配置...
})
.then((data) => {
// todo: 真正业务逻辑代码
console.log(data);
}, (err) => {
// 错误处理代码
if (err.response.status === 401) {
// handle authorization error
}
if (err.response.status === 403) {
// handle server forbidden error
}
// 其他错误处理.....
console.log(err);
});
- Экологическое отличие
- запрашивать информацию заголовка
- тип запроса
- тайм-аут запроса
- timeout: 3000
- Разрешить файлы cookie
- withCredentials: true
- Обработка результатов ответа
- Не удалось подтвердить вход
- Отсутствует разрешение
- успех
- ...
3. Как инкапсулировать аксиомы в проекте Vue
файлы axios упакованы в каталогsrc/utils/https.js
, выставленный наружуcallApi
функция
1. Экологическое отличие
callApi
функциональная экспозицияprefixUrl
Параметры для настройки URL-адреса API前缀
, значение по умолчаниюapi
// src/utils/https.js
import axios from 'axios'
export const callApi = ({
url,
...
prefixUrl = 'api'
}) => {
if (!url) {
const error = new Error('请传入url')
return Promise.reject(error)
}
const fullUrl = `/${prefixUrl}/${url}`
...
return axios({
url: fullUrl,
...
})
}
Увидев это, вы можете спросить, а почему бы не использовать параметры конфигурации, предоставляемые axios.baseURL
, Причина вbaseURL
Соответствующий префикс будет добавлен к каждому интерфейсу, и в реальном сценарии проекта есть интерфейсный проект, соответствующий нескольким服务
место действия. Нужно проксировать на разные сервисы через разные префиксы,baseURL
Хотя это можно реализовать, для этого требуется вторичный префикс, что не элегантно, и вы не можете увидеть, какой реальный адрес API при его использовании, потому что префикс прокси смешивается с реальным адресом.
использоватьbaseURL
, эффект следующий
Функция устанавливает параметр prefixUrl, эффект следующий
использовать环境变量
а такжеwebpack代理
(Здесь используется конфигурация vuecli3), чтобы различать среды разработки и тестирования. Та же конфигурация для производственной средыnginx
играет роль
// vue.config.js
const targetApi1 = process.env.NODE_ENV === 'development' ? "http://www.kaifa1.com" : "http://www.ceshi1.com"
const targetApi2 = process.env.NODE_ENV === 'development' ? "http://www.kaifa2.com" : "http://www.ceshi2.com"
module.exports = {
devServer: {
proxy: {
'/api1': {
target: targetApi1,
changeOrigin: true,
pathRewrite: {
'/api1': ""
}
},
'/api2': {
target: targetApi2,
changeOrigin: true,
pathRewrite: {
'/api2': ""
}
},
}
}
}
2. Заголовок запроса
Следующие три являются общими
(1)application/json
Параметры будут размещены непосредственно в теле запроса и отправлены на сервер в формате JSON. Это также способ по умолчанию для запросов axios. Этот тип является наиболее широко используемым.
(2)application/x-www-form-urlencoded
Данные в теле запроса отправляются на бэкэнд в обычной форме (пара ключ-значение).
(3)multipart/form-data
Параметры будут в теле запроса, в единицах тегов, разделенных разделителями (настраиваемые границы). Вы можете загружать как пары ключ-значение, так и файлы. Формат, обычно используемый для загрузки файлов.
callApi
функциональная экспозицияcontentType
параметры для настройки请求头
, значение по умолчаниюapplication/json; charset=utf-8
Увидев это, все могут запутаться, пройдите сразуoptions
настроитьheaders
Нет, ответ да, вы можете видетьnewOptions
Порядок значений: сначала принимается значение по умолчанию, затем принимается заданное значение.options
, и, наконец, взятьcontentType
,contentType
Может соответствовать большинству сценариев, может использоваться в сценариях, которые не могут быть удовлетвореныoptions
настроить
пройти черезoptions
настроитьheaders
, написать n разheaders: {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}
; и черезcontentType
настроить, передать параметрыjson || urlencoded || multipart
Только что
когдаcontentType
=== urlencoded
час,qs.stringify(data)
// src/utils/https.js
import axios from 'axios'
import qs from 'qs'
const contentTypes = {
json: 'application/json; charset=utf-8',
urlencoded: 'application/x-www-form-urlencoded; charset=utf-8',
multipart: 'multipart/form-data',
}
const defaultOptions = {
headers: {
Accept: 'application/json',
'Content-Type': contentTypes.json,
}
}
export const callApi = ({
url,
data = {},
options = {},
contentType = 'json', // json || urlencoded || multipart
prefixUrl = 'api'
}) => {
...
const newOptions = {
...defaultOptions,
...options,
headers: {
'Content-Type': options.headers && options.headers['Content-Type'] || contentTypes[contentType],
},
}
const { method } = newOptions
if (method !== 'get' && method !== 'head') {
if (data instanceof FormData) {
newOptions.data = data
newOptions.headers = {
'x-requested-with': 'XMLHttpRequest',
'cache-control': 'no-cache',
}
} else if (options.headers['Content-Type'] === contentTypes.urlencoded) {
newOptions.data = qs.stringify(data)
} else {
Object.keys(data).forEach((item) => {
if (
data[item] === null ||
data[item] === undefined ||
data[item] === ''
) {
delete data[item]
}
})
// 没有必要,因为axios会将JavaScript对象序列化为JSON
// newOptions.data = JSON.stringify(data);
}
}
return axios({
url: fullUrl,
...newOptions,
})
}
Обратите внимание, что вapplication/json
В этом формате JSON.stringify не имеет смысла обрабатывать параметры, потому что axios будет сериализовать объекты JavaScript в JSON, а это значит, что это JSON независимо от того, конвертируете вы его или нет.
3. Тип запроса
Параметр типа запросаaxios
изoptions
изmethod
введите соответствующий тип запроса, напримерpost
,get
Просто подожди
Не инкапсулируйте, используйте роднойaxios
час,发送带参数的get请求
следующим образом:
// src/service/index.js
import { callApi } from '@/utils/https';
export const delFile = (params) => callApi({
url: `file/delete?systemName=${params.systemName}&menuId=${params.menuId}&appSign=${params.appSign}`,
option: {
method: 'get',
},
});
// 或者
export const delFile = (params) => callApi({
url: 'file/delete',
option: {
method: 'get',
params
},
});
Официальные документы следующие
callApi
функциональная экспозицияmethod
параметры для настройки请求类型
, значение по умолчаниюget
когда тип запросаget
когда будетcallApi
функция выставленаdata
параметр, установленный наoptions.params
, чтобы параметры автоматически стыковались после url-адреса
// src/utils/https.js
import axios from 'axios'
export const callApi = ({
url,
data = {},
method = 'get',
options = {},
...
prefixUrl = 'api'
}) => {
...
const newOptions = {
...,
...options,
method
}
...
if(method === 'get'){
newOptions.params = data
}
...
return axios({
url: fullUrl,
...newOptions,
})
}
4. Тайм-аут запроса
// src/utils/https.js
const defaultOptions = {
timeout: 15000,
}
5. Разрешить файлы cookie
// src/utils/https.js
const defaultOptions = {
withCredentials: true,
}
6. Обработка результатов ответа
пройти через.then
,.catch()
иметь дело с
Это необходимо согласовать с сервером.接口响应全局码
, так что единая трактовка登录校验失败
,无权限
,成功
ждать результата
Например, некоторые серверы登录校验失败
,无权限
,成功
Все возвращаемые коды ответов равны 200, а коды состояния, возвращаемые в тексте ответа, — 20001, 20002 и 10000 соответственно.then()
обработка
Например, некоторые серверы登录校验失败
,无权限
,成功
Коды ответов возвращают 401, 403, 200, вcatch()
обработка
// src/utils/https.js
import axios from 'axios'
import { Message } from "element-ui";
export const callApi = ({
...
}) => {
...
return axios({
url: fullUrl,
...newOptions,
})
.then((response) => {
const { data } = response
if (data.code === 'xxx') {
// 与服务端约定
// 登录校验失败
} else if (data.code === 'xxx') {
// 与服务端约定
// 无权限
router.replace({ path: '/403' })
} else if (data.code === 'xxx') {
// 与服务端约定
return Promise.resolve(data)
} else {
const { message } = data
if (!errorMsgObj[message]) {
errorMsgObj[message] = message
}
setTimeout(debounce(toastMsg, 1000, true), 1000)
return Promise.reject(data)
}
})
.catch((error) => {
if (error.response) {
const { data } = error.response
const resCode = data.status
const resMsg = data.message || '服务异常'
// if (resCode === 401) { // 与服务端约定
// // 登录校验失败
// } else if (data.code === 403) { // 与服务端约定
// // 无权限
// router.replace({ path: '/403' })
// }
if (!errorMsgObj[resMsg]) {
errorMsgObj[resMsg] = resMsg
}
setTimeout(debounce(toastMsg, 1000, true), 1000)
const err = { code: resCode, respMsg: resMsg }
return Promise.reject(err)
} else {
const err = { type: 'canceled', respMsg: '数据请求超时' }
return Promise.reject(err)
}
})
}
Вышеуказанная схема находится вMessage.error(xx)
, когда информация об ошибке, возвращаемая несколькими интерфейсами, непротиворечива, будет重复提示
проблема, как показано на рисунке ниже
схема оптимизации, использующая防抖
, реализовать подсказку об ошибке один раз, более элегантно
В-четвертых, полная упаковка и конкретное использование
код доступенмой гитхаб
полный пакет axios-ajax
// src/utils/https.js
import axios from 'axios'
import qs from 'qs'
import { debounce } from './debounce'
const contentTypes = {
json: 'application/json; charset=utf-8',
urlencoded: 'application/x-www-form-urlencoded; charset=utf-8',
multipart: 'multipart/form-data',
}
function toastMsg() {
Object.keys(errorMsgObj).map((item) => {
Message.error(item)
delete errorMsgObj[item]
})
}
let errorMsgObj = {}
const defaultOptions = {
withCredentials: true, // 允许把cookie传递到后台
headers: {
Accept: 'application/json',
'Content-Type': contentTypes.json,
},
timeout: 15000,
}
export const callApi = ({
url,
data = {},
method = 'get',
options = {},
contentType = 'json', // json || urlencoded || multipart
prefixUrl = 'api',
}) => {
if (!url) {
const error = new Error('请传入url')
return Promise.reject(error)
}
const fullUrl = `/${prefixUrl}/${url}`
const newOptions = {
...defaultOptions,
...options,
headers: {
'Content-Type':
(options.headers && options.headers['Content-Type']) ||
contentTypes[contentType],
},
method,
}
if (method === 'get') {
newOptions.params = data
}
if (method !== 'get' && method !== 'head') {
newOptions.data = data
if (data instanceof FormData) {
newOptions.headers = {
'x-requested-with': 'XMLHttpRequest',
'cache-control': 'no-cache',
}
} else if (newOptions.headers['Content-Type'] === contentTypes.urlencoded) {
newOptions.data = qs.stringify(data)
} else {
Object.keys(data).forEach((item) => {
if (
data[item] === null ||
data[item] === undefined ||
data[item] === ''
) {
delete data[item]
}
})
// 没有必要,因为axios会将JavaScript对象序列化为JSON
// newOptions.data = JSON.stringify(data);
}
}
axios.interceptors.request.use((request) => {
// 移除起始部分 / 所有请求url走相对路径
request.url = request.url.replace(/^\//, '')
return request
})
return axios({
url: fullUrl,
...newOptions,
})
.then((response) => {
const { data } = response
if (data.code === 'xxx') {
// 与服务端约定
// 登录校验失败
} else if (data.code === 'xxx') {
// 与服务端约定
// 无权限
router.replace({ path: '/403' })
} else if (data.code === 'xxx') {
// 与服务端约定
return Promise.resolve(data)
} else {
const { message } = data
if (!errorMsgObj[message]) {
errorMsgObj[message] = message
}
setTimeout(debounce(toastMsg, 1000, true), 1000)
return Promise.reject(data)
}
})
.catch((error) => {
if (error.response) {
const { data } = error.response
const resCode = data.status
const resMsg = data.message || '服务异常'
// if (resCode === 401) { // 与服务端约定
// // 登录校验失败
// } else if (data.code === 403) { // 与服务端约定
// // 无权限
// router.replace({ path: '/403' })
// }
if (!errorMsgObj[resMsg]) {
errorMsgObj[resMsg] = resMsg
}
setTimeout(debounce(toastMsg, 1000, true), 1000)
const err = { code: resCode, respMsg: resMsg }
return Promise.reject(err)
} else {
const err = { type: 'canceled', respMsg: '数据请求超时' }
return Promise.reject(err)
}
})
}
// src/utils/debounce.js
export const debounce = (func, timeout, immediate) => {
let timer
return function () {
let context = this
let args = arguments
if (timer) clearTimeout(timer)
if (immediate) {
var callNow = !timer
timer = setTimeout(() => {
timer = null
}, timeout)
if (callNow) func.apply(context, args)
} else {
timer = setTimeout(function () {
func.apply(context, args)
}, timeout)
}
}
}
конкретное использование
api файл в управлении каталогамиsrc/service
Вниз,index.js
файл выставляет другие модули, другие файлы нажимают功能模块划分
документ
получить запрос с параметрамиПользовательский префикс прокси для разных сервисовобработка типов файлов
V. Резюме
axios
Абсолютного стандарта для упаковки нет, и его нужно совмещать с проектом实际场景
дизайну, но нет сомнений, что инкапсуляция axios-ajax очень нужна
Оригинальный адрес:GitHub.com/в следующем месяце 25/asy…
Другие статьи:Рекомендации по работе с общими модулями между несколькими проектами Vue
Колонка [Бизнес-резюме], включенная в эту статью, направлена наУпреждать проблемы, возникающие в работе, и обобщать их как лучшие практики, добро пожаловать в подписку ✨