如何在Vaadin 8中引入远程JS模块以集成FriendlyCaptcha?
在Vaadin 8中集成FriendlyCaptcha的问题与解决方法
问题描述
我尝试在Vaadin 8应用中集成FriendlyCaptcha,需要引入两个远程JavaScript模块的script标签:
<script type="module" src="https://unpkg.com/friendly-challenge@0.9.8/widget.module.min.js" async defer></script> <script nomodule src="https://unpkg.com/friendly-challenge@0.9.8/widget.min.js" async defer></script>
该功能在普通HTML文件中可正常运行,示例代码如下:
<!DOCTYPE HTML> <html> <body> <form METHOD="POST"> <input type="checkbox"> <div class="frc-captcha" data-sitekey="MY-SITE-KEY" data-callback="myCallback"></div> <input type="submit" id="sub" disabled=true> </form> <script> function myCallback(){ document.getElementById("sub").disabled = false; } </script> <script type="module" src="https://unpkg.com/friendly-challenge@0.9.8/widget.module.min.js" async defer></script> <script nomodule src="https://unpkg.com/friendly-challenge@0.9.8/widget.min.js" async defer></script> </body> </html>
但我在Vaadin 8中尝试以下实现时,div元素可显示但验证码无法加载:
captchaDiv = new Label( "<div class=\"frc-captcha\" data-sitekey=\"MY_SITE_KEY\" data-callback=\"myCallback\"></div>" + "<script type=\"module\" src=\"https://unpkg.com/friendly-challenge@0.9.8/widget.module.min.js\" async defer></script>\n" + "<script nomodule src=\"https://unpkg.com/friendly-challenge@0.9.8/widget.min.js\" async defer></script>", ContentMode.HTML); addComponent(captchaDiv); setComponentAlignment(captchaDiv, Alignment.MIDDLE_CENTER);
Vaadin 14有@JsModule注解,但Vaadin 8没有,请问如何确保这些JavaScript模块能在客户端正常加载?
解决方法
Vaadin 8会过滤Label中直接注入的script标签,且渲染时机无法保证脚本正确初始化,需通过Page对象向页面头部添加脚本,并确保回调与元素初始化顺序正确:
1. 向页面头部添加远程脚本
在UI的init()方法或视图初始化逻辑中,用Page.getCurrent()创建并添加脚本:
@Override protected void init(VaadinRequest vaadinRequest) { // 添加module类型脚本 Page.getCurrent().getJavaScript().add( "var moduleScript = document.createElement('script'); " + "moduleScript.type = 'module'; " + "moduleScript.src = 'https://unpkg.com/friendly-challenge@0.9.8/widget.module.min.js'; " + "moduleScript.async = true; " + "moduleScript.defer = true; " + "document.head.appendChild(moduleScript);" ); // 添加nomodule类型脚本 Page.getCurrent().getJavaScript().add( "var nomoduleScript = document.createElement('script'); " + "nomoduleScript.nomodule = true; " + "nomoduleScript.src = 'https://unpkg.com/friendly-challenge@0.9.8/widget.min.js'; " + "nomoduleScript.async = true; " + "nomoduleScript.defer = true; " + "document.head.appendChild(nomoduleScript);" ); // 后续添加组件逻辑... }
2. 注入回调函数
确保回调函数在captcha初始化前可用,同样通过Page对象注入:
Page.getCurrent().getJavaScript().add( "function myCallback() { " + " // 若操作Vaadin组件,需用Vaadin API获取,比如:" + " // var submitBtn = UI.getCurrent().getComponentById('submitBtn'); " + " // submitBtn.setEnabled(true); " + " document.getElementById('sub').disabled = false; " + "}" );
3. 单独添加Captcha元素
不要将script与div混在Label中,单独渲染captcha容器:
Label captchaDiv = new Label( "<div class=\"frc-captcha\" data-sitekey=\"MY_SITE_KEY\" data-callback=\"myCallback\"></div>", ContentMode.HTML ); addComponent(captchaDiv); setComponentAlignment(captchaDiv, Alignment.MIDDLE_CENTER);
4. 手动触发Captcha初始化(可选)
若脚本加载后captcha仍未自动初始化,可在页面加载完成后手动调用渲染方法:
Page.getCurrent().getJavaScript().add( "window.addEventListener('load', function() { " + " if (window.frc) { " + " frc.render(document.querySelector('.frc-captcha'), { " + " sitekey: 'MY_SITE_KEY', " + " callback: myCallback " + " }); " + " } " + "});" );
核心原因
Vaadin 8的HTML内容安全机制会过滤Label内的script标签,且Label渲染时机晚于脚本初始化需求。通过Page对象直接操作DOM添加脚本,能确保脚本被加载到正确位置并按预期执行。
内容的提问来源于stack exchange,提问作者snek
相关产品推荐
相关产品推荐

