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

Vaadin 14下不刷新Portlet实现Portal与Portlet主题切换方案咨询

不刷新IFrame Portlet实现Vaadin Portal主题同步切换方案

核心思路是利用浏览器PostMessage API实现Portal(父页面)与Portlet(IFrame子页面)的跨页面通信,同步主题切换指令,同时保留Portlet的用户状态。以下是具体实现步骤:

一、Portal端(父应用)实现

1. 主题切换按钮逻辑

在Portal的AppLayout头部添加主题切换按钮,点击时完成两个操作:切换自身主题,同时向所有Portlet IFrame发送主题切换指令。

Button themeToggleBtn = new Button("切换明暗主题", click -> {
    // 切换Portal自身的Vaadin主题
    UI currentUI = UI.getCurrent();
    String currentTheme = currentUI.getElement().getAttribute("theme");
    String targetTheme = "dark".equals(currentTheme) ? "light" : "dark";
    currentUI.getElement().setAttribute("theme", targetTheme);

    // 通过PostMessage向所有Portlet IFrame发送主题切换消息
    currentUI.getPage().executeJs("""
        // 匹配所有加载Portlet的IFrame(根据实际URL规则调整选择器)
        const portletIframes = document.querySelectorAll('iframe[src*="/your-portlet-path/"]');
        portletIframes.forEach(iframe => {
            iframe.contentWindow.postMessage(
                { type: 'SYNC_THEME', theme: $0 },
                'https://your-portlet-domain.com' // 生产环境替换为Portlet的实际域名
            );
        });
    """, targetTheme);
});

2. 处理Portlet初始主题同步请求

当Portlet首次加载时,可能需要主动请求Portal的当前主题以保持一致,因此在Portal端添加消息监听:

// 在Portal的UI初始化方法中添加
UI.getCurrent().getPage().executeJs("""
    window.addEventListener('message', event => {
        // 验证消息来源,仅处理可信Portlet域名的请求
        if (event.origin !== 'https://your-portlet-domain.com') return;
        
        if (event.data.type === 'REQUEST_INIT_THEME') {
            // 获取Portal当前主题
            const portalTheme = document.querySelector('vaadin-ui').getAttribute('theme');
            // 回复Portlet初始主题
            event.source.postMessage(
                { type: 'INIT_THEME', theme: portalTheme },
                event.origin
            );
        }
    });
""");

二、Portlet端(子应用)实现

1. 监听Portal的主题切换指令

在Portlet的Vaadin UI初始化时,添加浏览器消息监听,接收Portal的主题切换指令并同步自身主题,无需刷新页面:

@Override
protected void init(VaadinRequest request) {
    // 其他业务初始化逻辑...

    // 添加主题同步监听
    UI.getCurrent().getPage().executeJs("""
        // 监听来自Portal的主题同步消息
        window.addEventListener('message', event => {
            if (event.origin !== 'https://your-portal-domain.com') return;
            
            if (event.data.type === 'SYNC_THEME') {
                // 切换Portlet的Vaadin主题
                const vaadinUI = document.querySelector('vaadin-ui');
                vaadinUI.setAttribute('theme', event.data.theme);
            }
            
            // 处理初始主题同步回复
            if (event.data.type === 'INIT_THEME') {
                const vaadinUI = document.querySelector('vaadin-ui');
                vaadinUI.setAttribute('theme', event.data.theme);
            }
        });

        // 首次加载时向Portal请求初始主题
        window.parent.postMessage(
            { type: 'REQUEST_INIT_THEME' },
            'https://your-portal-domain.com'
        );
    """);
}

2. 确保Lumo主题配置正确

确保Portlet的前端样式中导入了Lumo的完整主题(包括明暗模式),在frontend/styles/shared-styles.css中添加:

@import '@vaadin/vaadin-lumo-styles/all-imports';

/* 可选:自定义明暗主题变量 */
[theme="dark"] {
    --lumo-primary-color: #2196F3;
    --lumo-base-color: #121212;
    /* 其他自定义深色主题变量 */
}

三、关键注意事项

  • 域名验证:生产环境必须在PostMessage中指定具体的目标域名(不能用*),避免跨域安全风险。
  • 主题切换方式:上述示例使用Vaadin UI的theme属性切换,如果你是通过body类(如body.dark)实现主题,只需修改JS中的DOM操作逻辑即可。
  • 状态保留:此方案无需刷新IFrame,Portlet中的用户输入数据、组件状态都会完整保留。

内容的提问来源于stack exchange,提问作者user12926217

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 04:05:36