Flask+Bootstrap+Dash多应用切换后未激活应用持续刷新问题
解决Dash应用切换标签页后后台仍刷新的问题
嘿,我来帮你捋捋这个问题!首先得先修正你代码里一个明显的小错误,然后再解决核心的刷新控制问题。
第一步:先修复路由配置的低级错误
看你的app_main.py里,/app2的路由竟然指向了app1.app_dash.server,这就导致你切换到app2标签时,实际加载的还是app1!这肯定会出现app1持续请求的情况,先把这个改过来:
from werkzeug.wsgi import DispatcherMiddleware from flask_bootstrap import Bootstrap import flask import app1 import app2 flask_app = flask.Flask(__name__) server = DispatcherMiddleware(flask_app, { '/app1': app1.app_dash.server, '/app2': app2.app_dash.server, # 这里之前写错了,现在修正为app2的server }) Bootstrap(flask_app)
另外还要检查index.html里的object标签路径,你挂载的路由是/app1和/app2,所以data属性应该对应:
<div id="app1" class="tab-pane fade"> <object data="/app1"></object> </div> <div id="app2" class="tab-pane fade"> <object data="/app2"></object> </div>
第二步:控制隐藏标签页的Dash应用停止刷新
即使修复了路由,切换标签后隐藏的object标签里的Dash应用还是会在后台运行——浏览器不会自动暂停隐藏页面的JS执行,所以dcc.Interval会持续触发/_dash-update-component请求。我们可以通过监听Bootstrap标签切换事件,动态控制Interval的启用/禁用来解决这个问题。
方案一:用Dash回调+前端消息通信(更稳妥,不依赖Dash内部实现)
这个方法通过父页面发送消息,让Dash应用自己控制Interval的状态:
- 修改app1.py(app2同理):给Interval添加
disabled属性,再加一个隐藏组件用来接收前端信号
import dash from dash import html, dcc, Input, Output, State app_dash = dash.Dash(__name__, server=app1.server, url_base_pathname='/app1/') app_dash.layout = html.Div([ dcc.Interval( id='interval-component', interval=5e3, n_intervals=0, disabled=False # 初始状态为启用 ), html.Div(id='tab-status-trigger', style={'display': 'none'}), # 隐藏的信号接收组件 dcc.Graph(id='graph_stream'), ]) # 回调:根据触发信号控制Interval的禁用状态 @app_dash.callback( Output('interval-component', 'disabled'), Input('tab-status-trigger', 'children') ) def toggle_interval(status): return status == 'hidden' # 前端监听父页面的消息,更新触发组件的值 app_dash.clientside_callback( """ function() { window.addEventListener('message', function(event) { const triggerEl = document.getElementById('tab-status-trigger'); if (event.data === 'show') { triggerEl.innerText = 'show'; } else if (event.data === 'hide') { triggerEl.innerText = 'hidden'; } }); return ''; } """, Output('tab-status-trigger', 'children'), Input('interval-component', 'n_intervals'), prevent_initial_call=True )
- 在index.html里添加标签切换监听JS:
<script> $(document).ready(function() { // 监听Bootstrap标签切换事件 $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { const activeTabId = $(e.target).attr('href'); // 获取当前激活的标签,比如#app1 // 给app1发送状态消息 const app1Frame = document.querySelector('#app1 object').contentWindow; if (activeTabId === '#app1') { app1Frame.postMessage('show', '*'); } else { app1Frame.postMessage('hide', '*'); } // 给app2发送状态消息(如果app2也有Interval的话) const app2Frame = document.querySelector('#app2 object').contentWindow; if (activeTabId === '#app2') { app2Frame.postMessage('show', '*'); } else { app2Frame.postMessage('hide', '*'); } }); }); </script>
方案二:直接前端操作Dash组件(简单但依赖Dash内部实现)
如果你不想改Dash代码,可以直接在父页面通过JS操作子页面的Interval组件,不过这个方法依赖Dash的React内部实现,版本更新可能失效:
<script> $(document).ready(function() { $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { const activeTabId = $(e.target).attr('href'); // 控制app1的Interval const app1Object = document.querySelector('#app1 object'); if (app1Object.contentWindow) { const intervalEl = app1Object.contentWindow.document.querySelector('[id="interval-component"]'); if (intervalEl) { // 获取Dash Interval的React实例 const intervalInstance = app1Object.contentWindow.ReactDOM.findDOMNode(intervalEl).__reactInternalInstance__.return.stateNode; if (activeTabId === '#app1') { intervalInstance.props.disabled = false; intervalInstance.start(); // 恢复刷新 } else { intervalInstance.props.disabled = true; intervalInstance.stop(); // 暂停刷新 } } } // 同理控制app2的Interval const app2Object = document.querySelector('#app2 object'); if (app2Object.contentWindow) { const intervalEl = app2Object.contentWindow.document.querySelector('[id="interval-component"]'); if (intervalEl) { const intervalInstance = app2Object.contentWindow.ReactDOM.findDOMNode(intervalEl).__reactInternalInstance__.return.stateNode; if (activeTabId === '#app2') { intervalInstance.props.disabled = false; intervalInstance.start(); } else { intervalInstance.props.disabled = true; intervalInstance.stop(); } } } }); }); </script>
总结
先把路由配置的错误修正,然后通过前端监听标签切换事件,动态控制对应Dash应用的dcc.Interval组件的启用状态,就能解决切换标签后后台仍持续刷新的问题了。
内容的提问来源于stack exchange,提问作者su79eu7k
相关产品推荐
相关产品推荐

