第9章 9.1 uniCloud 云开发

「上一章我们做了一个可视化数据大屏,把数据展示得漂漂亮亮的。但问题来了——这些数据是写死在代码里的,真实项目里数据要从数据库里读、用户要能交互、云端要存文件……这一章,我们来解决这些」真实项目里绕不开的问题」。

想象一下:你做了一个「班级成绩查询」的小程序,自己测试没问题,但同学想用自己的学号查成绩,数据存在哪?你自己搭服务器?买云数据库?对于没接触过服务端开发的同学来说,这些词听起来就像天书。

uniCloud 就是来解救你的——它让你的小程序直接拥有「云端大脑」,不用自己搭服务器,数据、文件、用户管理统统搞定。

🎯 开场 3 分钟:为什么要学这个?

痛点场景:你想做一个「图书借阅小程序」,同学能查书、预约、还书。

  • 数据存在哪?用户查到的数据从哪来?
  • 怎么让小程序「记住」谁借了什么书?
  • 文件(比如图书封面图片)存哪?

如果你遇到这些问题,这一章就是为你准备的。

学完本文能带走
- 理解云函数、云数据库、云存储是什么
- 能用云函数读写数据库
- 能用云\n\nSimple tech illustration expla\n\nAI comic creation scene, creat\n\n存储上传/下载文件
- 独立做出一个「图书借阅」小工具


🧱 基础 25 分钟:核心概念

什么是云函数?

云函数就像「云端的迷你程序」——你定义一个函数,小程序随时可以调用它,它在云端运行,处理完把结果返回给你。

生活类比:就像你去海底捞,门口有个「呼叫服务员」的小程序。你按一下,服务员(云函数)就去帮你安排座位、拿菜单——你不需要知道后厨怎么运作,只关心结果。

为什么要用
- 代码运行在云端,不占用小程序的内存
- 可以连接数据库、处理复杂逻辑
- 多个小程序可以共用同一个云函数

怎么用(Python 伪代码帮助理解,uniCloud 用 JS 写,但思路一样):

# 这是一个云函数的逻辑(类比)
def get_book_info(book_id):
# 1. 去数据库查这本书
book = db.collection('books').find_one({'id': book_id})
# 2. 返回结果
return book

什么是云数据库?

云数据库就是云端的数据表,像 Excel 一样有行有列,但你可以用代码随时读写。

生活类比:想象一个「共享 Excel」,所有人都能往里面写数据,但只有你授权的人才能看到。云数据库就是这个,只不过是 API 操控,不是手动编辑。

为什么要用
- 数据存在云端,删了小程序数据还在
- 多人可以同时读写同一条数据
- 自带权限管理,谁能读谁能写

基本概念
- collection(集合)= 一张表,如「books」表存所有书
- doc(文档)= 一行数据,如一本书的信息
- field(字段)= 一列,如「书名」「作者」「库存」

# 一本书的数据结构长这样
book = {
"name": "Python 入门到精通",
"author": "张三",
"stock": 5,
"borrowers": []  # 借书人学号列表
}

什么是云存储?

云存储就是「云端硬盘」,可以存图片、视频、文件,然后拿一个链接随时访问。

生活类比:就像你把照片发到朋友圈,生成一个链接,谁都能看。云存储就是这个,只是你可以控制谁能上传、谁能下载。

为什么要用
- 小程序包体有限,图片视频存云端
- 用户上传的头像、文件集中管理
- 生成永久链接,不怕换手机


🔥 实战 35 分钟:3 个递进小项目

项目 1:第一个云函数(5 分钟)

目标:写一个云函数,返回「Hello」+ 你输入的名字。

代码(uniCloud 云函数格式):

// cloudfunctions/hello/index.js
'use strict';
exports.main = async (event, context) => {
// event 是小程序传过来的参数
const name = event.name || '同学';
// 返回结果
return {
    success: true,
    message: '你好,' + name + '!欢迎使用云函数!'
};
};

小程序端调用

// 调用云函数
uniCloud.callFunction({
name: 'hello',
data: { name: '小明' }
}).then(res => {
console.log(res.result);  // { success: true, message: '你好,小明!欢迎使用云函数!' }
});

预期输出

你好,小明!欢迎使用云函数!

一句话解释:云函数就是一个「在云端运行的函数」,你传参数,它返回结果。


项目 2:读写云数据库(15 分钟)

目标:做一个「图书查询」功能,根据书名查库存。

Step 1:创建数据库集合

database 目录下创建 books.json

{
"collectionName": "books",
"data": [
    { "_id": "1", "name": "Python 入门", "author": "张三", "stock": 3 },
    { "_id": "2", "name": "JavaScript 高级", "author": "李四", "stock": 0 },
    { "_id": "3", "name": "uniapp 实战", "author": "王五", "stock": 5 }
]
}

Step 2:查询云函数

// cloudfunctions/queryBook/index.js
'use strict';
exports.main = async (event, context) => {
const db = uniCloud.database();
const { bookName } = event;

// 模糊搜索书名
const res = await db.collection('books')
    .where({ name: new RegExp(bookName) })
    .get();

if (res.data.length === 0) {
    return { success: false, message: '没找到这本书' };
}

return {
    success: true,
    data: res.data[0]
};
};

Step 3:小程序端调用

// 查询图书
function queryBook(name) {
uniCloud.callFunction({
    name: 'queryBook',
    data: { bookName: name }
}).then(res => {
    if (res.result.success) {
        const book = res.result.data;
        console.log(`《${book.name}》作者:${book.author},库存:${book.stock}`);
    } else {
        console.log(res.result.message);
    }
});
}

// 测试
queryBook('Python');  // 《Python 入门》作者:张三,库存:3
queryBook('前端');    // 没找到这本书

预期输出

《Python 入门》作者:张三,库存:3
没找到这本书

一句话解释:云函数里用 db.collection('books').get() 查数据,就像用 Python 操作 MongoDB 一样。


项目 3:图书借阅系统(15 分钟)

目标:组合云函数和云数据库,做一个完整的借书/还书功能。

功能点
1. 借书:输入学号 + 书名,库存减 1,借书人列表加学号
2. 还书:输入学号 + 书名,库存加 1,借书人列表移除学号
3. 查询:输入书名,查当前状态

云函数 borrowBook

// cloudfunctions/borrowBook/index.js
'use strict';
exports.main = async (event, context) => {
const db = uniCloud.database();
const { bookName, studentId, action } = event;  // action: 'borrow' 或 'return'

// 1. 找到这本书
const bookRes = await db.collection('books')
    .where({ name: new RegExp(bookName) })
    .get();

if (bookRes.data.length === 0) {
    return { success: false, message: '这本书不存在' };
}

const book = bookRes.data[0];
const borrowers = book.borrowers || [];

if (action === 'borrow') {
    // 借书
    if (book.stock <= 0) {
        return { success: false, message: '库存不足,借不到' };
    }
    if (borrowers.includes(studentId)) {
        return { success: false, message: '你已经借过这本书了' };
    }

    // 更新:库存减1,借书人加学号
    await db.collection('books').doc(book._id).update({
        stock: book.stock - 1,
        borrowers: [...borrowers, studentId]
    });
    return { success: true, message: `借书成功!学号 ${studentId} 借了《${book.name}》` };

} else if (action === 'return') {
    // 还书
    if (!borrowers.includes(studentId)) {
        return { success: false, message: '你没有借这本书' };
    }

    // 更新:库存加1,借书人移除学号
    await db.collection('books').doc(book._id).update({
        stock: book.stock + 1,
        borrowers: borrowers.filter(id => id !== studentId)
    });
    return { success: true, message: `还书成功!学号 ${studentId} 还了《${book.name}》` };
}

return { success: false, message: 'action 参数错误' };
};

小程序端调用

// 统一借还书接口
function handleBook(bookName, studentId, action) {
uniCloud.callFunction({
    name: 'borrowBook',
    data: { bookName, studentId, action }
}).then(res => {
    console.log(res.result.message);
});
}

// 小明借《Python 入门》
handleBook('Python 入门', '2024001', 'borrow');
// 输出:借书成功!学号 2024001 借了《Python 入门》

// 小明还《Python 入门》
handleBook('Python 入门', '2024001', 'return');
// 输出:还书成功!学号 2024001 还了《Python 入门》

预期输出

借书成功!学号 2024001 借了《Python 入门》
还书成功!学号 2024001 还了《Python 入门》

一句话解释:云函数可以读写云数据库,一条 update 语句就能同时改库存和借书人列表。


💪 进阶 20 分钟:常见坑 + 性能小贴士

❌ 坑 1:忘记 await

