在 Vue.js 应用程序中构建 HTTP 客户端以实现高效的网络通信
现代 Web 应用高度依赖与 API 服务器和外部服务的网络通信。从实时数据到静态资源,一切都通过网络连接完成。因此,设计网络接口层或HTTP 客户端至关重要,它能帮助应用程序高效、稳定地调用 API 端点。本文将探讨如何在 Vue.js 应用中设计 HTTP 客户端并发出网络请求,并介绍一些最佳实践和技巧。
我们将详细探讨以下概念以及如何在我们的应用程序中实现它们。我更喜欢使用Axios,因为它提供了更大的灵活性和控制力,并且对浏览器和 Node.js 的支持非常出色。
- 使用 axios 实例创建 HTTP 客户端
- 构建 API 端点
- 在 Vuex 操作中发出网络请求
- 使用拦截器管理身份验证凭据
- 处理网络错误和日志记录
- 缓存和限流
在开始之前,请注意以下代码片段是为 Vue.js 开发者编写的。但是,它们也适用于 React 或任何其他前端库/框架。
这是“Vue应用架构设计”系列的第二部分。您可以在这里找到第一部分,其中我讨论了如何为大型Vue.js应用构建Vuex store。
1. 使用 axios 实例创建 HTTP 客户端
Axios 为所有 API 调用提供开箱即用的支持,允许我们使用 axios 实例来持久化配置。在我们的应用程序中,我们将使用 axios 实例作为 HTTP 客户端,并配置这些实例。如果您正在开发大型应用程序,则可能需要与不同的 API 端点通信。在这种情况下,我们可能需要创建多个 axios 实例,每个实例都有自己的配置,并将这些配置分别写入不同的文件中。
在您的项目中安装 axios
$ npm install --save axios
将 axios 导入到您的项目中
考虑到最佳实践,建议.env在开发大型应用程序时将 API URL 添加到文件中。在 Vue.js 应用程序中,为了能够在项目内部访问环境VUE_APP_变量,我们需要在其前面加上 `.env` 前缀。因此,如果您想保存`BASE_URL`,请在项目根目录下创建一个 `.env` 文件,并添加以下代码行。
VUE_APP_BASE_URL=https://myApiServerUrl.com
环境变量设置完成后,我们就可以在创建 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;
还有一点需要注意,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",
}
});
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
}
我们可以采用简单的目录结构来存储所有这些文件。
api/
├── httpClient.js --> HTTP Client with our configs
├── users.api.js
├── posts.api.js
└── comments.api.js
我们可以通过简单地导入它们,在 Vue.js 组件和 Vuex store 中使用这些组件。
import { getAllUsers, getUser } from '@/api/users.api';
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
}
在我们的 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>
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;
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);
6. 缓存和限流
Axios 适配器为 HttpClient 赋予了强大的功能。自定义适配器是一种简洁的方式,可以通过缓存和限流来增强应用程序的网络通信。我们将使用axios-extensions将缓存和限流适配器附加到我们的 HttpClient 上。
请注意,不建议在客户端进行缓存,因为服务器更了解数据何时发生变化。正如@darthvitalus指出的,最好设置缓存头来告知浏览器使用哪种缓存策略。如果您仍然想在客户端进行缓存,可以参考以下示例。
安装 axios-extensions
$ npm install --save axios-extensions
缓存
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);
})
设置好缓存适配器后,我们可以配置每个请求在首次请求后进行缓存。在定义端点的文件中,我们可以传递一个额外的参数,指示是否应该缓存响应。
const getUsers = () => httpClient.get('/users', { useCahe: true });
首次调用之后的所有后续调用都将从缓存中响应。
getUsers(); // actual network request and response gets cached
getUsers(); // from cache
getUsers(); // from cache
节流
在我们的应用场景中,限流指的是限制在特定时间内发出的请求数量。在大规模应用中,每次向服务器发出请求都会带来更大的计算成本,因此缓存是实现限流的一种方法。
如果偶尔会有新数据传入怎么办?在这种情况下,我们可以使用限流机制,先从缓存中获取数据一段时间,然后在指定的时间段过后再发起实际请求。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;
如果设置了限流,则在阈值时间段内发出的相同请求将从缓存中响应。只有在阈值时间段之后才会发出真正的请求。
getUsers(); // actual request
getUsers(); // responds from cache
getUsers(); // responds from cache
setTimeout(() => {
getUsers(); // threshold period passed, actual request.
}, 2*1000);
感谢阅读这篇文章💖。喜欢这篇文章吗?有什么反馈或建议吗?请点赞并留言。这能帮助我更好地了解大家的想法,并创作更多精彩的文章🙂。
接下来会发生什么?
在接下来的文章中,我们将更多地讨论如何从性能和生产力的角度构建大规模 Vue.js 应用程序。
您好!我是 Locale.ai 的一名用户体验工程师,负责为我们的 B2B 客户解决地理空间问题。如果您热爱为用户解决用户体验问题,热爱设计,并且渴望与充满热情的团队共事,欢迎查看 Locale 的招聘信息。想和我聊聊吗?您可以在Twitter、Instagram和GitHub
上找到我。
原文发布于haxzie.com
文章来源:https://dev.to/localeai/architecting-http-clients-in-vue-js-applications-for- effective-network-communication-1eec