React模块化项目中整合Google Recaptcha的脚本加载方案咨询
我之前在React模块化项目里整合Google reCAPTCHA的时候,刚好踩过类似的坑,给你分享几个适配模块化架构的封装方案,亲测好用:
方案一:动态Script加载工具函数(基础版)
先封装一个通用的动态加载脚本工具,这样不仅能用来加载reCAPTCHA,其他需要动态引入的脚本也能复用。
// utils/loadScript.js export const loadScript = (url, callback) => { // 先检查脚本是否已经存在,避免重复加载 if (document.querySelector(`script[src="${url}"]`)) { callback?.(); return; } const script = document.createElement('script'); script.src = url; script.async = true; script.defer = true; // 加载成功回调 script.onload = () => { callback?.(); }; // 加载失败处理 script.onerror = (error) => { console.error('脚本加载失败:', error); }; document.body.appendChild(script); };
然后在你的React组件里这样用:
import { useEffect, useState } from 'react'; import { loadScript } from '../utils/loadScript'; const RecaptchaComponent = () => { const [isRecaptchaReady, setIsRecaptchaReady] = useState(false); useEffect(() => { // 定义Google脚本需要的全局回调函数 window.onloadCallback = () => { setIsRecaptchaReady(true); // 这里可以初始化reCAPTCHA实例 }; // 加载reCAPTCHA脚本 loadScript( 'https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit', () => console.log('reCAPTCHA脚本加载完成') ); // 组件卸载时清理全局回调,避免内存泄漏 return () => { delete window.onloadCallback; }; }, []); return ( <div> {isRecaptchaReady ? ( <div id="recaptcha-box"></div> ) : ( <p>reCAPTCHA加载中...</p> )} </div> ); }; export default RecaptchaComponent;
方案二:自定义Hook封装(React化复用方案)
如果你的项目里多个组件都需要用到reCAPTCHA,用自定义Hook封装会更符合React的组件化思想,复用性更强:
// hooks/useRecaptcha.js import { useEffect, useState } from 'react'; export const useRecaptcha = () => { const [isLoaded, setIsLoaded] = useState(false); const [loadError, setLoadError] = useState(null); useEffect(() => { const recaptchaUrl = 'https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit'; // 如果已经加载过grecaptcha,直接标记为就绪 if (window.grecaptcha) { setIsLoaded(true); return; } // 定义全局回调 window.onloadCallback = () => { setIsLoaded(true); }; const script = document.createElement('script'); script.src = recaptchaUrl; script.async = true; script.defer = true; script.onload = () => console.log('reCAPTCHA脚本加载成功'); script.onerror = (err) => { setLoadError('reCAPTCHA加载失败,请稍后重试'); console.error(err); }; document.body.appendChild(script); // 清理函数 return () => { delete window.onloadCallback; }; }, []); return { isLoaded, loadError, grecaptcha: window.grecaptcha }; };
组件中使用这个Hook:
import { useEffect, useRef } from 'react'; import { useRecaptcha } from '../hooks/useRecaptcha'; const LoginForm = () => { const { isLoaded, loadError, grecaptcha } = useRecaptcha(); const recaptchaRef = useRef(null); useEffect(() => { if (isLoaded && recaptchaRef.current) { // 渲染reCAPTCHA grecaptcha.render(recaptchaRef.current, { sitekey: process.env.REACT_APP_RECAPTCHA_SITE_KEY, // 建议用环境变量存密钥 callback: (token) => { console.log('验证通过,token:', token); // 这里处理表单提交逻辑,比如把token传给后端验证 } }); } }, [isLoaded, grecaptcha]); if (loadError) return <p className="error">{loadError}</p>; if (!isLoaded) return <p>加载中...</p>; return ( <form> <input type="email" placeholder="邮箱" /> <input type="password" placeholder="密码" /> {/* 挂载reCAPTCHA的容器 */} <div ref={recaptchaRef}></div> <button type="submit">登录</button> </form> ); }; export default LoginForm;
几个关键注意事项
- 全局回调必须挂在window上:Google的reCAPTCHA脚本会全局查找
onloadCallback函数,所以必须把它挂载到window对象上,组件卸载时记得清理,避免内存泄漏。 - 避免重复加载:每次加载前先检查脚本是否已经存在,或者检查
window.grecaptcha是否已定义,防止多次插入script标签。 - 环境变量存密钥:不要把reCAPTCHA的站点密钥硬编码在代码里,存在环境变量(比如
.env文件)更安全。 - SSR项目适配:如果你的项目是服务端渲染(比如Next.js),要确保脚本只在客户端加载,可以用
typeof window !== 'undefined'判断环境,或者用框架提供的客户端加载方案。
内容的提问来源于stack exchange,提问作者JavaHead
相关产品推荐
相关产品推荐

