uni-app 云开发实战
核心知识点
1. 云开发架构设计
云开发架构是基于 uniCloud 的全栈解决方案,主要包含以下几个核心组件:
- 云函数:处理后端业务逻辑,支持 Node.js 运行环境
- 云数据库:基于 MongoDB 的 NoSQL 数据库,支持复杂查询
- 云存储:用于存储用户上传的文件,如图片、视频等
- 前端应用:基于 uni-app 开发的跨平台应用
2. 前后端分离实现
在云开发架构中,前后端分离主要通过以下方式实现:
- 前端:负责 UI 渲染、用户交互、数据展示
- 云函数:作为后端 API,处理业务逻辑、数据处理、第三方服务集成
- 数据交互:通过 uniCloud SDK 进行前后端通信
3. 部署流程
完整的云开发部署流程包括:
- 本地开发:在 HBuilderX 中编写前端代码和云函数
- 云函数部署:将云函数上传到云端运行环境
- 数据库初始化:创建集合、设置索引、导入初始数据
- 存储配置:设置存储桶权限、配置 CDN
- 前端发布:将前端应用打包发布到各个平台
实用案例分析
案例:构建一个完整的云开发应用
我们将构建一个简单的任务管理应用,包含以下功能:
- 用户注册登录
- 任务创建、编辑、删除
- 任务状态管理
- 任务列表查询
1. 项目初始化
- 创建 uni-app 项目:在 HBuilderX 中创建新项目,选择 "uni-app 云开发" 模板
- 关联云服务空间:在项目中关联一个 uniCloud 服务空间
2. 云函数开发
创建以下云函数:
user-register 云函数:
'use strict';
exports.main = async (event, context) => {
const db = uniCloud.database();
const { username, password } = event;
// 检查用户是否已存在
const userExists = await db.collection('users')
.where({ username })
.get();
if (userExists.data.length > 0) {
return { success: false, message: '用户已存在' };
}
// 创建新用户
const result = await db.collection('users').add({
username,
password: uniCloud.md5(password), // 简单加密,实际项目中应使用更安全的方式
createTime: new Date()
});
return { success: true, message: '注册成功', userId: result.id };
};user-login 云函数:
'use strict';
exports.main = async (event, context) => {
const db = uniCloud.database();
const { username, password } = event;
// 查询用户
const user = await db.collection('users')
.where({
username,
password: uniCloud.md5(password)
})
.get();
if (user.data.length === 0) {
return { success: false, message: '用户名或密码错误' };
}
return {
success: true,
message: '登录成功',
userInfo: {
id: user.data[0]._id,
username: user.data[0].username
}
};
};task-crud 云函数:
'use strict';
exports.main = async (event, context) => {
const db = uniCloud.database();
const { action, taskId, taskData, userId } = event;
switch (action) {
case 'create':
// 创建任务
const createResult = await db.collection('tasks').add({
...taskData,
userId,
createTime: new Date(),
status: 'pending'
});
return { success: true, message: '任务创建成功', taskId: createResult.id };
case 'update':
// 更新任务
await db.collection('tasks')
.doc(taskId)
.update(taskData);
return { success: true, message: '任务更新成功' };
case 'delete':
// 删除任务
await db.collection('tasks')
.doc(taskId)
.remove();
return { success: true, message: '任务删除成功' };
case 'getList':
// 获取任务列表
const tasks = await db.collection('tasks')
.where({ userId })
.orderBy('createTime', 'desc')
.get();
return { success: true, data: tasks.data };
default:
return { success: false, message: '无效的操作' };
}
};3. 前端应用开发
登录页面 (pages/login/login.vue):
<template>
<view class="login-container">
<view class="login-form">
<view class="form-item">
<input v-model="form.username" placeholder="请输入用户名" />
</view>
<view class="form-item">
<input v-model="form.password" type="password" placeholder="请输入密码" />
</view>
<view class="form-actions">
<button @click="login" type="primary">登录</button>
<button @click="register" type="default">注册</button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
form: {
username: '',
password: ''
}
};
},
methods: {
async login() {
const result = await uniCloud.callFunction({
name: 'user-login',
data: this.form
});
if (result.result.success) {
uni.setStorageSync('userInfo', result.result.userInfo);
uni.switchTab({ url: '/pages/task/task' });
} else {
uni.showToast({ title: result.result.message, icon: 'none' });
}
},
async register() {
const result = await uniCloud.callFunction({
name: 'user-register',
data: this.form
});
if (result.result.success) {
uni.showToast({ title: '注册成功,请登录', icon: 'success' });
} else {
uni.showToast({ title: result.result.message, icon: 'none' });
}
}
}
};
</script>
<style scoped>
.login-container {
padding: 40rpx;
}
.login-form {
background-color: #fff;
padding: 40rpx;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.form-item {
margin-bottom: 30rpx;
}
.form-item input {
border: 1rpx solid #ddd;
border-radius: 5rpx;
padding: 20rpx;
font-size: 32rpx;
}
.form-actions {
margin-top: 40rpx;
}
.form-actions button {
margin-bottom: 20rpx;
}
</style>任务列表页面 (pages/task/task.vue):
<template>
<view class="task-container">
<view class="task-header">
<text class="task-title">任务管理</text>
<button @click="addTask" type="primary" size="mini">新建任务</button>
</view>
<view class="task-list">
<view v-for="task in tasks" :key="task._id" class="task-item">
<view class="task-info">
<text class="task-name">{{ task.name }}</text>
<text class="task-status">{{ task.status === 'pending' ? '待完成' : '已完成' }}</text>
</view>
<view class="task-actions">
<button @click="editTask(task)" type="default" size="mini">编辑</button>
<button @click="deleteTask(task._id)" type="warn" size="mini">删除</button>
<button @click="toggleStatus(task)" type="primary" size="mini">
{{ task.status === 'pending' ? '完成' : '重置' }}
</button>
</view>
</view>
</view>
<!-- 新建/编辑任务弹窗 -->
<uni-popup v-model="popupVisible" mode="bottom">
<view class="popup-content">
<view class="popup-header">
<text>{{ editingTask ? '编辑任务' : '新建任务' }}</text>
<text @click="popupVisible = false" class="popup-close">×</text>
</view>
<view class="popup-body">
<input v-model="taskForm.name" placeholder="请输入任务名称" />
<textarea v-model="taskForm.description" placeholder="请输入任务描述" />
</view>
<view class="popup-footer">
<button @click="popupVisible = false" type="default">取消</button>
<button @click="saveTask" type="primary">保存</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
export default {
data() {
return {
tasks: [],
popupVisible: false,
editingTask: null,
taskForm: {
name: '',
description: ''
}
};
},
onLoad() {
this.getTasks();
},
methods: {
async getTasks() {
const userInfo = uni.getStorageSync('userInfo');
if (!userInfo) {
uni.navigateTo({ url: '/pages/login/login' });
return;
}
const result = await uniCloud.callFunction({
name: 'task-crud',
data: {
action: 'getList',
userId: userInfo.id
}
});
if (result.result.success) {
this.tasks = result.result.data;
}
},
addTask() {
this.editingTask = null;
this.taskForm = { name: '', description: '' };
this.popupVisible = true;
},
editTask(task) {
this.editingTask = task;
this.taskForm = { ...task };
this.popupVisible = true;
},
async saveTask() {
const userInfo = uni.getStorageSync('userInfo');
if (this.editingTask) {
// 更新任务
const result = await uniCloud.callFunction({
name: 'task-crud',
data: {
action: 'update',
taskId: this.editingTask._id,
taskData: this.taskForm
}
});
if (result.result.success) {
uni.showToast({ title: '任务更新成功', icon: 'success' });
this.getTasks();
this.popupVisible = false;
}
} else {
// 创建任务
const result = await uniCloud.callFunction({
name: 'task-crud',
data: {
action: 'create',
taskData: this.taskForm,
userId: userInfo.id
}
});
if (result.result.success) {
uni.showToast({ title: '任务创建成功', icon: 'success' });
this.getTasks();
this.popupVisible = false;
}
}
},
async deleteTask(taskId) {
uni.showModal({
title: '确认删除',
content: '确定要删除这个任务吗?',
success: async (res) => {
if (res.confirm) {
const result = await uniCloud.callFunction({
name: 'task-crud',
data: {
action: 'delete',
taskId
}
});
if (result.result.success) {
uni.showToast({ title: '任务删除成功', icon: 'success' });
this.getTasks();
}
}
}
});
},
async toggleStatus(task) {
const newStatus = task.status === 'pending' ? 'completed' : 'pending';
const result = await uniCloud.callFunction({
name: 'task-crud',
data: {
action: 'update',
taskId: task._id,
taskData: { status: newStatus }
}
});
if (result.result.success) {
uni.showToast({ title: '任务状态更新成功', icon: 'success' });
this.getTasks();
}
}
}
};
</script>
<style scoped>
.task-container {
padding: 20rpx;
}
.task-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.task-title {
font-size: 36rpx;
font-weight: bold;
}
.task-list {
background-color: #fff;
border-radius: 10rpx;
overflow: hidden;
}
.task-item {
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.task-info {
display: flex;
justify-content: space-between;
margin-bottom: 15rpx;
}
.task-name {
font-size: 32rpx;
font-weight: 500;
}
.task-status {
font-size: 28rpx;
color: #999;
}
.task-actions {
display: flex;
justify-content: flex-end;
gap: 10rpx;
}
.popup-content {
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
padding: 20rpx;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.popup-close {
font-size: 40rpx;
color: #999;
}
.popup-body input,
.popup-body textarea {
border: 1rpx solid #ddd;
border-radius: 5rpx;
padding: 20rpx;
margin-bottom: 20rpx;
width: 100%;
}
.popup-footer {
display: flex;
gap: 20rpx;
margin-top: 30rpx;
}
.popup-footer button {
flex: 1;
}
</style>4. 部署与发布
部署云函数:在 HBuilderX 中,右键点击
cloudfunctions目录,选择 "上传所有云函数"。数据库初始化:在 uniCloud Web 控制台中,创建
users和tasks集合。前端发布:
- 在 HBuilderX 中,选择 "发行" -> "网站 - Web 前端"
- 填写应用名称和版本号
- 点击 "发行" 按钮,生成 Web 版前端代码
- 将生成的代码部署到静态网站托管服务
学习目标
通过本章节的学习,你应该能够:
- 掌握云开发架构:理解 uniCloud 的核心组件和工作原理
- 实现前后端分离:通过云函数和前端应用的配合,构建完整的前后端分离架构
- 熟悉部署流程:掌握从开发到部署的完整流程
- 构建全栈应用:能够独立开发和部署基于云开发的完整应用
总结
uni-app 云开发提供了一种全新的全栈开发模式,让开发者可以专注于业务逻辑的实现,而无需关心服务器运维、数据库管理等底层问题。通过云函数、云数据库和云存储的配合,我们可以快速构建出功能完整、性能可靠的应用。
在实际项目中,我们还可以结合更多的云开发能力,如:
- 云调用:直接调用微信、支付宝等平台的开放能力
- 定时触发:设置定时任务,执行周期性操作
- HTTP 触发:通过 HTTP 请求触发云函数,实现 RESTful API
- 消息推送:集成推送服务,实现消息通知功能
通过不断探索和实践,你将能够充分发挥 uni-app 云开发的优势,构建出更加复杂和强大的应用。