uni-app网络请求与缓存策略:构建高效的数据层 0 次阅读

网络请求和数据缓存是应用开发中的关键环节,本文将详细介绍如何在uni-app中构建高效可靠的数据层。

1. 网络请求封装

1.1 基础请求类

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// utils/request.js
class Request {
constructor(options = {}) {
this.baseURL = options.baseURL || ''
this.timeout = options.timeout || 60000
this.header = options.header || {}
this.beforeRequest = options.beforeRequest
this.afterRequest = options.afterRequest
}

// 请求拦截
beforeRequest(config) {
// 添加token
const token = uni.getStorageSync('token')
if (token) {
config.header.Authorization = `Bearer ${token}`
}
return config
}

// 响应拦截
afterRequest(response) {
const { statusCode, data } = response
if (statusCode >= 200 && statusCode < 300) {
return data
}
return Promise.reject(response)
}

// 请求方法
request(options = {}) {
options.baseURL = options.baseURL || this.baseURL
options.timeout = options.timeout || this.timeout
options.header = { ...this.header, ...options.header }
options.method = options.method || 'GET'

// 请求拦截
if (this.beforeRequest) {
options = this.beforeRequest(options)
}

return new Promise((resolve, reject) => {
uni.request({
...options,
url: options.baseURL + options.url,
success: (res) => {
// 响应拦截
if (this.afterRequest) {
try {
const data = this.afterRequest(res)
resolve(data)
} catch (error) {
reject(error)
}
} else {
resolve(res.data)
}
},
fail: (err) => {
reject(err)
}
})
})
}

// 封装常用请求方法
get(url, data = {}, options = {}) {
return this.request({
url,
data,
method: 'GET',
...options
})
}

post(url, data = {}, options = {}) {
return this.request({
url,
data,
method: 'POST',
...options
})
}

put(url, data = {}, options = {}) {
return this.request({
url,
data,
method: 'PUT',
...options
})
}

delete(url, data = {}, options = {}) {
return this.request({
url,
data,
method: 'DELETE',
...options
})
}
}

export default new Request({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 10000,
header: {
'Content-Type': 'application/json'
}
})

1.2 请求拦截器

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// utils/interceptors.js
import store from '@/store'

// 请求队列
let requestQueue = []
// 是否正在刷新token
let isRefreshing = false

export const requestInterceptor = (config) => {
// 添加token
const token = uni.getStorageSync('token')
if (token) {
config.header.Authorization = `Bearer ${token}`
}
return config
}

export const responseInterceptor = async (response) => {
const { statusCode, data } = response

// token过期处理
if (statusCode === 401) {
// 将请求添加到队列
const retryRequest = new Promise((resolve) => {
requestQueue.push((token) => {
config.header.Authorization = `Bearer ${token}`
resolve(request.request(config))
})
})

// 是否正在刷新token
if (!isRefreshing) {
isRefreshing = true
try {
// 刷新token
const { token } = await store.dispatch('user/refreshToken')
// 重试队列中的请求
requestQueue.forEach(callback => callback(token))
requestQueue = []
return request.request(config)
} catch (error) {
// 刷新token失败,跳转登录页
store.dispatch('user/logout')
uni.navigateTo({
url: '/pages/login/index'
})
return Promise.reject(error)
} finally {
isRefreshing = false
}
}
return retryRequest
}

return data
}

2. 数据缓存策略

2.1 缓存管理类

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// utils/cache.js
class Cache {
constructor(options = {}) {
this.prefix = options.prefix || 'app_'
this.expire = options.expire || 0
this.isEncrypt = options.isEncrypt || false
}

// 设置缓存
setItem(key, value, expire = 0) {
const data = {
value,
time: Date.now(),
expire: expire || this.expire
}

// 加密处理
const stringData = this.isEncrypt
? this.encrypt(JSON.stringify(data))
: JSON.stringify(data)

try {
uni.setStorageSync(this.prefix + key, stringData)
} catch (error) {
console.error('缓存设置失败:', error)
}
}

// 获取缓存
getItem(key) {
try {
const data = uni.getStorageSync(this.prefix + key)
if (!data) return null

// 解密处理
const storage = this.isEncrypt
? JSON.parse(this.decrypt(data))
: JSON.parse(data)

// 过期判断
if (storage.expire && Date.now() - storage.time > storage.expire * 1000) {
uni.removeStorageSync(this.prefix + key)
return null
}

return storage.value
} catch (error) {
console.error('缓存获取失败:', error)
return null
}
}

// 移除缓存
removeItem(key) {
try {
uni.removeStorageSync(this.prefix + key)
} catch (error) {
console.error('缓存移除失败:', error)
}
}

// 清空缓存
clear() {
try {
uni.clearStorageSync()
} catch (error) {
console.error('缓存清空失败:', error)
}
}

// 加密方法
encrypt(data) {
// 实现加密逻辑
return data
}

// 解密方法
decrypt(data) {
// 实现解密逻辑
return data
}
}

export default new Cache({
prefix: 'app_',
expire: 7 * 24 * 60 * 60, // 默认7天过期
isEncrypt: process.env.NODE_ENV === 'production' // 生产环境开启加密
})

2.2 数据预加载

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// utils/preload.js
import cache from './cache'
import request from './request'

class Preload {
constructor() {
this.tasks = new Map()
}

// 添加预加载任务
add(key, promiseCreator) {
if (!this.tasks.has(key)) {
const task = promiseCreator().then(data => {
cache.setItem(key, data)
return data
})
this.tasks.set(key, task)
}
return this.tasks.get(key)
}

// 获取预加载数据
async get(key, promiseCreator) {
// 优先从缓存获取
const cached = cache.getItem(key)
if (cached) return cached

// 其次从预加载任务获取
if (this.tasks.has(key)) {
return this.tasks.get(key)
}

// 最后实时加载
return this.add(key, promiseCreator)
}

// 清除预加载任务
clear() {
this.tasks.clear()
}
}

export default new Preload()

// 使用示例
const preload = new Preload()

// 预加载数据
preload.add('userInfo', () => {
return request.get('/api/user/info')
})

// 获取数据
const userInfo = await preload.get('userInfo', () => {
return request.get('/api/user/info')
})

3. 实际应用示例

3.1 API封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// api/user.js
import request from '@/utils/request'
import cache from '@/utils/cache'

export function login(data) {
return request.post('/api/login', data)
}

export function getUserInfo() {
return cache.remember('user_info', () => {
return request.get('/api/user/info')
}, 30 * 60) // 缓存30分钟
}

export function updateUserInfo(data) {
return request.put('/api/user/info', data).then(() => {
// 更新后清除缓存
cache.removeItem('user_info')
})
}

3.2 数据预加载

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
32
33
// pages/index/index.vue
import preload from '@/utils/preload'

export default {
onLoad() {
// 预加载下一页数据
this.preloadNextPage()
},
methods: {
preloadNextPage() {
const nextPage = this.currentPage + 1
preload.add(`list_page_${nextPage}`, () => {
return request.get('/api/list', {
page: nextPage,
size: 10
})
})
},
async loadNextPage() {
const nextPage = this.currentPage + 1
const data = await preload.get(`list_page_${nextPage}`, () => {
return request.get('/api/list', {
page: nextPage,
size: 10
})
})
this.list.push(...data.list)
this.currentPage = nextPage
// 继续预加载下一页
this.preloadNextPage()
}
}
}

4. 最佳实践建议

  1. 统一封装请求方法
  2. 实现请求拦截和响应拦截
  3. 合理使用数据缓存
  4. 实现数据预加载
  5. 注意安全性处理

5. 总结

  1. 掌握请求封装技巧
  2. 实现数据缓存策略
  3. 优化数据加载体验
  4. 处理异常情况
  5. 保证数据安全性

如果觉得文章对你有帮助,欢迎点赞、评论、分享,你的支持是我继续创作的动力!

上一篇 uni-app组件开发实战:从基础到进阶的最佳实践
下一篇 uni-app状态管理进阶:Vuex最佳实践与性能优化
感谢您的支持!
微信赞赏码 微信赞赏
支付宝赞赏码 支付宝赞赏