需求背景
这天,主管找到我:"有个微信小程序的验证码接口被恶意调用,一天刷了几百条,你看能不能加一个验证码限制一下"。我一听,终于来活了,直接开搞。
需求分析
公司一直有订阅阿里云验证码2.0服务,看文档微信小程序刚好能用,就直接用它,先登录验证码2.0控制台新建验证场景、填写配置,然后登录微信小程序后台添加插件,等申请通过就能用了。
开始接入
在app.json
中声明插件
json
"plugins": {
"AliyunCaptcha": {
"version": "2.1.1",
"provider": "wxbe275ff84246f1a4"
}
}
引入验证码小程序组件
json
{
"usingComponents": {
"aliyun-captcha": "plugin://AliyunCaptcha/captcha"
}
}
模板插入
html
<aliyun-captcha id="captcha-element" wx:if="{{loadCaptcha}}" props="{{pluginProps}}" />
插件初始化
js
// ali-captcha.js
import { postCode } from '../../service/api';
const AliyunCaptchaPluginInterface = requirePlugin('AliyunCaptcha');
const INTERVAL = 100;
// 业务请求(带验证码校验)回调函数
export const captchaVerifyCallback = async function (captchaVerifyParam) {
try {
const result = await postCode({ ali_captcha_verify_param: captchaVerifyParam, phone: this.data.phone });
return {
captchaResult: result.data.ali_verify_captcha,
bizResult: result.data.ali_verify_captcha,
};
} catch (error) {
AliyunCaptchaPluginInterface.refresh();
}
};
// 业务请求验证结果回调函数
export const onBizResultCallback = function (bizResult) {
if (bizResult === true) {
let time = setInterval(() => {
let {
countInterval
} = this.data;
if (countInterval <= 1) {
clearInterval(time);
this.setData({
countInterval: INTERVAL,
codeBtnText: '重新获取'
});
return;
}
countInterval--
this.setData({
countInterval,
codeBtnText: `${countInterval}s`
})
}, 1000)
} else {
wx.showToast({
title: '业务验证不通过!',
duration: 2000,
icon: 'error',
});
}
};
js
// 主要代码
import { captchaVerifyCallback, onBizResultCallback } from 'ali-captcha.js';
const AliyunCaptchaPluginInterface = requirePlugin('AliyunCaptcha');
onLoad() {
const pluginProps = {
SceneId: '',
mode: 'popup',
captchaVerifyCallback: captchaVerifyCallback.bind(this),
onBizResultCallback: onBizResultCallback.bind(this),
slideStyle: {
width: 540,
height: 60,
},
language: 'cn',
region: 'cn',
};
getCaptchaConfig().then((res) => {
pluginProps.SceneId = res.data.scene_id;
this.setData({
loadCaptcha: true,
pluginProps,
});
}).catch(err=>{
wx.showToast({
title: '获取验证码配置失败',
icon: 'none',
duration: 3000
})
})
},
getCodeHandle() {
// 表单校验
AliyunCaptchaPluginInterface.show();
}
开始分包
写好,测试,没问题,提CR,结果

居然把插件放在主包了,马上改,使用分包异步化+占位组件
文档:在小程序中,不同的分包对应不同的下载单元;因此,除了非独立分包可以依赖主包外,分包之间不能互相使用自定义组件或进行 require
。「分包异步化」特性将允许通过一些配置和新的接口,使部分跨分包的内容可以等待下载后异步使用,从而一定程度上解决这个限制。
将插件封装为一个单独的组件
json
{
"component": true,
"usingComponents": {
"aliyun-captcha": "plugin://AliyunCaptcha/captcha"
}
}
js
import { getCaptchaConfig, postCode } from '../../service/api';
const AliyunCaptchaPluginInterface = requirePlugin('AliyunCaptcha');
// 业务请求(带验证码校验)回调函数
const captchaVerifyCallback = async function (captchaVerifyParam) {
try {
const result = await postCode({ ali_captcha_verify_param: captchaVerifyParam, phone: this.data.phone });
return {
captchaResult: result.data.ali_verify_captcha,
bizResult: result.data.ali_verify_captcha,
};
} catch (error) {
AliyunCaptchaPluginInterface.refresh();
}
};
// 业务请求验证结果回调函数
const onBizResultCallback = function (bizResult) {
if (bizResult === true) {
this.triggerEvent('changeCodeBtnState');
} else {
wx.showToast({
title: '业务验证不通过!',
duration: 2000,
icon: 'error',
});
}
};
Component({
lifetimes: {
attached: function () {
const pluginProps = {
SceneId: '',
mode: 'popup',
captchaVerifyCallback: captchaVerifyCallback.bind(this),
onBizResultCallback: onBizResultCallback.bind(this),
slideStyle: {
width: 540,
height: 60,
},
language: 'cn',
region: 'cn',
}
getCaptchaConfig().then((res) => {
pluginProps.SceneId = res.data.scene_id;
this.setData({
loadCaptcha: true,
pluginProps,
});
}).catch(()=>{
wx.showToast({
title: '获取验证码配置失败',
icon: 'none',
duration: 3000
})
})
}
},
properties: {
phone: {
type: String,
value: ''
}
},
data: {
},
methods: {
show() {
AliyunCaptchaPluginInterface.show();
}
}
})
html
<aliyun-captcha id="captcha-element" wx:if="{{loadCaptcha}}" props="{{pluginProps}}" />
将其设置为一个单独的分包:
json
// app.json
{
"root": "pkg-plugin",
"pages": [
"ali-captcha/ali-captcha"
],
"plugins": {
"AliyunCaptcha": {
"version": "2.1.1",
"provider": "wxbe275ff84246f1a4"
}
}
}
分包完成,在需要用到的页面中使用
json
// login.json
{
"navigationBarTitleText": "登录",
"usingComponents": {
"captcha": "/pkg-plugin/ali-captcha/ali-captcha"
},
"componentPlaceholder": {
"captcha": "view"
}
}
html
<-- login.wxml -->
<captcha class="aliCaptcha" phone="{{ phone }}" bind:changeCodeBtnState="changeCodeBtnState" />
js
// login.js
// 处理表单校验等业务
参考: