Vue 魔法师 —— 将 API “类化”

简介: 【类】和【接口】是 JavaScript 未来的方向,我们的 API 调用也将如此,朝着这个方向和业务模块同步分类、同步升级。本文讲的正是 —— Vue 魔法师,如何将 API “类化”?万物皆模块,万物可归类。闲言少叙,直接正题。

image.png

前言



【类】和【接口】是 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)


本次就到这里,我是掘金安东尼,人不狠,话也多!公众号【掘金安东尼】。

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

image.png


相关文章
|
2月前
|
JavaScript API
vue中api统一管理
【10月更文挑战第4天】
|
4月前
|
API 索引
String类下常用API
String类下常用API
49 1
|
4月前
|
前端开发 JavaScript 安全
入门Vue+.NET 8 Web Api记录(一)
入门Vue+.NET 8 Web Api记录(一)
166 4
|
4月前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
129 0
|
28天前
|
JavaScript 前端开发 API
Vue.js 3:探索组合式API带来的新变革
Vue.js 3:探索组合式API带来的新变革
|
28天前
|
JavaScript 前端开发 API
Vue.js 3中的Composition API:提升你的组件开发体验
Vue.js 3中的Composition API:提升你的组件开发体验
|
28天前
|
JavaScript 前端开发 API
Vue.js 3:深入探索组合式API的实践与应用
Vue.js 3:深入探索组合式API的实践与应用
|
2月前
|
JavaScript 前端开发 API
探索Vue.js 3的组合式API:一种更灵活的组件状态管理方式
【10月更文挑战第5天】探索Vue.js 3的组合式API:一种更灵活的组件状态管理方式
|
3月前
|
JSON 资源调度 JavaScript
Vue框架中Ajax请求的实现方式:使用axios库或fetch API
选择 `axios`还是 `fetch`取决于项目需求和个人偏好。`axios`提供了更丰富的API和更灵活的错误处理方式,适用于需要复杂请求配置的场景。而 `fetch`作为现代浏览器的原生API,使用起来更为简洁,但在旧浏览器兼容性和某些高级特性上可能略显不足。无论选择哪种方式,它们都能有效地在Vue应用中实现Ajax请求的功能。
48 4
|
2月前
|
API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
39 0