发布于 2026-01-05 0 阅读
0

在 Vue.js 应用程序中构建 HTTP 客户端以实现高效的网络通信

在 Vue.js 应用程序中构建 HTTP 客户端以实现高效的网络通信

现代 Web 应用高度依赖与 API 服务器和外部服务的网络通信。从实时数据到静态资源,一切都通过网络连接完成。因此,设计网络接口层或HTTP 客户端至关重要,它能帮助应用程序高效、稳定地调用 API 端点。本文将探讨如何在 Vue.js 应用中设计 HTTP 客户端并发出网络请求,并介绍一些最佳实践和技巧。

我们将详细探讨以下概念以及如何在我们的应用程序中实现它们。我更喜欢使用Axios,因为它提供了更大的灵活性和控制力,并且对浏览器和 Node.js 的支持非常出色。

  1. 使用 axios 实例创建 HTTP 客户端
  2. 构建 API 端点
  3. 在 Vuex 操作中发出网络请求
  4. 使用拦截器管理身份验证凭据
  5. 处理网络错误和日志记录
  6. 缓存和限流

在开始之前,请注意以下代码片段是为 Vue.js 开发者编写的。但是,它们也适用于 React 或任何其他前端库/框架。

这是“Vue应用架构设计”系列的第二部分。您可以在这里找到第一部分,其中我讨论了如何为大型Vue.js应用构建Vuex store


1. 使用 axios 实例创建 HTTP 客户端

Axios 为所有 API 调用提供开箱即用的支持,允许我们使用 axios 实例来持久化配置。在我们的应用程序中,我们将使用 axios 实例作为 HTTP 客户端,并配置这些实例。如果您正在开发大型应用程序,则可能需要与不同的 API 端点通信。在这种情况下,我们可能需要创建多个 axios 实例,每个实例都有自己的配置,并将这些配置分别写入不同的文件中。

在您的项目中安装 axios

$ npm install --save axios
Enter fullscreen mode Exit fullscreen mode

将 axios 导入到您的项目中

考虑到最佳实践,建议.env在开发大型应用程序时将 API URL 添加到文件中。在 Vue.js 应用程序中,为了能够在项目内部访问环境VUE_APP_变量,我们需要在其前面加上 `.env` 前缀。因此,如果您想保存`BASE_URL`,请在项目根目录下创建一个 `.env` 文件,并添加以下代码行。

VUE_APP_BASE_URL=https://myApiServerUrl.com
Enter fullscreen mode Exit fullscreen mode

环境变量设置完成后,我们就可以在创建 axios 实例时获取它们。此外,我们还可以将所有配置(包括请求头)传递给该实例,并使用该实例创建 HTTP 请求。

import axios from axios;

const httpClient = axios.create({
    baseURL: process.env.VUE_APP_BASE_URL,
    headers: {
        "Content-Type": "application/json",
        // anything you want to add to the headers
    }
});

export default httpClient;
Enter fullscreen mode Exit fullscreen mode

还有一点需要注意,Axios 默认的超时时间为0,这意味着没有超时限制。但在大多数情况下,我们需要在应用程序中设置请求超时时间以及重试周期。我们将在以下章节讨论如何重试失败的请求,但您可以在创建 httpClient 时更改其默认超时时间。

const httpClient = axios.create({
    baseURL: process.env.VUE_APP_BASE_URL,
    timeout: 1000, // indicates, 1000ms ie. 1 second
    headers: {
        "Content-Type": "application/json",
    }
});
Enter fullscreen mode Exit fullscreen mode

2. 构建 API 端点

根据 REST 设计原则,我们的大多数端点可能都关联着 CRUD 操作。因此,将端点及其所有请求方法分组在一起是组织 API 调用的一种方法。我们可以导入所需的 HTTP 客户端,并将所有必需的请求导出为方法。以下示例展示了如何将所有相关请求分组Users到一个文件中。

import httpClient from './httpClient';

const END_POINT = '/users';


const getAllUsers = () => httpClient.get(END_POINT);

// you can pass arguments to use as request parameters/data
const getUser = (user_id) => httpClient.get(END_POINT, { user_id });
// maybe more than one..
const createUser = (username, password) => httpClient.post(END_POINT, { username, password });

export {
    getAllUsers,
    getUser,
    createUser
}
Enter fullscreen mode Exit fullscreen mode

