Доброе утро, сегодня, поделиться с людьми для передней для всехAPI
Небольшой опыт и взгляд на многоуровневую архитектуру. Архитектурный дизайн — это бесконечная дорога, лучшего нет, есть только лучше. Этот принцип применим к различным сценариям проектирования программного обеспечения,API
Дизайн слоя не является исключением.Если вы чувствуете, что при вызове интерфейса еще много слотов, это означает, что ваша архитектура слоя интерфейса нуждается в оптимизации. Сегодня я беруvue + axios
Например, чтобы разобраться с некоторыми из моих опытов и идей для вас.
боль каменного века
позвонить напрямуюaxios
, Это действительно болезненно, каждое место, куда вы звоните, должно оценивать статус ответа, и слишком много избыточных кодов.
import axios from "axios"
axios.get('/usercenter/user/page?pageNo=1&pageSize=10').then(res => {
const data = res.data
// 判断请求状态,success字段为true代表成功,视前后端约束而定
if (data.success) {
// 结果成功后的业务代码
} else {
// 结果失败后的业务代码
}
})
Это кажется действительно неудобным, каждый раз, когда вызывается интерфейс, столько повторяющейся работы!
Бронзовый век, вполне удовлетворительно
Для решения прямого вызоваaxios
болевые точки, мы обычно используемPromise
правильноaxios
Вторичная инкапсуляция, централизованное суждение о статусе ответа интерфейса и внешнем воздействииget
, post
, put
, delete
Ждатьhttp
метод.
вторичная упаковка аксиос
import axios from "axios"
import router from "@/router"
import { BASE_URL } from "@/router/base-url"
import { errorMsg } from "@/utils/msg";
import { stringify } from "@/utils/helper";
// 创建axios实例
const v3api = axios.create({
baseURL: process.env.BASE_API,
timeout: 10000
});
// axios实例默认配置
v3api.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded';
v3api.defaults.transformRequest = data => {
return stringify(data)
}
// 返回状态拦截,进行状态的集中判断
v3api.interceptors.response.use(
response => {
const res = response.data;
if (res.success) {
return Promise.resolve(res)
} else {
// 内部错误码处理
if (res.code === 1401) {
errorMsg(res.message || '登录已过期,请重新登录!')
router.replace({ path: `${BASE_URL}/login` })
} else {
// 默认的错误提示
errorMsg(res.message || '网络异常,请稍后重试!')
}
return Promise.reject(res);
}
},
error => {
if (/timeout\sof\s\d+ms\sexceeded/.test(error.message)) {
// 超时
errorMsg('网络出了点问题,请稍后重试!')
}
if (error.response) {
// http状态码判断
switch (error.response.status) {
// http status handler
case 404:
errorMsg('请求的资源不存在!')
break
case 500:
errorMsg('内部错误,请稍后重试!')
break
case 503:
errorMsg('服务器正在维护,请稍等!')
break
}
}
return Promise.reject(error.response)
}
)
// 处理get请求
const get = (url, params, config = {}) => v3api.get(url, { ...config, params })
// 处理delete请求,为了防止和关键词delete冲突,方法名定义为deletes
const deletes = (url, params, config = {}) => v3api.delete(url, { ...config, params })
// 处理post请求
const post = (url, params, config = {}) => v3api.post(url, params, config)
// 处理put请求
const put = (url, params, config = {}) => v3api.put(url, params, config)
export default {
get,
deletes,
post,
put
}
Вызывающий больше не определяет статус запроса
import api from "@/api";
methods: {
getUserPageData() {
api.get('/usercenter/user/page?pageNo=1&pageSize=10').then(res => {
// 状态已经集中判断了,这里直接写成功的逻辑
// 业务代码......
const result = res.result;
}).catch(res => {
// 失败的情况写在catch中
})
}
}
асинхронный/ожидающий дооснащения
Использование семантических асинхронных функций
methods: {
async getUserPageData() {
try {
const res = await api.get('/usercenter/user/page?pageNo=1&pageSize=10')
// 业务代码......
const { result } = res;
} catch(error) {
// 失败的情况写在catch中
}
}
}
существующие проблемы
- Степень семантики ограничена, и вызов интерфейса по-прежнему требует запроса интерфейса.
url
- внешний интерфейс
api
Слой сложно поддерживать, если меняется внутренний интерфейс, внешний интерфейс нужно сильно менять. - если
UI
Существуют различия между моделью данных компонента и структурой данных, необходимой для внутреннего интерфейса.Обработка данных должна выполняться перед вызовом каждого интерфейса, чтобы сгладить различия, такие как[1,2,3]
Перемена1,2,3
Это (конечно, это только самый простой пример). Таким образом, если данные обрабатываются небрежно, у вызывающего абонента высока вероятность ошибок! - Трудно выполнить специальные сценарии, например, сценарий запроса, внутренние требования, если введено ключевое слово поиска.
keyword
, надо позвонить/user/search
Интерфейс, если ключевое слово не введено, его можно только вызвать/user/page
интерфейс. Если каждый вызывающий абонент должен оценить, было ли введено ключевое слово, а затем решить, какой интерфейс вызывать, какова, по вашему мнению, вероятность ошибки и раздражает ли его использование? - В продукте сказано, что эти сценарии нужно оптимизировать, и по умолчанию стоит сортировка по времени создания в порядке убывания. стирать и менять по одному?
- ......
Итак, как решить эти проблемы? Пожалуйста, наберитесь терпения и смотрите...
Железный век, это круто
Решение, которое я имею в виду, состоит в том, чтобы добавить еще один слой между базовой инкапсуляцией и вызывающей стороной.API
Уровень адаптации (уровень адаптации, принимающий специальное значение), уровень адаптации унифицирует обработку, включая параметры обработки, обработку заголовка запроса, специализированную обработку, для извлечения более семантического способа, позволяющего вызывающему абоненту звонить в стиле «Дурак», а не находить интерфейсurl
и беспокоясь о повторяющейся работе со структурами данных, поместитеViewModel
Модель данных, привязанная к слою, передается непосредственно на уровень адаптации для унифицированной обработки.
Согласуйте микросервисную архитектуру
Во-первых, чтобы согласовать внутреннюю микросервисную архитектуру, во внешнемAPI
Звонок разделен на три модуля.
├─api
index.js axios底层封装
├─base 负责调用基础服务,basecenter
├─iot 负责调用物联网服务,iotcenter
└─user 负责调用用户相关服务,usercenter
Под каждым модулем определяется единое пространство имен микросервисов, например/src/api/user/index.js
:
export const namespace = 'usercenter';
функциональный модуль
Каждая функция имеет независимыйjs
Модуль, взяв в качестве примера интерфейс, связанный с управлением ролями, модуль/src/api/user/role.js
import api from '../index'
import { paramsFilter } from "@/utils/helper";
import { namespace } from "./index"
const feature = 'role'
// 添加角色
export const addRole = params => api.post(`/${namespace}/${feature}/add`, paramsFilter(params));
// 删除角色
export const deleteRole = id => api.deletes(`/${namespace}/${feature}/delete`, { id });
// 更新角色
export const updateRole = params => api.put(`/${namespace}/${feature}/update`, paramsFilter(params));
// 条件查询角色
export const findRoles = params => api.get(`/${namespace}/${feature}/find`, paramsFilter(params));
// 查询所有角色,不传参调用find接口代表查询所有角色
export const getAllRoles = () => findRoles();
// 获取角色详情
export const getRoleDetail = id => api.get(`/${namespace}/${feature}/detail`, { id });
// 分页查询角色
export const getRolePage = params => api.get(`/${namespace}/${feature}/page`, paramsFilter(params));
// 搜索角色
export const searchRole = params => params.keyword ? api.get(`/${namespace}/${feature}/search`, paramsFilter(params)) : getRolePage(params);
-
Каждый интерфейс основан на
RESTful
стиль, вызывая приращение(api.post
)удалять(api.deletes
)изменять(api.put
)чек об оплате(api.get
) базового метода и внешнего вывода семантического метода. -
называется
url
Он состоит из трех частей в формате:/微服务命名空间/特性命名空间/方法
-
Соглашение об именовании функций уровня адаптации интерфейса:
-
- Добавлен:
addXXX
- удалять:
deleteXXX
- обновить:
updateXXX
- Запросить записи по ID:
getXXXDetail
- Условный запрос для записи:
findOneXXX
- Условный запрос:
findXXXs
- Запросить все записи:
getAllXXXs
- Пейджинговый запрос:
getXXXPage
- поиск:
searchXXX
- Остальные интерфейсы персонализации названы в соответствии с семантикой
- Добавлен:
Решать проблему
-
Высшая степень семантики, сотрудничество
vscode
Функция подсказки кода , не будьте слишком круты для использования! -
Быстрая реакция на изменения интерфейса, унифицированная обработка адаптационным слоем
-
Централизованная обработка данных (для обработки общедоступных данных мы используем
paramsFilter
Решите для особых случаев, а затем разберитесь отдельно), звонящий может чувствовать себя непринужденно при ведении бизнеса. -
Для удовлетворения специальных сценариев, буддийская система имеет дело с друзьями и продуктами
- Для сценария запроса по ключевому слову, упомянутого в предыдущем разделе, мы оцениваем, существует ли
keyword
поле, решите позвонитьsearch
ещеpage
интерфейс. Нам просто нужно разоблачитьsearchRole
метод, вызывающей стороне нужно только вызватьsearchRole
метод, никаких других соображений не требуется.
export const searchRole = params => params.keyword ? api.get(`/${namespace}/${feature}/search`, paramsFilter(params)) : getRolePage(params);
- Для заказа продукта при внезапном увеличении спроса мы можем работать с параметрами по умолчанию, чтобы сделать слой адаптации.
Во-первых, мы создаем новый, предназначенный для управления параметрами по умолчанию.
js
,Такие какsrc/api/default-options.js
// 默认按创建时间降序的参数对象 export const SORT_BY_CREATETIME_OPTIONS = { sortField: 'createTime', // desc代表降序,asc是升序 sortType: 'desc' }
Далее делаем централизованную обработку на уровне адаптации интерфейса
import api from '../index' import { SORT_BY_CREATETIME_OPTIONS } from "../default-options" import { paramsFilter } from "@/utils/helper"; import { namespace } from "./index" const feature = 'role' export const getRolePage = params => api.get(`/${namespace}/${feature}/page`, paramsFilter({ ...SORT_BY_CREATETIME_OPTIONS, ...params }));
SORT_BY_CREATETIME_OPTIONS
Оно помещается впереди, чтобы удовлетворить другие требования сортировки, а поле сортировки, переданное вызывающей стороной, может переопределить параметры по умолчанию. - Для сценария запроса по ключевому слову, упомянутого в предыдущем разделе, мы оцениваем, существует ли
издеваться над первым
идеально подходитAPI
Дизайн слоев определенно неотделимmock
из. Прежде чем серверная часть предоставит интерфейс, внешний интерфейс должен быть разработан параллельно с использованием смоделированных данных, иначе прогресс не может быть гарантирован. Так как же разработать интерфейс, максимально совместимый с реальным интерфейсом?mock
Что насчет системы? Я просто поделюсь здесь.
- Сначала создайте
mock
преданныйaxios
Пример
мы вsrc
новый каталогmock
каталог и вsrc/mock/index.js
простой пакетaxios
Пример
// 仅限模拟数据使用
import axios from "axios"
const mock = axios.create({
baseURL: ''
});
// 返回状态拦截
mock.interceptors.response.use(
response => {
return Promise.resolve(response.data)
},
error => {
return Promise.reject(error.response)
}
)
export default mock
-
mock
Он также разделен на модули дляusercenter
Управление ролями в микросервисахmock
Интерфейс как пример
├─mock
index.js mock底层axios封装
├─user 负责调用基础服务,usercenter
├─role
├─index.js
мы вsrc/mock/user/role/index.js
Простое моделирование интерфейса для получения всех ролейgetAllRoles
import mock from "@/mock";
export const getAllRoles = () => mock.get('/static/mock/user/role/getAllRoles.json')
Как видите, мыmock
получено из интерфейсаstatic/mock
в каталогеjson
данные. Поэтому нам нужно подготовиться в соответствии с интерфейсным документом или согласованной структурой данных.getAllRoles.json
данные
{
"success": true,
"result": {
"pageNo": 1,
"pageSize": 10,
"total": 2,
"list": [
{
"id": 1,
"createTime": "2019-11-19 12:53:05",
"updateTime": "2019-12-03 09:53:41",
"name": "管理员",
"code": "管理员",
"description": "一个拥有部分权限的管理员角色",
"sort": 1,
"menuIds": "789,2,55,983,54",
"menuNames": "数据字典, 后台, 账户信息, 修改密码, 账户中心"
},
{
"id": 2,
"createTime": "2019-11-27 17:18:54",
"updateTime": "2019-12-01 19:14:30",
"name": "前台测试",
"code": "前台测试",
"description": "一个拥有部分权限的前台测试角色",
"sort": 2,
"menuIds": "15,4,1",
"menuNames": "油耗统计, 车联网, 物联网监管系统"
}
]
},
"message": "请求成功",
"code": 0
}
- Давайте посмотрим
mock
Как ты сделал это?
Давайте сначала посмотрим на вызывающий метод реального интерфейса.
import { getAllRoles } from "@/api/user/role";
created() {
this.getAllRolesData()
},
methods: {
async getAllRolesData() {
const res = await getAllRoles()
console.log(res)
}
}
Такmock
как это сделать? очень просто, просто положиmock
заменить метод, указанный вapi
предоставленный метод.
// import { getAllRoles } from "@/api/user/role";
import { getAllRoles } from "@/mock/user/role";
Видно, что этоmock
Соответствие между методом и вызовом реального интерфейса довольно высокое, когда интерфейс официально отлажен, вам нужно только настроить закомментированный код, и переход очень плавный!
- Обратите внимание, что в производственной среде для предотвращения упаковки
static/mock
содержимое каталогаcopy
прибытьdist
каталог, нам нужно настроитьCopyWebpackPlugin
,кvue-cli@2
Например, мы модифицируемwebpack.base.conf.js
Вот и все.
const devMode = process.env.NODE_ENV === 'development';
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: devMode ? config.dev.assetsSubDirectory : config.build.assetsSubDirectory,
ignore: devMode ? '' : 'mock/**/*'
}
])
Паровой век, действительно ароматный
Далее представьте, используйте type-safetypescript
, пусть передний конецAPI
Уровень действительно реализует программирование интерфейса документа, стандартизируя входные параметры, выходные параметры, дополнительные параметры и т. д., что повышает удобство сопровождения и значительно снижает вероятность ошибок на этапе кодирования. Хотя он все еще находится в стадии рефакторинга, я хотел бы сказать, чтоtypescript
Он действительно ароматный, я вдруг скучаю по немуAngular
Прошло два года, ждуvue3.0
способенtypescript
Сочетается более идеально ...
Электрический век, больше воображения
Будущее открывает безграничные возможности. Перед лицом все более сложных и разнообразных бизнес-сценариев мы будем извлекать лучшие архитектурные и дизайнерские шаблоны. В настоящее время существует незрелая идея, может ли он быть более стандартизированным в дизайне интерфейса, и в то же время внутренние документы интерфейса вывода извлекатьAPI json
структуры данных, как? внешний интерфейсAPI json
,пройти черезnodejs
Возможность файлового программирования, автоматическая генерация кода внешнего интерфейса, свободные руки.
Эпилог
Конечно, все вышеизложенное — лишь часть моего опыта и предположений. Это то, что я могу придумать в пределах своих возможностей. Надеюсь, это поможет некоторым нуждающимся студентам. Если у больших парней больше опыта, вы можете подсказать.
Прошлые основные моменты:
яTusi, небольшой фронтенд-лидер начинающей компании, все еще беспокоящийся о ежедневном написании бесконечных бизнес-кодов, накоплении технологий на пути к полировке продуктов и изучении путей роста. Если вы похожи на меня и думаете о собственном технологическом росте и ценности, добро пожаловать, добавьте меня в WeChat для общения и обсуждения, WeChat IDice_lloly. я буду в публичном аккаунтеБольшой салон фронтенд техникии апплетБлог ТусиСинхронизируйте содержимое блога, приходите и дразните меня!