【类】和【接口】是 JavaScript 未来的方向,我们的 API 调用也将如此,朝着这个方向和业务模块同步分类、同步升级。本文讲的正是 —— Vue 魔法师,如何将 API “类化”?
万物皆模块,万物可归类。闲言少叙,直接正题。
咱这里直接 VueCLI 迅速快捷构建
1 |
vue create vue-api-module |
得到如下目录
1 2 3 4 5 |
--src ----store ------index.js ----App.vue ----main.js |
然后新建文件 api.js 如下,
1 2 3 4 5 6 7 |
--src ----services ------api.js ----store ------index.js ----App.vue ----main.js |
我们先创建一个基础类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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 }); } } |
然后,我们再创建一个子类:包含 fetch、get 只读方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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); } } } |
接着,我们再创建一个包含可读写方法的子类:post、put、delete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
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); } } } |
让我们看看两个简单的继承示例:
1 2 3 4 5 |
class UsersApiService extends ReadOnlyApiService { constructor() { super("users"); } } |
1 2 3 4 5 |
class PostsApiService extends ModelApiService { constructor() { super("posts"); } } |
【UsersApiService 类】继承了只读类 API —— ReadOnlyApiService,可以使用 fetch、get 两种方法。而 【PostsApiService 类】继承了读写类 API —— ModelApiService,可以使用 fetch、get、post、put、delete 五种方法。
我们也可以根据业务来写继承 API 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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:
1 2 3 4 5 |
export const $api = { users: new UsersApiService(), posts: new PostsApiService(), albums: new AlbumsApiService() }; |
在 Vue 项目中如何调用以上的 API 类?我们主要在 Vuex 和 components 中调用它:
1 2 3 4 5 6 7 8 9 |
--src ----plugins ------storePlugins.js ----services ------api.js ----store ------index.js ----App.vue ----main.js |
1 2 3 4 5 6 7 8 9 |
import { $api } from "@/services/api"; export default function(store) { try { store.$api = $api; } catch (e) { console.error(e); } } |
1 2 3 4 5 6 7 8 |
... import storePlugins from "@/plugins/storePlugins"; ... export default new Vuex.Store({ plugins: [storePlugins], state: { ... }); |
1 2 3 4 5 6 7 8 9 10 |
--src ----plugins ------mixins.js ------storePlugins.js ----services ------api.js ----store ------index.js ----App.vue ----main.js |
1 2 3 4 5 6 7 8 |
import Vue from "vue"; import { $api } from "@/services/api"; Vue.mixin({ computed: { $api: () => $api } }); |
1 2 3 |
... import "@/plugins/mixins"; ... |
OK!现在你就可以在 store 和 components 中调用了,例如:
1 2 3 4 5 6 7 8 9 |
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() |
可本地调试~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<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> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
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 也应该同步如此。一是思路清晰,跟着业务走;二是扩展性和复用性都更好;三是看起来就很高级……😀😀😀
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 获取文章 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) } ...... |
1 |
import {getPost,addPost,updatePost} from '@/services/api' |
1 2 3 4 5 |
class PostsApiService extends ModelApiService { constructor() { super("posts"); } } |
1 |
this.$api.posts.post(param) |
本次就到这里,我是掘金安东尼,人不狠,话也多!公众号【掘金安东尼】。
点赞小手动一动,你我一起向前冲~ 再会~
作者:掘金安东尼
链接:https://juejin.cn/post/6920422878500519950
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。