You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 12:55:20