#NodeJS

使用 axios 请求后端模型时,有一些最佳实践可以帮助你编写更健壮、可维护的代码。以下是一些常见的最佳实践:

1. 创建 API 服务层

axios 请求封装在专门的服务层中,而不是在组件中直接编写请求代码。这样可以提高代码的可复用性、可测试性并保持组件的清晰。

示例:

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
// api.js
import axios from 'axios';

// 创建 axios 实例
const apiClient = axios.create({
baseURL: 'https://your-api-endpoint.com',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}` // 如果需要身份验证
}
});

// 获取用户信息
export function getUserInfo(userId) {
return apiClient.get(`/users/${userId}`);
}

// 更新用户信息
export function updateUserInfo(userId, data) {
return apiClient.put(`/users/${userId}`, data);
}

// 创建用户
export function createUser(data) {
return apiClient.post('/users', data);
}

然后在 Vue 组件中使用这些 API 方法:

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
// UserProfile.vue
import { getUserInfo, updateUserInfo } from './api';

export default {
data() {
return {
user: {}
};
},
mounted() {
this.fetchUserInfo(1); // 获取用户信息
},
methods: {
async fetchUserInfo(userId) {
try {
const response = await getUserInfo(userId);
this.user = response.data;
} catch (error) {
console.error('Error fetching user info:', error);
}
},
async saveUserInfo() {
try {
const response = await updateUserInfo(this.user.id, this.user);
this.user = response.data;
} catch (error) {
console.error('Error saving user info:', error);
}
}
}
};

2. 错误处理

始终为 API 请求添加错误处理,避免应用崩溃并提供用户友好的反馈。你可以使用 try/catchasync/await)或者 then/catch(Promise)来捕获错误。

示例:

1
2
3
4
5
6
7
8
try {
const response = await axios.get('/api/endpoint');
// 成功处理
} catch (error) {
console.error('API请求失败', error);
// 错误提示
this.$toast.error('请求失败,请稍后重试');
}

你还可以根据错误的类型和状态码,做出不同的响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
axios.get('/api/endpoint')
.then(response => {
// 处理响应数据
})
.catch(error => {
if (error.response) {
// 请求已发出,且服务器响应了状态码
if (error.response.status === 401) {
// 处理未授权错误
} else if (error.response.status === 500) {
// 处理服务器错误
}
} else if (error.request) {
// 请求已发出,但没有响应
console.error('没有响应');
} else {
console.error('发生了其他错误', error.message);
}
});

3. 请求拦截器和响应拦截器

使用 axios 的拦截器来处理请求和响应。请求拦截器可以在发送请求之前做一些通用处理(例如添加认证信息),响应拦截器可以统一处理错误。

请求拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiClient.interceptors.request.use(
config => {
// 可以在请求头中添加认证信息或其他全局信息
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);

响应拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiClient.interceptors.response.use(
response => {
// 可以在这里做响应的统一处理,例如根据返回的状态码做全局的处理
return response;
},
error => {
// 统一错误处理
if (error.response.status === 401) {
// 处理未授权,可能是用户需要重新登录
alert('未授权,请重新登录');
}
return Promise.reject(error);
}
);

4. 请求的参数和数据结构

确保你发送到后端的请求数据结构与后端期望的模型一致。你可以通过 axiosparams(GET 请求)或 data(POST 请求)属性来绑定数据。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
// GET 请求
axios.get('/api/users', {
params: {
page: 1,
limit: 10
}
});

// POST 请求
axios.post('/api/users', {
name: 'John Doe',
email: '[email protected]'
});

5. 全局配置和环境变量

如果有多个接口或后端环境,使用环境变量来管理不同的 API 根路径、API 密钥等。

示例:

1
2
3
4
5
6
const apiClient = axios.create({
baseURL: process.env.VUE_APP_API_BASE_URL || 'https://your-api-endpoint.com',
headers: {
'Content-Type': 'application/json'
}
});

然后在 .env 文件中设置环境变量:

1
VUE_APP_API_BASE_URL=https://dev.api.com

6. 分页和懒加载

对于数据量大的接口,建议使用分页、懒加载等技术来优化性能。通过在请求中添加分页参数,将数据分批获取,避免一次性请求大量数据。

示例:

1
2
3
4
5
6
7
8
9
10
async fetchUsers(page = 1, limit = 20) {
try {
const response = await axios.get('/api/users', {
params: { page, limit }
});
this.users = response.data;
} catch (error) {
console.error(error);
}
}

7. 缓存和重复请求防止

如果某些请求会多次发送相同的数据,可以考虑缓存请求结果,或者在发送新请求之前,检查是否已发起了相同请求。

总结

  • 封装 API 服务层:将 axios 请求抽象成服务方法,方便管理和复用。
  • 错误处理:处理请求失败的情况,显示用户友好的错误信息。
  • 请求和响应拦截器:统一处理请求和响应,尤其是认证和错误处理。
  • 避免重复请求:缓存或防止发送重复的请求。
  • 分页和懒加载:提高性能,避免一次性加载大量数据。

这样,你的 axios 请求会更加清晰、可维护,并且具有良好的扩展性。

话不多说,先看效果,非常之暴力+魔幻。

很多年前看到b站的弹幕,就非常想开发一个可以全网弹幕的工具。
彼时,还没有ChatGPT这么强大的伙伴,而我,一个前端菜鸡,想完成这一切是非常难的。
而如今不同了,在ChatGPT的加持下,我,已然成长为一个StrongMan!

事情应该从前几天开始说起,这不,最近闲下来的我想给自己的博客的主机源码升级一下评论的插件,写着写着忽然想到,如果做成弹幕岂不是更好玩?然后又想起曾经想要做的全网弹幕插件,想到是不是可以用tampermonkey来实现。于是先用tampermonkey先简单实现了一版,但是又觉得但单调了。以来很多人不想用tampermonkey,也不想再为这个功能专门装个tampermonkey,此外既然是弹幕,多少得加上一些设置功能,设置特效啊啥的,所以就干脆写成独立的插件吧。然后就在ChatGPT的帮助下爆肝几个晚上开发了这个弹幕插件–弹幕局v1.0
在ChatGPT的帮助下,源码很快就写出来了。

安装了这个插件之后,在网页上按下”F4“即可唤出这个弹幕面板,输入内容就可以发送弹幕了。
值得一提的是,可以允许用户切换频道,后端我已经完全开源出来了(基于CloudFlare的Pages),源码在这里:https://github.com/Chorder/danmuju,你可以基于开源的后端源码,搭建一个只属于你和你小伙伴的专属私人弹幕频道,这样相对来说就更加私密和安全了。
这个开源的后端是基于CloudFlare的Pages构建的,如果你对Pages很熟悉,应该一看就明白,只需要克隆仓库过去,用wrangler 创建一个D1数据库,然后在pages项目上绑定数据库,配置从Github仓库构建,框架选Vue/Vite,即可。如果你对上述这些不是很熟悉,也不用着急,过段时间我会再详细补充一下文档。

弹幕局插件包含一个设置界面,这里可以开启和关闭弹幕,也可以设置弹幕的字体和字号、速度等,一开始甚至想要加上自定义css,不过想到css可能会引入很多的安全问题,就这样也够用了,那就先这样吧,以后的版本有时间可以再慢慢完善。

目前的弹幕可以横着飞,竖着非,斜着飞,嘎嘎乱飞,已经很暴力了,我个人很喜欢,哈哈!

对了,怎么安装:

目前火狐的插件已经上传了正在审核中,审核完成我挥第一时间在这里分享。Chrome的插件想要上传得支付5美金,算了算了,还是走开源路线吧。。。

所以想要尝鲜的小伙伴可以从Github直接下载客户端源码或者压缩包,临时加载插件,方法如下:

插件源码地址:

Chrome:https://github.com/Chorder/danmuju/tree/master/clients/danmuju-chrome
Firefox:https://github.com/Chorder/danmuju/tree/master/clients/danmuju-firefox

压缩包下载地址:

https://github.com/Chorder/danmuju/tree/master/clients

上面链接打开之后有两个压缩包:danmuju-chrome_1.0.zip 和 danmuju-firefox_1.0.zip,需要哪个版本就下载哪个版本。下载下来解压之后就能看到源代码,觉得哪里不爽就自己改(改完可以PR到仓库呀!)
尤其是如果觉得快捷键(目前是F4)用的不爽,可以修改插件源码content.js的第501行,修改成你想要的快捷键即可。

火狐,点击临时加载附加组件,选择danmuju-firefox_1.0.zip即可安装。

Chrome和Edge,打开开发模式,直接把压缩包下载下来解压,然后点击”加载解压缩的拓展“即可安装。

经过测试,Chrome版的zip是可以在Edge良好运行的。如果安装之后看不到弹幕,需要点开插件的设置面板,把弹幕开关开启一下。

安装好弹幕,打开百度或者这个网页,你应该可以看到我发的测试弹幕了。

最后关于全网弹幕这个思路,再简单讲几点

  1. 安全
    这个插件最应该防范的就是前端攻击,例如XSS、浏览器沙箱逃逸等。前者我已经测试过基本没啥问题,dom型和非dom型的xss都测试过,希望大佬们再帮忙测一测,如有bug希望即使反馈(也可以在Github直接提交PR修了,哈哈哈哈)
    而沙箱逃逸嘛这个。。但凡有这种漏洞想必遭殃的也不止这一个插件了。

  2. 可玩性
    这个东西我之所以想要开发,就是希望当我们在浏览任何网页的时候,都能有网友在旁边提供帮助,增加网页的交互性。

  3. 后续期待

希望这个东西可以给Web3.0时代增添更多的乐趣,我觉得弹幕是一个有趣的发明。我能想到的一些好玩的使用场景,比如说在配置内网地址192.168.x.x的路由器的时候,跨域弹幕会很好玩,它就像有个网友老司机在身边指导你。

目前服务端和客户端都开源在了https://github.com/Chorder/danmuju
希望多多支持,多多PR~

另外给插件写了个简单的官网,在这里,以后有重要内容会在官网上更新:https://danmuju.pages.dev/

在以root用户运行Puppeteer程序时,遇到一些错误,这里记录下解决的过程。
首先提示一个X11错误,这是因为我在服务器上运行Chrome,而服务器没有安装桌面环境导致的。
安装以下依赖项即可,以Debian系统为例:

1
apt-get install gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget

接着就是报了一个sandbox错误,报错内容是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(node:4963) UnhandledPromiseRejectionWarning: Error: Failed to launch chrome!
[0428/104254.469304:ERROR:zygote_host_impl_linux.cc(89)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.


TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md

at onClose (/root/monkey/node_modules/puppeteer/lib/Launcher.js:342:14)
at Interface.helper.addEventListener (/root/monkey/node_modules/puppeteer/lib/Launcher.js:331:50)
at emitNone (events.js:111:20)
at Interface.emit (events.js:208:7)
at Interface.close (readline.js:370:8)
at Socket.onend (readline.js:149:10)
at emitNone (events.js:111:20)
at Socket.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
(node:4963) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:4963) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

不了解具体的含义,但是从sandbox来看,应该是和Chrome的安全沙箱有关,单纯的想解决这个问题,只需要在代码中加入--no-sandbox启动Chrome的参数即可。

Example:

1
2
3
4
5
6
7
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({args: ['--no-sandbox']});
const page = await browser.newPage();
await page.goto("https://www.baidu.com");
await browser.close();
})()

Tips:

但是这样做可能会造成一些风险,主要是Chrome的sandbox会多一些安全特性(我理解的),如果是将Node的代码作为生产环境运行在产品中,还是需要谨慎一些的,确保请求的站点安全可信(最好不信)。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×