我们可以采用简单的目录结构来存储所有这些文件。

api/
  ├── httpClient.js  --> HTTP Client with our configs
  ├── users.api.js
  ├── posts.api.js
  └── comments.api.js
Enter fullscreen mode Exit fullscreen mode

我们可以通过简单地导入它们,在 Vue.js 组件和 Vuex store 中使用这些组件。

import { getAllUsers, getUser } from '@/api/users.api';
Enter fullscreen mode Exit fullscreen mode

3. 在 Vuex actions 中发出网络请求

将所有业务逻辑(包括所有网络请求)移至 Vuex store 中,可以使视图组件独立运行。我们可以使用 store 中的 actions 来获取数据并将其存储在状态对象中。Vuex actions 默认是同步的,但只有将 actions 设置为异步或返回 Promise 才能知道一个 actions 是否已完成。我们可以使用 actions 通过 mutations 将数据提交到 store。以下是一个包​​含 actions 的 store 模块示例,该模块会获取数据并将其提交到 store。

/*
*   store/modules/users.module.js
*/

// import the api endpoints
import { getAllUsers } from "@/api/users.api"

const state = {
    users: []
}

const getters = {
    getUsers(state) {
        return state.users;
    }
}

const actions = {
    async fetchUsers({ commit }) {
            try {
                const response = await getAllUsers();
                commit('SET_USERS', response.data);
            } catch (error) {
                // handle the error here
            }    
        });
    }
}

const mutations = {
    SET_USERS(state, data) {
        state.users = data;
    }
}

export default {
    namespaced: true,
    state,
    getters,
    actions,
    mutations
}
Enter fullscreen mode Exit fullscreen mode

在我们的 Vue.js 组件中,我们可以先检查 store 中是否有数据,从而避免额外的网络请求。如果没有数据,我们可以使用 actions 来获取数据。

<template>
    <!-- Your template here -->
</template>

<script>
import { mapActions, mapGetters } from "vuex";

export default {
    data() {
        return {
            isLoading: false;
        }
    },
    computed: {
        ...mapGetters('Users', ['getUsers'])
    },
    methods: {
        ...mapActions('Users', ['fetchUsers'])
    },
    async mounted(): {
        // Make network request if the data is empty
        if ( this.getUsers.length === 0 ) {
            // set loading screen
            this.isLoading = true;
            await this.fetchUsers();
            this.isLoading = false;
        }
    }
}
</script>
Enter fullscreen mode Exit fullscreen mode

4. 使用拦截器管理身份验证凭据

创建拦截器来注入请求头是利用身份验证凭据保护请求的简便方法。如果您正在构建一个需要用户登录的应用程序,我们可以使用拦截器将身份验证令牌注入到每个请求的请求头中。在我们的httpClient.js文件中,我们可以添加以下代码来创建请求拦截器。

import axios from axios;

const httpClient = axios.create({
    baseURL: process.env.VUE_APP_BASE_URL,
    timeout: 5000
});

const getAuthToken = () => localStorage.getItem('token');

const authInterceptor = (config) => {
    config.headers['Authorization'] = getAuthToken();
    return config;
}

httpClient.interceptors.request.use(authInterceptor);

export default httpClient;
Enter fullscreen mode Exit fullscreen mode

5. 处理网络错误和日志记录

每次请求都检查状态并记录错误并不理想response.status === 500。而 axios 提供了拦截错误响应的功能,这使得查找错误、记录错误或向用户显示提示信息(例如服务器出错)成为可能。此外,如果请求未经授权或服务器提示会话已过期,我们还可以利用此功能将用户从应用程序中注销。

在下面的示例中,我使用vue-notifications在屏幕上显示小型通知。

// interceptor to catch errors
const errorInterceptor = error => {
    // check if it's a server error
    if (!error.response) {
      notify.warn('Network/Server error');
      return Promise.reject(error);
    }

    // all the other error responses
    switch(error.response.status) {
        case 400:
            console.error(error.response.status, error.message);
            notify.warn('Nothing to display','Data Not Found');
            break;

        case 401: // authentication error, logout the user
            notify.warn( 'Please login again', 'Session Expired');
            localStorage.removeItem('token');
            router.push('/auth');
            break;

        default:
            console.error(error.response.status, error.message);
            notify.error('Server Error');

    }
    return Promise.reject(error);
}

