前言
【类】和【接口】是 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
- 先创建一个 storePlugins.js:
--src ----plugins ------storePlugins.js ----services ------api.js ----store ------index.js ----App.vue ----main.js
- 在 storePlugins.js 中引入 api.js:
import { $api } from "@/services/api"; export default function(store) { try { store.$api = $api; } catch (e) { console.error(e); } }
- 在 src/store/index.js 中引入该插件:
... import storePlugins from "@/plugins/storePlugins"; ... export default new Vuex.Store({ plugins: [storePlugins], state: { ... });
mixins
- 创建 mixins.js 在如下位置:
--src ----plugins ------mixins.js ------storePlugins.js ----services ------api.js ----store ------index.js ----App.vue ----main.js
- mixin.js:
import Vue from "vue"; import { $api } from "@/services/api"; Vue.mixin({ computed: { $api: () => $api } });
- 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)
本次就到这里,我是掘金安东尼,人不狠,话也多!公众号【掘金安东尼】。
点赞小手动一动,你我一起向前冲~ 再会~