// ❌ 错误:没写 await,拿到的不是结果,是 Promise 对象
const res = db.collection('books').get();
console.log(res.data);  // undefined!

// ✅ 正确:加上 await
const res = await db.collection('books').get();
console.log(res.data);  // 正常拿到数据

❌ 坑 2:权限问题

// ❌ 错误:云函数内读写数据库默认是 admin 权限,但小程序端可能没权限
// 需要在 database 目录下创建 permission.json 配权限

// ✅ 正确:在 database/permission.json 配置
{
"books.read": "auth.uid != null",
"books.write": "auth.uid != null"
}

❌ 坑 3:库存为负数

// ❌ 错误:没检查库存就直接减
await db.collection('books').doc(bookId).update({
stock: book.stock - 1  // 可能变成 -1
});

// ✅ 正确:先检查
if (book.stock <= 0) {
return { success: false, message: '库存不足' };
}

❌ 坑 4:数组操作没写对

// ❌ 错误:想把学号加入数组,直接赋值会覆盖
await db.collection('books').doc(bookId).update({
borrowers: studentId  // 错误!会变成字符串,不是数组
});

// ✅ 正确:用展开运算符或数组方法
await db.collection('books').doc(bookId).update({
borrowers: [...book.borrowers, studentId]
});

💡 调试技巧:云函数日志

exports.main = async (event, context) => {
console.log('收到参数:', event);  // 查看传了什么参数
console.log('调用者信息:', context);  // 看谁调用的

// 调试完成后删掉
const result = await db.collection('books').get();
console.log('查询结果:', result);

return result;
};

在 uniCloud 控制台能看到这些 console.log 输出。


✏️ 练习题 + 作业题

练习题(10 分钟)

练习 1(2 分钟):改名字
- 输入:调用云函数 hello,传 name: '小红'
- 预期输出:'你好,小红!欢迎使用云函数!'
- 提示:只改 data 里的 name 值

练习 2(2 分钟):加判断
- 输入:在 hello 云函数里加判断,如果名字是「老师」,返回特殊问候
- 预期输出:'您好,老师!欢迎使用云函数!'
- 提示:用 if 判断 name 是否等于 '老师'

练习 3(3 分钟):查新书
- 输入:用 queryBook 查询「JavaScript」
- 预期输出:显示《JavaScript 高级》库存为 0
- 提示:学完项目 2,直接调用就行

练习 4(3 分钟):借书检查
- 输入:小红(学号 2024002)借《Python 入门》,然后再借一次
- 预期输出:第一次成功,第二次提示「你已经借过这本书了」
- 提示:borrowBook 云函数会检查 borrowers 数组

练习 5(5 分钟,看代码找错)
- 输入:以下代码为什么会报错?

const res = db.collection('books').get();
return res.data[0];
  • 预期输出:报错 Cannot read property 'data' of undefined
  • 提示:想想项目 2 里怎么写的

作业题:做一个「班级通知系统」

需求描述:做一个简单的通知发布/查看工具,老师发通知,学生看通知。

功能点
1. 发布通知:云函数 publishNotice,传标题 + 内容,写入 notices 集合
2. 查看通知列表:云函数 getNotices,返回所有通知(按时间倒序)
3. 标记已读:云函数 markRead,记录哪个学号看了哪个通知

加分项
1. 每个通知显示「已读人数」
2. 通知内容超过 50 字截断,显示「...展开」

验收标准
- 三个云函数都能跑
- publishNotice 后 getNotices 能查到
- markRead 记录不丢失

提交方式:评论区贴关键代码,或 GitHub 链接


📚 总结 + 资源

本文学了 3 件事
- 云函数 = 在云端运行的「迷你程序」,小程序随时调用
- 云数据库 = 共享的数据表,用 API 读写,不用自己搭服务器
- 云存储 = 云端文件柜,存图片视频,生成链接随时访问

延伸资源
- uniCloud 官方文档:最权威的参考资料
- DCloud 视频教程:B 站搜「uniCloud」有大量实战视频
- 《uniapp 入门到精通》:配套书籍,系统学习

互动钩子:「你在项目里用过云数据库吗?遇到过什么坑?评论区聊聊,老粉优先回复!」


下章剧透:这一章我们学会了数据存在云端,但都是「你查一次更新一次」的模式。真实项目里像聊天、实时股价、多人协作编辑,需要数据变了立刻知道——下一章我们学 WebSocket 实时通信,让你的小程序「活」起来。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。