PocketBase与前端框架集成:React、Vue、Angular实战
·
PocketBase与前端框架集成:React、Vue、Angular实战
【免费下载链接】pocketbase 开源的实时后端,仅用1个文件实现。 项目地址: https://gitcode.com/GitHub_Trending/po/pocketbase
引言:为什么选择PocketBase作为后端解决方案?
还在为复杂的后端架构和数据库配置而头疼?还在为API接口的繁琐开发而烦恼?PocketBase(口袋数据库)作为一款开源的实时后端解决方案,仅用单个文件即可提供完整的数据库管理、用户认证、文件存储和实时订阅功能。本文将深入探讨如何将PocketBase与主流前端框架(React、Vue、Angular)进行无缝集成,助你快速构建全栈应用。
读完本文,你将掌握:
- ✅ PocketBase核心API架构与功能特性
- ✅ React应用集成PocketBase的完整实践
- ✅ Vue.js项目对接PocketBase的最佳方案
- ✅ Angular应用使用PocketBase的配置指南
- ✅ 实时数据订阅与状态管理的高级技巧
PocketBase核心架构解析
API端点设计
PocketBase采用RESTful风格的API设计,主要端点包括:
认证机制
PocketBase支持多种认证方式:
| 认证方式 | 端点 | 适用场景 |
|---|---|---|
| 密码认证 | /api/collections/:collection/auth-with-password |
传统用户名密码登录 |
| OAuth2 | /api/collections/:collection/auth-with-oauth2 |
第三方登录集成 |
| Token刷新 | /api/collections/:collection/auth-refresh |
保持会话活跃 |
| 文件访问Token | /api/files/token |
保护文件访问权限 |
React集成实战
环境配置与SDK安装
首先安装PocketBase JavaScript SDK:
npm install pocketbase
# 或
yarn add pocketbase
基础配置与初始化
创建PocketBase客户端实例:
import PocketBase from 'pocketbase';
// 初始化客户端
const pb = new PocketBase('http://127.0.0.1:8090');
// 配置自动Token刷新
pb.autoCancellation(false); // 禁用自动请求取消
export default pb;
认证管理组件
创建React认证上下文:
import React, { createContext, useContext, useEffect, useState } from 'react';
import pb from '../lib/pocketbase';
const AuthContext = createContext();
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth必须在AuthProvider内部使用');
}
return context;
};
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(pb.authStore.record);
const [token, setToken] = useState(pb.authStore.token);
useEffect(() => {
// 监听认证状态变化
const unsubscribe = pb.authStore.onChange((token, record) => {
setToken(token);
setUser(record);
});
return () => unsubscribe();
}, []);
const login = async (email, password) => {
try {
const authData = await pb.collection('users').authWithPassword(email, password);
return authData;
} catch (error) {
throw new Error(error.message);
}
};
const logout = () => {
pb.authStore.clear();
};
const value = {
user,
token,
login,
logout,
isAuthenticated: pb.authStore.isValid
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
CRUD操作封装
创建数据操作Hook:
import { useState, useCallback } from 'react';
import pb from '../lib/pocketbase';
export const useCollection = (collectionName) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchRecords = useCallback(async (page = 1, perPage = 30, filters = {}) => {
setLoading(true);
setError(null);
try {
const result = await pb.collection(collectionName).getList(page, perPage, {
filter: Object.entries(filters)
.map(([key, value]) => `${key} ~ "${value}"`)
.join(' && '),
sort: '-created'
});
setData(result.items);
return result;
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
}, [collectionName]);
const createRecord = useCallback(async (recordData) => {
try {
const record = await pb.collection(collectionName).create(recordData);
setData(prev => [...prev, record]);
return record;
} catch (err) {
setError(err.message);
throw err;
}
}, [collectionName]);
const updateRecord = useCallback(async (id, recordData) => {
try {
const record = await pb.collection(collectionName).update(id, recordData);
setData(prev => prev.map(item => item.id === id ? record : item));
return record;
} catch (err) {
setError(err.message);
throw err;
}
}, [collectionName]);
const deleteRecord = useCallback(async (id) => {
try {
await pb.collection(collectionName).delete(id);
setData(prev => prev.filter(item => item.id !== id));
} catch (err) {
setError(err.message);
throw err;
}
}, [collectionName]);
return {
data,
loading,
error,
fetchRecords,
createRecord,
updateRecord,
deleteRecord,
refetch: () => fetchRecords(1, 30)
};
};
实时数据订阅
实现实时数据更新:
import { useEffect } from 'react';
import pb from '../lib/pocketbase';
export const useRealtime = (collectionName, callback) => {
useEffect(() => {
const unsubscribe = pb.collection(collectionName).subscribe('*', (e) => {
callback(e);
});
return () => {
unsubscribe();
};
}, [collectionName, callback]);
};
// 在组件中使用
const MyComponent = () => {
const [records, setRecords] = useState([]);
useRealtime('posts', (e) => {
switch (e.action) {
case 'create':
setRecords(prev => [...prev, e.record]);
break;
case 'update':
setRecords(prev => prev.map(item =>
item.id === e.record.id ? e.record : item
));
break;
case 'delete':
setRecords(prev => prev.filter(item => item.id !== e.record.id));
break;
}
});
// ... 组件其余部分
};
Vue.js集成方案
Vue 3 Composition API集成
创建PocketBase插件:
// plugins/pocketbase.js
import PocketBase from 'pocketbase';
import { ref } from 'vue';
export default {
install: (app, options) => {
const pb = new PocketBase(options.baseUrl || 'http://localhost:8090');
const user = ref(pb.authStore.record);
const token = ref(pb.authStore.token);
// 监听认证状态变化
pb.authStore.onChange((newToken, newRecord) => {
token.value = newToken;
user.value = newRecord;
});
app.provide('$pb', pb);
app.provide('$user', user);
app.provide('$token', token);
}
};
在main.js中配置
import { createApp } from 'vue';
import App from './App.vue';
import pocketbase from './plugins/pocketbase';
const app = createApp(App);
app.use(pocketbase, {
baseUrl: import.meta.env.VITE_POCKETBASE_URL
});
app.mount('#app');
组合式函数封装
// composables/usePocketBase.js
import { inject } from 'vue';
export const usePocketBase = () => {
const pb = inject('$pb');
const user = inject('$user');
const token = inject('$token');
const login = async (email, password) => {
try {
return await pb.collection('users').authWithPassword(email, password);
} catch (error) {
throw new Error(error.message);
}
};
const logout = () => {
pb.authStore.clear();
};
return {
pb,
user,
token,
login,
logout,
isAuthenticated: pb.authStore.isValid
};
};
// composables/useCollection.js
import { ref } from 'vue';
import { usePocketBase } from './usePocketBase';
export const useCollection = (collectionName) => {
const { pb } = usePocketBase();
const data = ref([]);
const loading = ref(false);
const error = ref(null);
const fetchRecords = async (page = 1, perPage = 30, filters = {}) => {
loading.value = true;
error.value = null;
try {
const result = await pb.collection(collectionName).getList(page, perPage, {
filter: Object.entries(filters)
.map(([key, value]) => `${key} ~ "${value}"`)
.join(' && '),
sort: '-created'
});
data.value = result.items;
return result;
} catch (err) {
error.value = err.message;
throw err;
} finally {
loading.value = false;
}
};
// ... 其他CRUD操作类似React版本
return {
data,
loading,
error,
fetchRecords
};
};
Vue组件中使用
<template>
<div>
<div v-if="loading">加载中...</div>
<div v-else-if="error">错误: {{ error }}</div>
<div v-else>
<div v-for="item in data" :key="item.id">
{{ item.title }}
</div>
</div>
</div>
</template>
<script setup>
import { useCollection } from '../composables/useCollection';
const { data, loading, error, fetchRecords } = useCollection('posts');
// 组件挂载时获取数据
onMounted(() => {
fetchRecords();
});
</script>
Angular集成指南
安装与配置
首先安装PocketBase SDK:
ng add pocketbase
# 或
npm install pocketbase
创建PocketBase服务
// services/pocketbase.service.ts
import { Injectable } from '@angular/core';
import PocketBase from 'pocketbase';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class PocketBaseService {
private pb: PocketBase;
private userSubject = new BehaviorSubject<any>(null);
private tokenSubject = new BehaviorSubject<string>('');
public user$ = this.userSubject.asObservable();
public token$ = this.tokenSubject.asObservable();
constructor() {
this.pb = new PocketBase('http://localhost:8090');
// 初始化状态
this.userSubject.next(this.pb.authStore.record);
this.tokenSubject.next(this.pb.authStore.token || '');
// 监听认证状态变化
this.pb.authStore.onChange((token, record) => {
this.tokenSubject.next(token || '');
this.userSubject.next(record);
});
}
get client(): PocketBase {
return this.pb;
}
async login(email: string, password: string): Promise<any> {
try {
return await this.pb.collection('users').authWithPassword(email, password);
} catch (error) {
throw new Error(error.message);
}
}
logout(): void {
this.pb.authStore.clear();
}
get isAuthenticated(): boolean {
return this.pb.authStore.isValid;
}
}
创建数据服务
// services/data.service.ts
import { Injectable } from '@angular/core';
import { PocketBaseService } from './pocketbase.service';
import { BehaviorSubject, catchError, finalize, from, Observable, tap } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DataService {
private loadingSubject = new BehaviorSubject<boolean>(false);
private errorSubject = new BehaviorSubject<string>('');
public loading$ = this.loadingSubject.asObservable();
public error$ = this.errorSubject.asObservable();
constructor(private pbService: PocketBaseService) {}
getCollection<T>(collectionName: string, page = 1, perPage = 30, filters: any = {}): Observable<T[]> {
this.loadingSubject.next(true);
this.errorSubject.next('');
return from(
this.pbService.client.collection(collectionName).getList(page, perPage, {
filter: this.buildFilterString(filters),
sort: '-created'
})
).pipe(
tap(result => result.items),
catchError(error => {
this.errorSubject.next(error.message);
throw error;
}),
finalize(() => this.loadingSubject.next(false))
);
}
private buildFilterString(filters: any): string {
return Object.entries(filters)
.filter(([_, value]) => value !== null && value !== undefined && value !== '')
.map(([key, value]) => `${key} ~ "${value}"`)
.join(' && ');
}
// 其他CRUD操作方法...
}
在组件中使用
// components/post-list.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from '../services/data.service';
import { PocketBaseService } from '../services/pocketbase.service';
@Component({
selector: 'app-post-list',
templateUrl: './post-list.component.html',
styleUrls: ['./post-list.component.css']
})
export class PostListComponent implements OnInit {
posts: any[] = [];
loading = false;
error = '';
constructor(
private dataService: DataService,
private pbService: PocketBaseService
) {}
ngOnInit(): void {
this.loadPosts();
}
loadPosts(): void {
this.loading = true;
this.dataService.getCollection('posts').subscribe({
next: (posts) => {
this.posts = posts;
this.error = '';
},
error: (error) => {
this.error = error.message;
},
complete: () => {
this.loading = false;
}
});
}
// 实时订阅示例
setupRealtimeSubscription(): void {
this.pbService.client.collection('posts').subscribe('*', (e) => {
console.log('实时更新:', e);
// 处理实时数据更新
});
}
}
高级特性与最佳实践
文件上传与管理
// 文件上传示例
const uploadFile = async (file, collectionName, recordId, fieldName) => {
const formData = new FormData();
formData.append(fieldName, file);
try {
const record = await pb.collection(collectionName).update(recordId, formData);
return record;
} catch (error) {
throw new Error(`文件上传失败: ${error.message}`);
}
};
// 获取文件URL
const getFileUrl = (record, fileName, thumb = '0x0') => {
return pb.files.getUrl(record, fileName, { thumb });
};
错误处理与重试机制
// 高级错误处理
const withRetry = async (operation, maxRetries = 3, delay = 1000) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries) throw error;
// 指数退避重试
await new Promise(resolve =>
setTimeout(resolve, delay * Math.pow(2, attempt - 1))
);
}
}
};
// 使用示例
const fetchDataWithRetry = () =>
withRetry(() => pb.collection('posts').getList(1, 10));
性能优化技巧
安全最佳实践
认证安全
// Token自动刷新
const setupTokenRefresh = () => {
setInterval(async () => {
if (pb.authStore.isValid) {
try {
await pb.collection('users').authRefresh();
} catch (error) {
console.warn('Token刷新失败:', error);
pb.authStore.clear();
}
}
}, 15 * 60 * 1000); // 每15分钟刷新一次
};
输入验证与清理
// 数据验证工具
const validateInput = (data, schema) => {
const errors = {};
Object.keys(schema).forEach(key => {
const validator = schema[key];
const value = data[key];
if (!validator(value)) {
errors[key] = `字段 ${key} 验证失败`;
}
});
return Object.keys(errors).length > 0 ? errors : null;
};
// 使用示例
const userSchema = {
email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
password: (value) => value && value.length >= 6
};
部署与生产环境配置
环境变量配置
// 环境敏感的配置
const getPocketBaseConfig = () => {
const env = process.env.NODE_ENV || 'development';
const configs = {
development: {
url: 'http://localhost:8090',
autoCancel: true
},
production: {
url: 'https://api.yourdomain.com',
autoCancel: false
},
test: {
url: 'http://localhost:8091',
autoCancel: true
}
};
return configs[env];
};
// 初始化配置
const config = getPocketBaseConfig();
const pb = new PocketBase(config.url);
pb.autoCancellation(config.autoCancel);
监控与日志
// 请求拦截器
pb.beforeSend = function (url, options) {
console.log(`请求: ${url}`, options);
return { url, options };
};
【免费下载链接】pocketbase 开源的实时后端,仅用1个文件实现。 项目地址: https://gitcode.com/GitHub_Trending/po/pocketbase
更多推荐

所有评论(0)