Простите меня за то, что я попал в заголовки, но внимательно прочитав эту статью, вы обязательно что-то приобретете!
Основная цель этой статьи — максимально упростить и автоматизировать метод фронтенд-запроса, привнеся немного счастья в нашу тяжелую жизнь, связанную с перемещением кирпичей ^_^.
Я считаю, что фронтенд-собака вроде меня уже давно устала писать разные запросы и разные пути запросов.В проекте все запросы делают по-разному, но общие методы схожи. Давайте взглянем на анализ недостатков распространенных методов запроса ниже и посмотрим, есть ли похожие болевые точки на примере проекта vue + axios.
# 通用的文件结构
request
|-- config.js
|-- http.js
// config.js,主要对项目中请求的异常捕捉,添加配置等
import Vue from "vue";
import axios from "axios";
import { Notification } from 'element-ui';
import store from "@/store";
// 配置 Content-Type
axios.defaults.headers.post["Content-Type"] = "aplication/json";
/**
* 配置 axios
*/
// http request 拦截器
axios.interceptors.request.use(
config => {
return config;
},
err => {
return Promise.reject(err);
}
);
// http response 拦截器
axios.interceptors.response.use(
response => {
// 对某些错误代码进行判断
if(response.data.code == 2){ // identity failure
store.dispatch("LogOut");
}
if (response.data.code !== 0 && response.data.msg !== -1) {
Notification({
title: '系统错误',
message: response.data.msg,
type: "error",
offset: 35,
duration: 2000
});
}
return response;
},
error => {
console.log(error);
}
);
export default axios;
Этот файл должен существовать в каждом проекте и в основном используется для общедоступной конфигурации и захвата исключений в запросах.
// http.js,对config完成的axios进行封装
import axios from config
export function get(url, payload){
return axios.get(url, {
params: payload
})
}
export function post(url, payload){
return axios.post(url, {
params: payload
})
}
// export function delete...
Этот файл в основном дляaxiosСделайте некоторую инкапсуляцию общих методовaxios.Подожди, короче есть единый вход, что удобнее при звонке. В этом есть свои преимущества, но есть ли лучшее решение? если естьDELETE PUTА как быть с этими просьбами? Что делать, если есть 10 способов запроса? отмечен здесь как痛点一, которые мы рассмотрим ниже.
// test.vue
import * as http from '@/path/to/http'
export default {
methods: {
getData(){
https.get('/v1/systemInfo').then(res => {
// do something
})
}
}
}
Вот как это выглядит, когда мы это называем, и выглядит довольно стандартно. Но только представьте, что если бэкенд вносит пакетные изменения в API. Если каждый вызов интерфейса разбросан по каждому файлу компонента, нужно ли нам извлекать и изменять их один за другим в каждом файле, что будет очень утомительно в обслуживании. И каждый раз приходится проверять документацию по апи (痛点二) путь интерфейса для вызова (痛点三) и метод запроса (痛点四( ? ?
решение
Много глупостей было сказано выше, и столько проблем сказано. Особенно, если вы не придумаете красивого плана, труда и управления... не беспокойтесь все, слушайте... слушайте меня медленно...
Структура каталогов
http
|--apiModules
| |--user.js
| |--system.js
|--parse
| |--parse.js
| |--api.json
|--fetch.js
|--config.js
Это наша структура каталогов, я представлю их один за другим ниже.
Как решить болевую точку один?
Чтобы избежать утомительной инкапсуляции существующих методов запроса, мы можем написать метод для реализации, какой метод объекта axios вызывается для любого переданного метода запроса. Метод передачи параметров прописывается в суждении, что позволяет избежать необходимости инкапсулировать метод запроса, какой метод мы используем.
import axios from './config' // config文件还是跟上面的一样,这里不再说明
// fetch.js
export function fetch(method, url, payload){
// 查看axios的文档,我们知道除了get的传参方式不一样,其余的都是直接传递,那么我们只需要判断get就可以
if(method === 'get'){
return axios['get'](url, {params: payload})
} else {
return axios[method](url, payload)
}
}
Таким образом, код нашей бизнес-страницы становится таким:
// test.vue
import fetch from '@/path/to/fetch'
export default {
methods: {
getData(){
fetch('get','/v1/systemInfo', {...}).then(res => {
// do something
})
}
}
}
Здесь вроде ничего не изменилось, изменилось только название метода. Но я обнаружил еще одну небольшую проблему: нужно ли обращаться к выборке каждый раз, когда я создаю новую страницу? Беда! Таким образом, вы можете напрямую смонтировать метод fetch в экземпляр vue, затем вы можете вызвать его прямо внутри компонента и решить небольшую проблему ^ _ ^
// fetch.js
class Fetch {
// 给Vue提供安装接口
install(vue) {
Object.assign(vue.prototype, {
$fetch: this.fetch
});
}
fetch(method, url, payload) {
if(method === 'get'){
return axios['get'](url, {params: payload})
} else {
return axios[method](url, payload)
}
}
}
export default new Fetch();
// main.js
import Vue from 'vue'
import Fetch from '@/path/to/fetch'
Vue.use(Fetch)
// test.vue
export default {
methods: {
getData(){
this.$fetch('get','/v1/systemInfo', {...}).then(res => {
// code
})
}
}
}
Как изящно решить болевые точки два, три и четыре?
Давайте резюмируем:
- Инкапсуляция метода запроса (
痛点一) - Каждый раз, когда вам нужно проверять документацию по API (
痛点二) - Путь интерфейса для вызова (
痛点三) - Просмотр метода запроса (
痛点四)
痛点一Может быть, не все существуют, тогда二三四Это должна быть общая проблема, и именно ее в основном и хочет решить эта статья. Чтобы каждый раз не смотреть на метод запроса документа, запрашивайте путь. в качестве стандарта前端配置攻城狮Мы можем настроить эту информацию унифицированным образом, избегая необходимости проверять ее каждый раз. Что нас должно заботить, так это формат возвращаемых данных, входящие параметры и т. д. Представьте, как мы были бы счастливы каждый раз, когда отправляем такой запрос!
this.$fetch('system.getVersion').then(res => {
// code
})
/*
* 大家的项目中,后端api肯定都是区别了某个模块的,可能每个模块做的人也不一样
* 在调用的时候指定一下模块名,接口名就可以
* 不需要知道知道请求方式,请求路径
*/
Чтобы выполнить вышеуказанные требования, нам обязательно нужно использовать файл конфигурации для записи вышеуказанной информации, хотя нам это не нужно, но программа должна заботиться!./apiModulesиспользуется для хранения этой информации.
// ./apiModules/system.js
export default {
getVersion: {
url: 'path/to/getVersion',
method: 'get'
},
modVersion: {
url: 'path/to/modVersion',
method: 'post'
}
}
// ./apiModules/user.js
export default {
getInfo: {
url: 'path/to/getInfo',
method: 'get'
}
}
// 当然,以上的配置字段都可以根据需求自定义,比如同一apiName要根据用户角色调用不同接口,只需要在fetch写上相应的判断就可以,非常方便!
Поэтому нам нужно снова изменить файл выборки.
import axios from "./config";
// 根据 ./apiModules文件夹中 生成fetchCfg -- 实现方法 webpack-require.context()
// fetchCfg = {
// system,
// user
// };
const fetchCfg = {};
// 通过 require.context 可以让webpack自动引用指定文件夹中的文件
// 我们将它存到 fetchCfg 上以供 fetch 方法使用
const requireContext = require.context('./apiModules', false, /\.js$/)
requireContext.keys().forEach(path => {
let module = path.replace(".js", "").replace("./", "")
fetchCfg[module] = requireContext(path).default
})
/**
* 解析参数
* 这个函数主要负责解析传入fetch的 module 和 apiName
* @param {String} param
*/
const fetchParam = param => {
var valid = /[a-z]+(\.[a-z])+/.test(param);
if (!valid) {
throw new Error(
"[Error in fetch]: fetch 参数格式为 moduleName.apiName"
);
} else {
return {
moduleName: param.split(".")[0],
apiName: param.split(".")[1]
};
}
};
class Fetch {
// 给Vue提供安装接口
install(vue) {
Object.assign(vue.prototype, {
$fetch: this.fetch
});
}
/**
* 对axios封装通用fetch方法
* 会根据传入的下列参数自动寻找 method 和路径
* @param {*} module 对应 fetch配置的名字
* @param {*} apiName 该模块下的某个请求配置名
*/
fetch(moduleInfo, payload) {
let prefix = '/api'
let moduleName = fetchParam(moduleInfo)["moduleName"];
let apiName = fetchParam(moduleInfo)["apiName"];
// 判断没有找到传入模块
if(!fetchCfg.hasOwnProperty(moduleName)){
throw new Error(
`[Error in fetch]: 在api配置文件中未找到模块 -> ${moduleName}`
);
}
// 判断没有找到对应接口
if(!fetchCfg[moduleName].hasOwnProperty(apiName)){
throw new Error(
`[Error in fetch]: 在模块${moduleName}中未找到接口 -> ${apiName}`
);
}
let fetchInfo = fetchCfg[moduleName][apiName];
let method = fetchInfo["method"];
let url = `${prefix}/${fetchInfo["url"]}`;
if (method === "get") {
return axios[method](url, {
params: payload
});
} else {
return axios[method](url, payload);
}
}
}
export default new Fetch();
С помощью вышеуказанного метода элегантно решена二三四Три болевые точки!
Вишенкой на торте является скрипт разбора конфигурационного файла API.
Наконец, давайте поговорим о нашей папке синтаксического анализа, которая является вишенкой на торте, если ваш сервер использует что-то вродеswaggerилиpostmanПосле того, как вы сможете экспортировать структурированные файлы, такие как json, вы можете получить приведенную выше информацию о конфигурации API с помощью простого преобразования скрипта узла.runВы можете применить все изменения к проекту в одном скрипте без ручной модификации файла API, даже если вам нужно вручную изменить его, вам не нужно изменять его в каждом бизнес-файле, что удобно и быстро ~
Ниже приведен мой скрипт для чтения документации почтальона слоя API, здесь также может быть много автоматизированных способов. Например, этот документ размещен на git.После каждого API обновления документа мы можем заранее написать сценарий оболочки, чтобы синхронизировать обновление git с локальным, а затем запустить скрипт узла (можно поместить команду в тег скрипта в вызове package.json с помощью npm) для чтения/записи документов. Может быть что-то не так при написании сценария в первый раз, но как только он будет написан, и договориться с бэкенд-друзьями, после этого работа пойдет намного быстрее?
// parse.js
/**
* README
* 读取中间层json文件,生成api配置
*/
let fs = require("fs");
let path = require("path");
let dosJson = require("./api.json");
var jsFile = fs.createWriteStream(path.resolve(__dirname, "./api/ddos.js"), {
encoding: "utf8"
});
function parsePostManJson(json) {
Object.keys(json).map(key => {
// 添加注释
if (key === "name") {
jsFile.write(`// ${json[key]}`)
console.log(`// ${json[key]}`);
}
if(key === "request"){
let urlName = json[key].url.path[json[key].url.path.length - 1];
let url = json[key].url.raw.replace("{{HOST}}", "");
let method = json[key].method;
let params = "";
if(method === "GET"){
params = `// ${url.split("?")[1] ? url.split("?")[1] : ""}`;
url = url.split("?")[0];
}
// let content = `${method === 'GET' ? params : ""}`
let content = `
${urlName}: {
url: "${url}",
method: "${method.toLowerCase()}",
custom: true
},
`
console.log(content);
jsFile.write(content)
}
if(key === "item" && json[key].constructor === Array){
json[key].map(itemJson => {
parsePostManJson(itemJson);
})
}
});
}
jsFile.write(`export default {`)
parsePostManJson(dosJson);
jsFile.write(`}`)
jsFile.end();
jsFile.on('finish',function(){
console.log('写入完成');
})
jsFile.on('error',function(){
console.log('写入失败');
})
В выходной файл api также добавлены некоторые комментарии.При необходимости вы можете напрямую написать формат параметра, и вам не нужно открывать онлайн-документ, чтобы просмотреть его позже.Разве это не очень удобно?
// ddos.js
export default {
// 获取ddos模式
getDDosCfg: {
url: "/getDDosCfg",
method: "post",
custom: true,
napi: true
},
// DDos融入// 数据报表统计// 获取机房概览信息
statisticsInfo: {
url: "/admin/Ddos/Statistic/statisticsInfo",
method: "post",
custom: true
}
};
В заключение
Ха-ха, нелегко быть терпеливым, видя здесь, ты действительно маленький парень~ (⊙﹏⊙)
Итак, в разработке мы пытаемся думать о проблеме, то есть о том, как упростить утомительные вещи.При столкновении с утомительными и повторяющимися проблемами у нас должны быть идеи для их решения. Для вещей, которые можно сделать с помощью программ, мы стараемся не повторять кирпичи. Таким образом, мы можем не только получить немного счастья от напряженной разработки, но и постепенно улучшать свои способности кодирования ~
Приведенное выше решение — это всего лишь идея, и конкретная реализация кода может быть инкапсулирована в соответствии со структурой проекта, фактической библиотекой запросов, на которую ссылаются, и бизнес-требованиями. Конечно, если у вас есть такие же потребности в бизнесе, как и у меня, приведенный выше код может соответствовать бизнесу, и я поместил код вgithub, вы можете использовать его для справки.