// Interceptor for responses
const responseInterceptor = response => {
    switch(response.status) {
        case 200: 
            // yay!
            break;
        // any other cases
        default:
            // default case
    }

    return response;
}

httpClient.interceptors.response.use(responseInterceptor, errorInterceptor);

Enter fullscreen mode Exit fullscreen mode

6. 缓存和限流

Axios 适配器为 HttpClient 赋予了强大的功能。自定义适配器是一种简洁的方式,可以通过缓存和限流来增强应用程序的网络通信。我们将使用axios-extensions将缓存和限流适配器附加到我们的 HttpClient 上。

请注意,不建议在客户端进行缓存,因为服务器更了解数据何时发生变化。正如@darthvitalus指出的,最好设置缓存头来告知浏览器使用哪种缓存策略。如果您仍然想在客户端进行缓存,可以参考以下示例。

安装 axios-extensions

$ npm install --save axios-extensions
Enter fullscreen mode Exit fullscreen mode

缓存

import axios from 'axios';
import { cacheAdapterEnhancer } from 'axios-extensions';

const cacheConfig = {
    enabledByDefault: false, 
    cacheFlag: 'useCache'
}

const httpClient = axios.create({
    baseURL: process.env.VUE_APP_BASE_URL,
    headers: {
        'Cache-Control': 'no-cache'
    },
    adapter: cacheAdapterEnhancer(axios.defaults.adapter, cacheConfig);
})
Enter fullscreen mode Exit fullscreen mode

设置好缓存适配器后,我们可以配置每个请求在首次请求后进行缓存。在定义端点的文件中,我们可以传递一个额外的参数,指示是否应该缓存响应。

const getUsers = () => httpClient.get('/users', { useCahe: true });
Enter fullscreen mode Exit fullscreen mode

首次调用之后的所有后续调用都将从缓存中响应。

getUsers(); // actual network request and response gets cached
getUsers(); // from cache
getUsers(); // from cache
Enter fullscreen mode Exit fullscreen mode

节流

在我们的应用场景中,限流指的是限制在特定时间内发出的请求数量。在大规模应用中,每次向服务器发出请求都会带来更大的计算成本,因此缓存是实现限流的一种方法。

如果偶尔会有新数据传入怎么办?在这种情况下,我们可以使用限流机制,先从缓存中获取数据一段时间,然后在指定的时间段过后再发起实际请求。Axios -extensions提供了一个throttleAdapterEnhancer,可以用来限制应用程序中的网络请求。如果我们使用限流机制,就可以避免使用持久缓存。

请注意,不建议对时效性数据使用限流。如果您的数据频繁更新,只有您的服务器知道这些数据。请改用缓存头来告知浏览器应使用哪种缓存策略。

import axios from 'axios';
import { throttleAdapterEnhancer } from 'axios-extensions';

const throttleConfig = {
    threshold: 2*1000 // 2 seconds
}

const httpClient = axios.create({
    baseURL: process.env.VUE_APP_BASE_URL,
    adapter: throttleAdapterEnhancer(axios.defaults.adapter, throttleConfig)
});

export default httpClient;
Enter fullscreen mode Exit fullscreen mode

如果设置了限流,则在阈值时间段内发出的相同请求将从缓存中响应。只有在阈值时间段之后才会发出真正的请求。

getUsers(); // actual request
getUsers(); // responds from cache
getUsers(); // responds from cache


setTimeout(() => {
    getUsers(); // threshold period passed, actual request.
}, 2*1000);
Enter fullscreen mode Exit fullscreen mode

感谢阅读这篇文章💖。喜欢这篇文章吗?有什么反馈或建议吗?请点赞并留言。这能帮助我更好地了解大家的想法,并创作更多精彩的文章🙂。

接下来会发生什么?

在接下来的文章中,我们将更多地讨论如何从性能和生产力的角度构建大规模 Vue.js 应用程序。


您好!我是 Locale.ai 的一名用户体验工程师,负责为我们的 B2B 客户解决地理空间问题。如果您热爱为用户解决用户体验问题,热爱设计,并且渴望与充满热情的团队共事,欢迎查看 Locale 的招聘信息想和我聊聊吗?您可以在TwitterInstagramGitHub
上找到我

原文发布于haxzie.com

文章来源:https://dev.to/localeai/architecting-http-clients-in-vue-js-applications-for- effective-network-communication-1eec