最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue 魔法师 —— 将 API “类化”

    正文概述 掘金(掘金安东尼)   2021-01-22   566

    前言

    【类】和【接口】是 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 “类化”


    起源地下载网 » Vue 魔法师 —— 将 API “类化”

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元