最近几年,移动应用开发领域最火的技术之一就是uni-app了。作为一个基于Vue.js的跨平台开发框架,它最大的优势就是"一次开发,多端发布"。我自己从2018年开始接触uni-app,用它开发过不下20个项目,最深的感受就是开发效率真的高。特别是配合DCloud的uniCloud云服务,很多原本需要后端支持的功能,现在前端开发者自己就能搞定。
短信验证码功能在用户注册、登录、支付等场景中几乎是标配。传统做法需要前端调用后端API,后端再对接第三方短信平台,整个链路复杂不说,还经常遇到各种坑。uniCloud短信服务把这个流程简化到了极致,前端开发者只需要几行代码就能实现完整的短信验证码功能。我去年在一个电商项目中首次尝试这个方案,从开通到上线只用了不到2天时间,比传统方式节省了至少70%的开发量。
在开始之前,你需要确保已经完成以下准备:
我第一次使用时犯了个低级错误,用老版本的HBuilderX折腾了半天都没找到短信服务入口。后来更新到最新版才发现所有功能都整整齐齐地放在那里。所以强烈建议先检查开发工具版本,避免浪费时间。
登录DCloud开发者中心后,找到uniCloud服务管理页面。这里有个小技巧:直接访问https://unicloud.dcloud.net.cn/ 可以快速进入控制台。点击"短信服务"选项卡,你会看到开通引导。
开通过程中需要特别注意两点:
我建议在项目根目录下新建一个config.js文件,把这些敏感信息存放在里面,然后通过.gitignore排除这个文件。千万不要直接硬编码在云函数里,更不要上传到代码仓库。
短信模板报备是整个流程中最容易出问题的环节。根据我的经验,大约40%的开发者第一次都会在这里被驳回。最常见的几个问题包括:
我总结了一个通过率最高的模板范例:
code复制【你的应用名称】验证码:${code},用于${action},${expMinute}分钟内有效,请勿泄露并尽快验证。
发送报备邮件时,很多人会忽略一些关键细节。我建议邮件主题一定要严格按照"短信服务模板报备"这个格式,正文内容要包含:
我通常会这样写邮件正文:
code复制DCloud公司:
我的应用appid为__UNI_XXXXXX,应用名称为XXX应用。
我已开通uniCloud短信服务,现申请为此应用报备短信模板,该模板为验证类短信模板,其内容如下:
【XXX应用】验证码:${code},用于${action},${expMinute}分钟内有效,请勿泄露并尽快验证。
发送到service@dcloud.io后,一般1-2个工作日会收到回复。如果超过3天没收到,建议检查垃圾邮件箱,或者重新发送一次。
在uniCloud控制台右键你的服务空间,选择"新建云函数"。我习惯命名为"smsService",这样一看就知道是做什么的。核心代码如下:
javascript复制'use strict';
exports.main = async (event, context) => {
const { phone, code, action = '登录' } = event
if (!phone || !code) {
return { code: 400, msg: '缺少必要参数' }
}
try {
const res = await uniCloud.sendSms({
smsKey: '你的smsKey',
smsSecret: '你的smsSecret',
phone: phone,
templateId: '你的模板ID',
name: '你的应用名称',
data: {
code: code,
action: action,
expMinute: '5' // 默认5分钟有效期
}
})
return { code: 200, data: res }
} catch(err) {
console.error('短信发送失败:', err)
return { code: err.errCode, msg: err.errMsg }
}
};
这个版本比我第一次写的要健壮很多,增加了参数校验和错误处理。特别提醒:templateId是邮件回复中提供的模板ID,不是你自己随便编的。
在前端页面中,我通常会封装一个专门的发送验证码方法:
javascript复制async function sendSmsCode(phone) {
// 生成随机验证码
const code = Math.floor(100000 + Math.random() * 900000).toString()
// 调用云函数
const res = await uniCloud.callFunction({
name: 'smsService',
data: {
phone: phone,
code: code,
action: '注册' // 根据实际场景修改
}
})
if (res.result.code === 200) {
// 发送成功,将验证码存入本地缓存
uni.setStorageSync('smsCode', code)
return true
} else {
uni.showToast({ title: '发送失败: ' + res.result.msg, icon: 'none' })
return false
}
}
在实际项目中,我还会加上60秒倒计时功能,防止用户频繁点击。这个实现起来也很简单:
javascript复制data() {
return {
countdown: 0,
timer: null
}
},
methods: {
startCountdown() {
this.countdown = 60
this.timer = setInterval(() => {
if (this.countdown <= 0) {
clearInterval(this.timer)
return
}
this.countdown--
}, 1000)
},
async handleSend() {
if (this.countdown > 0) return
const success = await sendSmsCode(this.phone)
if (success) {
this.startCountdown()
}
}
}
在实际使用中,我遇到过各种短信发送失败的情况。总结下来主要有以下几种:
建议的排查步骤:
验证码功能虽然简单,但安全性不容忽视。我总结了几条实践经验:
可以在云函数中加入这样的频率限制逻辑:
javascript复制// 在云函数开头添加
const db = uniCloud.database()
const collection = db.collection('sms-log')
// 查询1分钟内是否已经发送过
const recent = await collection.where({
phone: phone,
createTime: db.command.gt(Date.now() - 60000)
}).count()
if (recent.total > 0) {
return { code: 429, msg: '发送过于频繁' }
}
// 发送成功后记录日志
await collection.add({
phone: phone,
code: code,
createTime: Date.now(),
used: false
})
在促销活动等场景下,可能需要批量发送验证码。直接循环调用云函数效率很低,我推荐以下两种方案:
方案一:使用uniCloud的批量发送接口
javascript复制await uniCloud.sendSms({
// ...其他参数
phoneList: ['13800138000', '13900139000'], // 最多100个号码
isBatch: true // 开启批量模式
})
方案二:使用云对象+队列
javascript复制// sms.object.js
module.exports = {
async sendBatch(phoneList) {
const queue = []
for (let i = 0; i < phoneList.length; i += 100) {
queue.push(this._sendChunk(phoneList.slice(i, i + 100)))
}
return Promise.all(queue)
},
async _sendChunk(chunk) {
// 实际发送逻辑
}
}
如果你的应用有海外用户,可能需要发送国际短信。uniCloud短信服务也支持这个功能,只需要在手机号前加上国际区号即可,比如"+1"表示美国,"+44"表示英国等。
需要注意的是:
建议在发送前先查询目标国家的支持情况:
javascript复制const countrySupport = await uniCloud.getSupportedCountries()
在最近的一个社区类App项目中,我遇到了一个有趣的问题:用户反映收不到验证码,但我们的日志显示发送成功了。经过仔细排查,发现是手机系统自带的短信拦截功能把验证码短信归类到了垃圾短信。
解决方案是在短信模板中加入更多业务相关的描述,比如:
code复制【XX社区】亲爱的用户,您正在注册XX社区账号,验证码:${code},5分钟内有效。如非本人操作请忽略。
这个改动后,送达率从原来的85%提升到了98%。另外一个小技巧是在应用内增加提示:"如果收不到验证码,请检查手机短信拦截设置"。
另一个常见问题是用户输错验证码。我的做法是在前端增加一个"显示验证码"的开关按钮,让用户可以确认自己输入的内容。同时,在服务端校验时,不区分大小写(如果验证码是字母数字组合的话),这样可以减少一些不必要的用户困扰。