7

实战,Vue 项目中,将 API “类化”

 1 year ago
source link: https://blog.51cto.com/u_13961087/5285455
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

实战,Vue 项目中,将 API “类化”

原创

掘金安东尼 2022-05-10 13:14:02 ©著作权

文章标签 json ios 子类 文章分类 JavaScript 前端开发 阅读数207

【类】和【接口】是 JavaScript 未来的方向,我们的 API 调用也将如此,朝着这个方向和业务模块同步分类、同步升级。本文讲的正是 —— Vue 魔法师,如何将 API “类化”?

万物皆模块,万物可归类。闲言少叙,直接正题。

分类 API

  • 环境: Vue2+ 或 Vue3+

咱这里直接 VueCLI 迅速快捷构建

vue create vue-api-module

得到如下目录

--src
----store
------index.js
----App.vue
----main.js

然后新建文件 api.js 如下,

--src
----services
------api.js
----store
------index.js
----App.vue
----main.js

基础类 API

我们先创建一个基础类如下:

class BaseApiService {
baseUrl = "https://jsonplaceholder.typicode.com";
resource;

constructor(resource) {
if (!resource) throw new Error("Resource is not provided");
this.resource = resource;
}

getUrl(id = "") {
return `${this.baseUrl}/${this.resource}/${id}`;
}

handleErrors(err) {
// 处理错误:
console.log({ message: "Errors is handled here", err });
}
}
  • baseUrl:设置请求根路径;
  • resource:来源;
  • getUrl:获取链接函数,包含 baseUrl、resource、id;
  • handleErrors:处理错误函数;

只读类 API

然后,我们再创建一个子类:包含 fetch、get 只读方法。

class ReadOnlyApiService extends BaseApiService {
constructor(resource) {
super(resource);
}
async fetch(config = {}) {
try {
const response = await fetch(this.getUrl(), config);
return await response.json();
} catch (err) {
this.handleErrors(err);
}
}
async get(id) {
try {
if (!id) throw Error("Id 不合法");
const response = await fetch(this.getUrl(id));
return await response.json();
} catch (err) {
this.handleErrors(err);
}
}
}
  • fetch:获取数据;
  • get:通过 id 获取数据;

写入类 API

接着,我们再创建一个包含可读写方法的子类:post、put、delete

class ModelApiService extends ReadOnlyApiService {
constructor(resource) {
super(resource);
}
async post(data = {}) {
try {
const response = await fetch(this.getUrl(), {
method: "POST",
body: JSON.stringify(data)
});
const { id } = response.json();
return id;
} catch (err) {
this.handleErrors(err);
}
}
async put(id, data = {}) {
if (!id) throw Error("Id 不合法");
try {
const response = await fetch(this.getUrl(id), {
method: "PUT",
body: JSON.stringify(data)
});
const { id: responseId } = response.json();
return responseId;
} catch (err) {
this.handleErrors(err);
}
}
async delete(id) {
if (!id) throw Error("Id 不合法");
try {
await fetch(this.getUrl(id), {
method: "DELETE"
});
return true;
} catch (err) {
this.handleErrors(err);
}
}
}
  • post:创建数据;
  • put:更新数据;
  • delete:删除数据;

让我们看看两个简单的继承示例:

class UsersApiService extends ReadOnlyApiService {
constructor() {
super("users");
}
}
class PostsApiService extends ModelApiService {
constructor() {
super("posts");
}
}

【UsersApiService 类】继承了只读类 API —— ReadOnlyApiService,可以使用 fetch、get 两种方法。而 【PostsApiService 类】继承了读写类 API —— ModelApiService,可以使用 fetch、get、post、put、delete 五种方法。

我们也可以根据业务来写继承 API 类:

class AlbumsApiService extends ModelApiService {
constructor() {
super("albums");
}
async uploadImage() {
/*
这里可以写你的上传图片逻辑
*/
console.log({ message: "图片上传成功!" });
return true;
}

async triggerError() {
try {
throw Error(" API 模块调用错误!");
} catch (err) {
this.handleErrors(err);
}
}
}

我们在 api.js 导出这些 API:

export const $api = {
users: new UsersApiService(),
posts: new PostsApiService(),
albums: new AlbumsApiService()
};

分类 API 实践

在 Vue 项目中如何调用以上的 API 类?我们主要在 Vuex 和 components 中调用它:

storePlugins

  1. 先创建一个 storePlugins.js:
--src
----plugins
------storePlugins.js
----services
------api.js
----store
------index.js
----App.vue
----main.js
  1. 在 storePlugins.js 中引入 api.js:
import { $api } from "@/services/api";

export default function(store) {
try {
store.$api = $api;
} catch (e) {
console.error(e);
}
}
  1. 在 src/store/index.js 中引入该插件:
...
import storePlugins from "@/plugins/storePlugins";
...
export default new Vuex.Store({
plugins: [storePlugins],
state: {
...
});

mixins

  1. 创建 mixins.js 在如下位置:
--src
----plugins
------mixins.js
------storePlugins.js
----services
------api.js
----store
------index.js
----App.vue
----main.js
  1. mixin.js:
import Vue from "vue";
import { $api } from "@/services/api";

Vue.mixin({
computed: {
$api: () => $api
}
});
  1. main.js 全局引入 mixins:
...
import "@/plugins/mixins";
...

OK!现在你就可以在 store 和 components 中调用了,例如:

this.$api.{resource}.{method}
------
this.$api.users.fetch({})
this.$api.users.get(1)
this.$api.posts.post(post)
this.$api.posts.put(post)
this.$api.posts.delete(1)
this.$api.albums.uploadImage()
this.$api.albums.triggerError()

可本地调试~

  • components 中
<template>
<div id="app">
<h1>Vue API “类化” 示例</h1>
<p>请打开控制台</p>
<p>检查 Vuex store</p>
</div>
</template>

<script>
export default {
name: "App",
async created() {
// 获取用户资料
await this.$store.dispatch("fetchUsers");
const users = await this.$api.users.fetch({});
console.log({ message: "Users from the component", users });
// 根据 id 获取用户资料
const user = await this.$api.users.get(1);
console.log({ message: "User with id: 1", user });
// 创建新文章
let post = {
userId: 1,
title: "测试文章",
body:
"这是一篇测试文章"
};
await this.$store.dispatch("createPost", post);
// 更新文章
post = { ...post, id: 1 };
await this.$store.dispatch("updatePost", post);
// 删除文章
await this.$api.posts.delete(post.id);
console.log({ message: "成功删除文章!" });
// 执行自定义方法
await this.$api.albums.uploadImage();
// 执行自定义方法
await this.$api.albums.triggerError();
}
};
</script>

<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
  • store 中
import Vue from "vue";
import Vuex from "vuex";
import storePlugins from "@/plugins/storePlugins";

Vue.use(Vuex);

const ADD_USERS = "ADD_USERS";
const ADD_POST = "ADD_POST";
const UPDATE_POST = "UPDATE_POST";

export default new Vuex.Store({
plugins: [storePlugins],
state: {
users: [],
posts: []
},
mutations: {
[ADD_USERS](state, users) {
state.users = users;
},
[ADD_POST](state, post) {
state.posts = [...state.posts, post];
},
[UPDATE_POST](state, post) {
const index = state.posts.findIndex(({ id }) => id === post.id);
if (!~index) state.posts.splice(index, 1, post);
}
},
actions: {
async fetchUsers({ commit }, config) {
const users = await this.$api.users.fetch(config);
commit(ADD_USERS, users);
console.log({ message: "Vuex 中的用户数据", users });
},

async createPost({ commit }, post) {
const id = await this.$api.posts.post(post);
commit(ADD_POST, { ...post, id });
console.log({ message: "创建文章", post: { ...post, id } });
},

async updatePost({ commit }, post) {
const id = await this.$api.posts.put(post.id, post);
commit(UPDATE_POST, post);
console.log({ message: "更新文章", post: { post, id } });
}
}
});

为什么要这么写?

本瓜以为:如果你的业务是按照这种类的方式有作区分,那么 API 也应该同步如此。一是思路清晰,跟着业务走;二是扩展性和复用性都更好;三是看起来就很高级......😀😀😀

  • 普通
// 获取文章
export const getPost = params => {
return axios.get(`/interface/getPost/`)
}

// 新增文章
export const addPost = params => {
return axios.post(`/interface/addPost`, params)
}

// 修改文章
export const updatePost = params => {
return axios.post(`/interface/updatePost/`, params)
}

......
import {getPost,addPost,updatePost} from '@/services/api'
  • 高级
class PostsApiService extends ModelApiService {
constructor() {
super("posts");
}
}
this.$api.posts.post(param)

点赞小手动一动,你我一起向前冲~ 再会~

实战,Vue 项目中,将 API “类化”_json

  • 1
  • 收藏
  • 评论
  • 分享
  • 举报

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK