Google reCAPTCHA v2反复刷新偶发grecaptcha is not defined错误如何解决
问题根因
报错的核心原因是两个异步操作的执行时序不固定:
- Google reCAPTCHA的脚本下载完成后,还需要做内部初始化才会在全局挂载
grecaptcha对象,这个过程是异步的 - 你请求后端拿siteKey的AJAX请求也是异步的
当页面刷新时,如果AJAX请求先返回,执行render逻辑时grecaptcha还没初始化完成,就会触发grecaptcha is not defined的报错。你之前用$.getScript没生效也是同理,$.getScript的回调仅代表脚本文件下载完成,不代表reCAPTCHA内部初始化完成、grecaptcha可用。
解决方案
以下方案完全不需要使用定时器,也不需要把业务逻辑耦合到脚本加载逻辑里,原有业务代码基本不用改动:
方案1:双状态校验触发(最稳定)
- 先修改reCAPTCHA的脚本引入地址,添加onload参数指定初始化完成后的回调函数名,这个回调仅做状态标记,不涉及任何业务逻辑:
<script src="https://www.google.com/recaptcha/api.js?render=explicit&onload=onRecaptchaReady"></script>
- 在全局作用域定义状态变量和统一校验方法:
// 存储reCAPTCHA初始化完成状态 let isRecaptchaReady = false; // 存储从后端拿到的siteKey let fetchedSiteKey = null; // reCAPTCHA初始化完成后会自动调用该方法 window.onRecaptchaReady = function() { isRecaptchaReady = true; // 检查是否已经拿到siteKey,拿到就直接渲染 tryRenderCaptcha(); } // 统一渲染校验逻辑,两个条件都满足才会执行渲染 function tryRenderCaptcha() { if (isRecaptchaReady && fetchedSiteKey) { grecaptcha.render('captcha_container', { 'sitekey': fetchedSiteKey }); } }
- 仅需要修改AJAX成功回调里的siteKey相关逻辑,其他原有业务逻辑完全保留:
$.ajax({ url : url, type : 'GET', headers : { "cache-control" : "no-cache" }, success : function(data) { const siteKeyValue = data.siteKey; if(siteKeyValue) { fetchedSiteKey = siteKeyValue; // 检查reCAPTCHA是否初始化完成,完成就直接渲染 tryRenderCaptcha(); } // 原有其他业务逻辑全部保留在此处即可,不需要做任何改动 } });
该方案完全没有时序问题,不管是AJAX先返回还是reCAPTCHA先初始化完成,只要两个条件都满足就会自动触发渲染,逻辑完全解耦。
方案2:轻量轮询检测(不需要修改脚本引入规则)
如果确实不想在脚本地址上加onload参数,可以用短间隔轮询检测grecaptcha可用性,性能损耗几乎可以忽略:
$.ajax({ url : url, type : 'GET', headers : { "cache-control" : "no-cache" }, success : function(data) { const siteKeyValue = data.siteKey; if(siteKeyValue) { // 每50ms检测一次grecaptcha是否可用,可用就立刻执行渲染 const checkCaptchaInterval = setInterval(() => { if (window.grecaptcha) { clearInterval(checkCaptchaInterval); grecaptcha.render('captcha_container', { 'sitekey': siteKeyValue }); } }, 50); } // 原有其他业务逻辑全部保留在此处即可 } });
内容的提问来源于stack exchange,提问作者Tales
相关产品推荐
相关产品推荐

