当前位置: 首页 > news >正文

UniApp缓存系统详解

一、UniApp 缓存系统概述

1.1 缓存概念与作用

在 UniApp 中,本地缓存(Storage)是一种键值对(Key-Value)存储机制,用于在设备本地保存小量数据。它类似于浏览器的 localStorage 或小程序的 wx.setStorage,但 UniApp 通过统一的 API 封装,实现了跨平台的透明使用。缓存的作用主要体现在以下方面:

  • 性能提升:避免重复网络请求。例如,登录后将 Token 存入缓存,下次启动 App 时直接读取,无需重新验证。
  • 用户体验优化:支持离线模式,如缓存文章列表,用户无网也能浏览。
  • 数据持久化:应用重启或页面刷新后,数据不丢失(除非手动清理)。
  • 状态共享:跨页面/组件共享数据,如购物车内容。

与内存状态(如 Vuex)不同,缓存是持久化的,但需注意其非关系型特性:不支持复杂查询,仅适合简单对象或字符串。在实际项目中,我经常用它来处理临时配置,避免每次启动都从服务器拉取。

1.2 平台差异详解

UniApp 缓存在不同平台的底层实现有所差异,这直接影响存储上限、持久性和清理策略。以下表格总结关键差异:

平台底层实现存储上限持久性清理机制
H5localStorage约 5MB(浏览器限制)缓存概念,可能被浏览器清理用户清除浏览数据或过期
Appplus.storage无限制(设备存储空间)持久化,直至卸载 App手动清理或系统空间不足
微信小程序wx.setStorage单 key 1MB,总 10MB与小程序生命周期绑定用户删除小程序或超限自动清理
支付宝小程序my.setStorage单条 200KB,总 10MB与小程序生命周期绑定超限自动清理
百度/抖音小程序平台特定 API视平台文档(约 10MB)与小程序生命周期绑定平台策略清理
HarmonyOS分布式存储视设备(HBuilderX 4.23+ 支持)持久化手动或系统管理

注意:清空缓存后,非 App 平台可能导致 uni.getSystemInfo 的 deviceId 改变,影响设备标识。开发者需在多端测试,确保兼容。在我的经验中,App 端的无限制存储特别适合缓存大文件列表,但要警惕设备空间耗尽。

1.3 支持的数据类型与限制

UniApp 缓存支持原生类型(String、Number、Boolean、Array、Object)和可通过 JSON.stringify 序列化的复杂对象。但不支持函数、Date 对象(需转为字符串)或循环引用。限制包括:

  • Key 命名:避免系统保留前缀(如 uni-dcloud_),否则可能冲突或失败。
  • 数据大小:超出平台上限时,存储失败,无自动压缩。
  • 线程安全:异步 API 不阻塞 UI 线程,同步 API 可能在高频操作时影响性能。
  • 生命周期:H5/App 持久,小程序随应用销毁。

这些基础认知是上手的关键,接下来我们深入 API 层面。

二、基础 API 详解

UniApp 提供 5 对核心 API:存储、获取、移除、清空和信息查询。每对均有异步(回调式)和同步(try-catch 式)版本。异步适合复杂场景,避免阻塞;同步适合简单操作,代码简洁。以下逐一拆解。

2.1 存储数据:uni.setStorage / uni.setStorageSync

原理:将数据存入指定 key,会覆盖原有内容。底层序列化为字符串存储。

异步版本:uni.setStorage(OBJECT)

参数表:

参数名类型必填说明
keyString缓存键名,建议使用描述性命名如 ‘user_token’
dataAny存储内容,支持 JSON 序列化对象
successFunction成功回调,无参数
failFunction失败回调,参数为错误对象
completeFunction结束回调,无论成败

返回值:无,通过回调处理。

示例(Vue 页面中):

// pages/index/index.vue
export default {methods: {asyncStoreData() {uni.setStorage({key: 'user_info',data: { id: 1, name: '张三', token: 'abc123' },success: () => {console.log('存储成功');uni.showToast({ title: '数据已缓存' });},fail: (err) => {console.error('存储失败:', err);uni.showToast({ title: '存储出错', icon: 'none' });}});}}
}

解释:在按钮点击事件中调用,成功后提示用户。data 对象自动序列化。在实际开发中,这种异步方式特别适合用户交互密集的页面。

同步版本:uni.setStorageSync(key, data)

参数:key (String,必填),data (Any,必填)。

返回值:无,使用 try-catch 处理异常。

示例:

try {uni.setStorageSync('user_info', { id: 1, name: '张三', token: 'abc123' });console.log('同步存储成功');
} catch (e) {console.error('同步存储失败:', e);
}

规则

  • 异步优先:UI 交互场景用异步,避免卡顿。
  • 错误处理:fail 回调捕获网络/权限问题,try-catch 捕获序列化失败。
  • 注意事项:data 必须可序列化,否则抛错(如包含函数)。我通常在存储前添加一个序列化检查函数,确保稳定性。

2.2 获取数据:uni.getStorage / uni.getStorageSync

原理:从 key 读取内容,若不存在返回 undefined 或空值。

异步版本:uni.getStorage(OBJECT)

参数表:

参数名类型必填说明
keyString缓存键名
successFunction回调,res = { data: 读取内容 }
failFunction失败回调
completeFunction结束回调

success 返回值:{ data: Any }

示例:

asyncGetData() {uni.getStorage({key: 'user_info',success: (res) => {if (res.data) {console.log('获取数据:', res.data.name);this.userName = res.data.name;  // 更新页面数据} else {console.log('缓存为空');}},fail: (err) => {console.error('获取失败:', err);}});
}

解释:读取后直接绑定到页面 data,实现响应式更新。在组件 mounted 钩子中调用,能快速初始化视图。

同步版本:uni.getStorageSync(key)

参数:key (String,必填)。

返回值:Any(不存在时 undefined)。

示例:

try {const userInfo = uni.getStorageSync('user_info');if (userInfo) {console.log('同步获取:', userInfo.name);} else {console.log('无数据');}
} catch (e) {console.error('同步获取失败:', e);
}

规则

  • 默认值处理:读取前可检查 typeof res.data !== ‘undefined’。
  • 性能提示:高频读取用同步,低频用异步。同步版本在初始化阶段特别高效,能节省几毫秒加载时间。

2.3 移除数据:uni.removeStorage / uni.removeStorageSync

原理:删除指定 key,若不存在仍返回成功。

异步版本:uni.removeStorage(OBJECT)

参数表:

参数名类型必填说明
keyString要移除的键名
successFunction成功回调,无参数
failFunction失败回调
completeFunction结束回调

示例:

removeData() {uni.removeStorage({key: 'user_info',success: () => {console.log('移除成功');uni.showToast({ title: '缓存已清除' });}});
}

同步版本:uni.removeStorageSync(key)

示例:

try {uni.removeStorageSync('user_info');console.log('同步移除成功');
} catch (e) {console.error('移除失败:', e);
}

注意事项:移除后,相关页面需刷新数据源,避免显示旧值。结合事件总线,能通知其他组件更新。

2.4 清空数据:uni.clearStorage / uni.clearStorageSync

原理:移除所有缓存键值对,慎用!

异步版本:uni.clearStorage(OBJECT)

参数:success/fail/complete(可选)。

示例:

clearAll() {uni.clearStorage({success: () => {console.log('全部清空成功');// 跳转登录页uni.reLaunch({ url: '/pages/login/login' });}});
}

同步版本:uni.clearStorageSync()

示例:

try {uni.clearStorageSync();console.log('同步清空成功');
} catch (e) {console.error('清空失败:', e);
}

规则

  • 备份重要数据:清空前用 getStorageInfo 导出 keys。
  • 平台影响:非 App 端可能重置 deviceId。在生产环境中,我会添加确认对话框,防止误操作。

2.5 获取存储信息:uni.getStorageInfo / uni.getStorageInfoSync

原理:查询当前缓存状态,用于监控和调试。

异步版本:uni.getStorageInfo(OBJECT)

success 返回值:

参数名类型说明
keysArray所有键名数组
currentSizeNumber当前占用 KB
limitSizeNumber限制 KB

示例:

getInfo() {uni.getStorageInfo({success: (res) => {console.log('键列表:', res.keys);console.log('占用:', res.currentSize + 'KB / ' + res.limitSize + 'KB');}});
}

同步版本:uni.getStorageInfoSync()

示例:

try {const res = uni.getStorageInfoSync();console.table(res);  // 表格输出
} catch (e) {console.error(e);
}

应用:在设置页显示存储使用率,提示用户清理。定期调用能帮助诊断内存问题。

通过这些基础 API,开发者可快速实现简单缓存。接下来,我们探讨如何在复杂场景中扩展它们。

三、高级用法与技巧

基础 API 虽强大,但实际项目中需处理过期、集成和安全等问题。本节提供一些实用技巧,帮助你构建更robust的缓存系统。

3.1 缓存过期机制实现

原理:UniApp 无内置过期,需手动添加时间戳判断。

实现步骤:

  1. 存储时附加 timestamp: Date.now()。
  2. 读取时计算差值,超阈值则移除并返回 null。

示例(工具函数):

// utils/cache.js
const CACHE_EXPIRE = 60 * 60 * 1000;  // 1小时export function setWithExpire(key, data, expire = CACHE_EXPIRE) {const cacheObj = {data,timestamp: Date.now() + expire};try {uni.setStorageSync(key, JSON.stringify(cacheObj));} catch (e) {console.error('设置过期缓存失败:', e);}
}export function getWithExpire(key) {try {const str = uni.getStorageSync(key);if (!str) return null;const cacheObj = JSON.parse(str);if (Date.now() > cacheObj.timestamp) {uni.removeStorageSync(key);  // 过期移除return null;}return cacheObj.data;} catch (e) {console.error('获取过期缓存失败:', e);return null;}
}

使用

// 存储
setWithExpire('article_list', articles, 30 * 60 * 1000);  // 30分钟// 读取
const articles = getWithExpire('article_list');
if (articles) {this.list = articles;
} else {// 从服务器重新获取
}

优势:防止陈旧数据,提升数据新鲜度。在电商 App 中,我用它缓存商品价格,过期后自动刷新,避免用户看到过时信息。

3.2 与 Vuex/Pinia 集成持久化

原理:Vuex 内存状态非持久,结合缓存实现重启恢复。

Vuex 示例(store/index.js):

import Vue from 'vue';
import Vuex from 'vuex';Vue.use(Vuex);const store = new Vuex.Store({state: {user: null,cart: []},mutations: {SET_USER(state, user) {state.user = user;// 持久化uni.setStorageSync('vuex_user', JSON.stringify(user));},SET_CART(state, cart) {state.cart = cart;uni.setStorageSync('vuex_cart', JSON.stringify(cart));}},actions: {initFromCache({ commit }) {// 应用启动时恢复const userStr = uni.getStorageSync('vuex_user');if (userStr) commit('SET_USER', JSON.parse(userStr));const cartStr = uni.getStorageSync('vuex_cart');if (cartStr) commit('SET_CART', JSON.parse(cartStr));}}
});export default store;

main.js 中调用

import store from './store';
Vue.prototype.$store = store;
store.dispatch('initFromCache');  // App 启动时执行

Pinia 变体:类似,使用 defineStore 的 persist 插件(社区扩展)。

规则:仅持久关键状态,避免缓存大对象。在大型项目中,这种集成能让状态管理更可靠,用户重启 App 后无缝继续。

3.3 全局缓存管理工具封装

原理:统一入口,简化多处调用,支持命名空间。

示例(utils/storageManager.js):

class StorageManager {constructor(namespace = '') {this.namespace = namespace ? `${namespace}_` : '';}set(key, data) {try {uni.setStorageSync(this.namespace + key, data);} catch (e) {throw new Error(`存储失败: ${e.message}`);}}get(key, defaultValue = null) {try {return uni.getStorageSync(this.namespace + key) || defaultValue;} catch (e) {console.warn(`获取失败: ${key}`);return defaultValue;}}remove(key) {try {uni.removeStorageSync(this.namespace + key);} catch (e) {}}clear() {try {uni.clearStorageSync();} catch (e) {}}getInfo() {try {return uni.getStorageInfoSync();} catch (e) {return null;}}
}// 使用
const userStorage = new StorageManager('user');
userStorage.set('token', 'xyz');
const token = userStorage.get('token');

优势:避免 key 冲突,支持模块化(如 ‘user_token’)。在团队开发中,这样的封装能减少代码重复,提高维护性。

3.4 数据加密存储

原理:敏感数据(如 Token)易被逆向,需 AES 加密。

依赖:UniApp 无内置 crypto,App 端用 plus.crypto(需条件编译)。

示例(简单 Base64 + 自定义密钥,H5/小程序通用):

// utils/encrypt.js
const KEY = 'MySecretKey12345';  // 生产环境用随机密钥function encrypt(data) {const str = typeof data === 'object' ? JSON.stringify(data) : data;let result = '';for (let i = 0; i < str.length; i++) {result += String.fromCharCode(str.charCodeAt(i) ^ KEY.charCodeAt(i % KEY.length));}return btoa(result);  // Base64 编码
}function decrypt(encrypted) {try {const decoded = atob(encrypted);let result = '';for (let i = 0; i < decoded.length; i++) {result += String.fromCharCode(decoded.charCodeAt(i) ^ KEY.charCodeAt(i % KEY.length));}return JSON.parse(result);  // 假设对象} catch (e) {return null;}
}// 使用
uni.setStorageSync('encrypted_token', encrypt('abc123'));
const token = decrypt(uni.getStorageSync('encrypted_token'));

注意:H5 用 CryptoJS 库(npm 引入),App 用原生。对于金融类 App,这层保护至关重要,能防范简单逆向工程。

3.5 批量操作与事务模拟

原理:无原生事务,用 Promise.all 模拟批量。

示例:

async batchSet(items) {const promises = items.map(({ key, data }) =>new Promise((resolve, reject) => {uni.setStorage({key,data,success: resolve,fail: reject});}));try {await Promise.all(promises);console.log('批量存储成功');} catch (e) {console.error('批量失败,回滚');// 模拟回滚:清空相关键items.forEach(({ key }) => uni.removeStorageSync(key));}
}// 调用
this.batchSet([{ key: 'config1', data: { theme: 'dark' } },{ key: 'config2', data: { lang: 'zh' } }
]);

规则:失败时回滚,确保一致性。适用于配置批量更新,在初始化多模块时很实用。

四、实际应用场景

本节通过 4 个典型场景,展示缓存的实战价值。每场景包含需求、实现和优化。

4.1 用户登录状态管理

需求:登录后保存 Token 和用户信息,退出时清除;重启 App 自动验证。

实现

  • 登录页(login.vue):
login() {// 模拟 APIconst user = { id: 1, name: '李四', token: 'def456' };uni.setStorageSync('user_token', user.token);uni.setStorageSync('user_info', JSON.stringify(user));uni.switchTab({ url: '/pages/home/home' });
}
  • Home 页(home.vue):
onLoad() {const token = uni.getStorageSync('user_token');if (token) {const userStr = uni.getStorageSync('user_info');this.user = userStr ? JSON.parse(userStr) : null;} else {uni.reLaunch({ url: '/pages/login/login' });}
},
methods: {logout() {uni.removeStorageSync('user_token');uni.removeStorageSync('user_info');uni.reLaunch({ url: '/pages/login/login' });}
}

优化:集成过期(3.1),每 7 天重登。

在实际项目中,可添加 JWT 解析验证 Token 有效性:

// 验证 Token
function isTokenValid(token) {try {const payload = JSON.parse(atob(token.split('.')[1]));return Date.now() < payload.exp * 1000;} catch (e) {return false;}
}

这确保安全,防止过期 Token 误用。在社交 App 中,这种机制让用户体验更流畅。

4.2 配置信息持久化

需求:保存主题色、语言等设置,重启生效。

实现

  • 设置页(setting.vue):
changeTheme(color) {uni.setStorageSync('app_theme', color);// 动态更新全局样式(需 CSS 变量)const dom = document.documentElement;dom.style.setProperty('--theme-color', color);
}
onLoad() {const savedTheme = uni.getStorageSync('app_theme') || '#007AFF';// 应用主题
}

优化:用 getStorageInfo 监控,超 1MB 提示清理。结合 Vuex(3.2),实现响应式切换。

扩展:多语言支持:

// 存储语言包
uni.setStorageSync('lang_pack', { zh: { hello: '你好' }, en: { hello: 'Hello' } });
const lang = uni.getStorageSync('current_lang') || 'zh';
this.messages = uni.getStorageSync('lang_pack')[lang];

这在国际化 App 中实用,减少 bundle 大小。用户切换语言时,立即生效,无需重启。

4.3 离线数据缓存

需求:缓存 API 返回的文章列表,支持无网浏览。

实现(使用 3.1 过期):

// 获取列表
async fetchArticles() {const cached = getWithExpire('articles');if (cached) {this.articles = cached;return;}try {const res = await uni.request({ url: '/api/articles' });setWithExpire('articles', res.data, 24 * 60 * 60 * 1000);  // 24小时this.articles = res.data;} catch (e) {console.error('网络错误,使用缓存');this.articles = cached || [];}
}

优化:分页缓存,如 ‘articles_page_1’。添加网络状态检测:

uni.getNetworkType({success: (res) => {if (res.networkType === 'none') {// 仅用缓存}}
});

这提升了鲁棒性,在弱网环境闪光。新闻类 App 中,用户地铁上也能阅读缓存内容。

4.4 列表分页缓存

需求:电商商品列表,分页加载,缓存每页数据。

实现

loadPage(page = 1) {const key = `goods_page_${page}`;const cached = uni.getStorageSync(key);if (cached) {this.goods = [...this.goods, ...cached];return;}uni.request({url: `/api/goods?page=${page}`,success: (res) => {uni.setStorageSync(key, res.data);this.goods = [...this.goods, ...res.data];}});
}

优化:预加载下一页,移除旧页(如 >10 页清空)。结合 getStorageInfo,避免总大小超限。

扩展讨论:在高并发场景,可用 IndexedDB(H5 专用,条件编译)补充大列表缓存:

<!-- #ifdef H5 -->
<script>
import { openDB } from 'idb';  // npm idb
const db = await openDB('goods_db', 1, {upgrade(db) {db.createObjectStore('pages');}
});
await db.put('pages', data, `page_${page}`);
</script>
<!-- #endif -->

这在 Web 端处理海量数据时高效,结合缓存层,能实现混合存储策略。

五、性能优化与最佳实践

5.1 同步 vs 异步选择

  • 异步优先:95% 场景用回调/Promise,避免 UI 阻塞。示例:onLoad 中异步 get。
  • 同步场景:启动时快速恢复,或简单工具函数。
  • 最佳实践:封装 Promise 化异步:
function setStoragePromise(key, data) {return new Promise((resolve, reject) => {uni.setStorage({key, data,success: resolve,fail: reject});});
}
// 使用 await setStoragePromise('key', data);

在我的项目中,统一用 Promise,能让 async/await 代码更优雅。

5.2 存储大小管理

  • 监控:定期调用 getStorageInfo,若 currentSize > 80% limitSize,清理旧数据。
  • 压缩:大对象用 lz-string 库压缩(npm 引入)。
  • 实践:设置页添加“清理缓存”按钮:
clearOld() {const res = uni.getStorageInfoSync();if (res.currentSize > res.limitSize * 0.8) {// 移除 7 天前数据(结合 3.1)res.keys.forEach(key => {if (getWithExpire(key) === null) uni.removeStorageSync(key);});}
}

定期清理能保持 App 轻量,尤其在低端设备上。

5.3 错误处理与调试

  • 统一日志:用 console.error + uni.reportEvent 上报。
  • 调试工具:HBuilderX 控制台查看 Storage,Chrome DevTools 检查 H5 localStorage。
  • 实践:添加 try-catch 包装所有操作,fail 回调 toast 提示。遇到序列化错误时,fallback 到字符串存储。

5.4 跨平台兼容

  • 条件编译:#ifdef APP 用 plus.storage 扩展。
  • 测试:多端运行,关注小程序审核(如支付宝 10MB 限)。
  • 实践:manifest.json 中配置 storage 权限。在 CI/CD 中集成多端测试脚本。

5.5 清理策略

  • 自动:应用退出时 clear(可选)。
  • 手动:用户设置页一键清。
  • 智能:基于使用频率,LRU(最近最少用)算法移除(自定义 Map 实现)。

示例 LRU:

class LRUCache {constructor(maxSize) {this.maxSize = maxSize;this.cache = new Map();}set(key, value) {if (this.cache.has(key)) this.cache.delete(key);this.cache.set(key, value);if (this.cache.size > this.maxSize) {const firstKey = this.cache.keys().next().value;this.cache.delete(firstKey);}}get(key) {if (this.cache.has(key)) {const value = this.cache.get(key);this.cache.delete(key);this.cache.set(key, value);return value;}return null;}
}

用它包装 Storage,能智能管理热数据。

六、常见问题与解决方案

6.1 常见错误

  • 序列化失败:data 含不可序列化类型。
    :预检查 JSON.stringify(data) 不抛错。
  • Key 冲突:用系统前缀。
    :命名规范,如 ‘app_user_token’。
  • 读取 undefined:key 不存在。
    :默认值 || {}

6.2 平台特定问题

  • H5 缓存丢失:浏览器隐私模式。
    :fallback 到 sessionStorage。
  • 小程序超限:微信 10MB。
    :分 key 存储,定期 getStorageInfo 检查。
  • App 无限制滥用:占用设备空间。
    :设置软上限 50MB,自行清理。

6.3 调试技巧

  • 打印 keys:循环 getStorageSync(key) 查看内容。
  • 模拟清理:clearStorageSync() + 重载。
  • 工具:UniApp 插件“Storage Viewer”。在开发时,添加一个调试模式,暴露所有 keys 到控制台。

七、扩展与进阶

7.1 与其他存储结合

  • SQLite(App 端):复杂数据用 uni.requireNativePlugin(‘sqlite’)。
    示例:用户订单表,缓存仅存 ID 列表。
  • IndexedDB(H5):大文件缓存。
    实践:图片缩略图存 IndexedDB,详情存 Storage。混合使用能平衡性能和容量。

7.2 云端同步

  • 原理:本地变更时上传云(uniCloud),拉取时合并。
  • 示例:登录后 syncCacheToCloud(),用 diff 算法避免覆盖。
async syncCache() {const localKeys = uni.getStorageInfoSync().keys;const cloudData = await uniCloud.callFunction({ name: 'getCache' });localKeys.forEach(key => {if (!cloudData.has(key)) {uniCloud.callFunction({ name: 'uploadCache', data: { key, value: uni.getStorageSync(key) } });}});
}

这在多设备同步场景中强大,如笔记 App。

7.3 自定义缓存模块

  • 基于 Redux-like:状态 + reducer + storage middleware。
  • 进阶:支持 RxJS 响应式缓存更新。示例:订阅缓存变化,自动 UI 刷新。
// 用 RxJS
import { fromEvent } from 'rxjs';
const storageEvent = fromEvent(window, 'storage');
storageEvent.subscribe(event => {if (event.key === 'my_key') {// 更新状态}
});

这让缓存成为响应式数据源。

http://www.dtcms.com/a/523517.html

相关文章:

  • 【LLM】用 FastAPI 搭建「OpenAI 兼容」DeepSeek-OCR 服务 + 简洁WebUI
  • 企业内部SRE/DevOps向精通Linux课程培训大纲
  • 《Effective Java》第13条:谨慎的覆盖clone
  • 第一章、React + TypeScript + Webpack项目构建
  • 前端:金丝雀部署(Canary Deployment)/ A、B部署 / 灰度部署
  • Spark微博舆情分析系统 情感分析 爬虫 Hadoop和Hive 贴吧数据 双平台 讲解视频 大数据 Hadoop ✅
  • 宁波公司网站建设价格dw建设手机网站
  • 长沙做网站价格有哪些网站可以做青旅义工
  • 怎么查看网站打开速度企业网站用vps还是虚拟主机
  • Vue3 模板引用——ref
  • XGBoost完整学习指南:从数据清洗到模型调参
  • 【深度学习新浪潮】AI Agent工具流产品:从原理到落地,打造智能协作新范式
  • 页面滚动加载更多
  • 除了provide和inject,Vue3还有哪些用于组件通信的方式?
  • React 表单与事件
  • AI代码开发宝库系列:FAISS向量数据库
  • 前端埋点学习
  • Spring AI与DeepSeek实战:打造企业级知识库+系统API调用
  • 秦皇岛市建设局网站关于装配式专家做运动特卖的网站
  • j2ee 建设简单网站设计婚纱网站
  • C++类和对象(中):const 成员函数与取地址运算符重载
  • 数据结构 散列表—— 冲突解决方法
  • 箭头函数和普通函数有什么区别
  • Spring Boot 缓存知识体系大纲
  • 破局政务数字化核心难题:金仓数据库以国产化方案引领电子证照系统升级之路
  • XML:从基础到 Schema 约束的全方位解析
  • 技术引领场景革新|合合信息PRCV论坛聚焦多模态文本智能前沿实践
  • 海南网站建设网络货运平台有哪些
  • 系统架构设计师备考第53天——业务逻辑层设计
  • 科技创新与数字化制造转型在“十五五”规划中的意义