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
相关产品推荐
相关产品推荐

