Dash应用浏览器端回调常用方法总结

本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/dash-master

大家好我是费老师,回调函数 是我们在Dash应用中实现各种交互功能的核心,在绝大多数情况下,我们只需要以纯Python的方式编写常规服务端回调函数即可,这也贯彻了Dash无需编写javascript即可构建web应用的理念。

但这并不代表在Dash应用中我们只能使用Python,更自由地,Dash针对回调函数编写还提供了client side callback (我们通常称作浏览器端回调 )相关功能,使得我们可以在仍然使用Python编排回调函数角色的基础上,嵌入自定义的javascript代码片段来执行相应的回调输入输出逻辑,从而解决一些特殊的需求。今天的文章中,我就将带大家一起学习Dash浏览器端回调常用的方法和技巧😎。
阅读本文大约需要15分钟

浏览器端回调 ,顾名思义,其对应的函数体计算过程是在每个用户的本地浏览器 中执行的,这在一些特殊的场景下,可以帮助我们节省服务器算力 、网络传输带宽 等消耗,还可以在用户网络状况很差 时,提升一些用户交互功能的流畅度 ,亦或是可以让我们在Dash应用中额外引入javascript生态的功能(譬如在Dash应用中高效渲染原生echarts图表)。

而在Dash中,我们主要有两种定义浏览器端回调的方式:

1 基于app.clientside_callback编写简单浏览器端逻辑

此种浏览器端回调定义方式适用于执行非常简单的javascript代码片段,只需要为app.clientside_callback()的第一个参数传入字符串形式的javascript函数体即可(推荐使用箭头函数),其中函数体内部参数的输入,以及结果的输出,原则类似常规的回调函数。

举个例子,我们来实现一段非常简单的逻辑,通过按钮的点击,来触发对应模态框的打开:

对应app.clientside_callback的完整应用代码如下:

app1.py

Python 复制代码
import dash
from dash import html
import feffery_antd_components as fac
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        fac.AntdButton(
            '打开模态框',
            id='open-modal',
            type='primary'
        ),
        fac.AntdModal(
            fac.AntdParagraph('测试内容'*100),
            id='modal',
            title='模态框示例'
        )
    ],
    style={
        'padding': '50px 100px'
    }
)

app.clientside_callback(
    '(nClicks) => true',
    Output('modal', 'visible'),
    Input('open-modal', 'nClicks'),
    prevent_initial_call=True
)

if __name__ == '__main__':
    app.run(debug=True)

可以看到,写法非常简单,对于编写此类简单浏览器端回调的需求,我们只需要用到javascript最基础的语法,非常的方便😇,再来个稍微复杂一点的例子,我们基于轮询组件,实现当前系统时间的实时更新:

app2.py

python 复制代码
import dash
from dash import html, dcc
import feffery_antd_components as fac
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        dcc.Interval(
            id='interval',
            interval=1000  # 每秒触发一次
        ),
        fac.AntdStatistic(
            id='current-datetime',
            title='当前时间'
        )
    ],
    style={
        'padding': '50px 100px'
    }
)

app.clientside_callback(
    '''(n_intervals) => {
        return `${new Date().toLocaleDateString().replaceAll("/", "-")} ${new Date().toLocaleTimeString()}`
    }''',
    Output('current-datetime', 'value'),
    Input('interval', 'n_intervals')
)

if __name__ == '__main__':
    app.run(debug=True)

2 基于ClientsideFunction编写复杂浏览器端回调

如果我们想要执行的浏览器端回调逻辑比较复杂和冗长,那么在app.clientside_callback里用字符串的方式写大段的javascript代码就不太高效了🙅‍♂️,相应的我们可以改为使用ClientsideFunction来定义。

使用ClientsideFunction来定义浏览器端回调,我们首先需要在我们的Dash应用静态资源目录下(默认为assets)建立相应的js文件(名称随意,Dash应用会自动加载静态资源目录下的js文件到用户浏览器中),并在该js文件中按照下列格式定义若干javascript回调函数:

javascript 复制代码
window.dash_clientside = Object.assign({}, window.dash_clientside, {
    clientside: {
        func1: () => {
            // write your code logic
        }
    }
});

接着在相应的Python程序中配合ClientsideFunction按照下列格式关联编排回调函数即可:

python 复制代码
app.clientside_callback(
    ClientsideFunction(
        namespace='clientside',
        function_name='函数名称'
    ),
    # 照常编排回调角色
)

废话不多说,我们直接将上文中实时刷新系统时间的示例改造成ClientsideFunction形式以便理解:

assets/clientside_callbacks.js

python 复制代码
window.dash_clientside = Object.assign({}, window.dash_clientside, {
    clientside: {
        update_datetime: (n_intervals) => {
            return `${new Date().toLocaleDateString().replaceAll("/", "-")} ${new Date().toLocaleTimeString()}`
        }
    }
});

app3.py

python 复制代码
import dash
from dash import html, dcc
import feffery_antd_components as fac
from dash.dependencies import Input, Output, ClientsideFunction

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        dcc.Interval(
            id='interval',
            interval=1000  # 每秒触发一次
        ),
        fac.AntdStatistic(
            id='current-datetime',
            title='当前时间'
        )
    ],
    style={
        'padding': '50px 100px'
    }
)

app.clientside_callback(
    ClientsideFunction(
        namespace='clientside',
        function_name='update_datetime'
    ),
    Output('current-datetime', 'value'),
    Input('interval', 'n_intervals')
)

if __name__ == '__main__':
    app.run(debug=True)

这样做的好处在于,我们可以把相对复杂的javascript逻辑在原生的js程序里编写,从而配合现代化ide获得更高效的编程体验,并且利用ClientsideFunction形式,可以很方便地实现外部js框架的引入使用,譬如引入使用原生echarts,篇幅有限,今天先按下不表,之后另外发文举例介绍。

3 编写浏览器端回调的常用技巧

通过上文,我们知晓了Dash中构建浏览器端回调的基本形式,下面我们补充一些有关浏览器端回调的实用技巧:

3.1 配合插件快捷生成模板代码

编写浏览器端回调,尤其是配合ClientsideFunction时,其代码格式还是有些特殊的,不过别担心,如果你恰好在使用vscode编写Dash应用,可以在拓展里安装由我开发维护的插件feffery-dash-snippets,安装完成后,可以通过输入一些快捷短语,进行相关代码模板的生成。

目前针对浏览器端回调+ClientsideFunction,在py文件中可用的快捷短语有:

  • callback-cs:oi:快速初始化具有Input和Output角色的浏览器端回调函数
  • callback-cs:ois:快速初始化具有Input、Output及State角色的浏览器端回调函数

在js文件中可用的快捷短语有:

  • callback:init:快捷生成浏览器端回调函数定义模板

3.2 常用对象在浏览器端回调中的写法

在常规的服务端回调函数中我们经常会使用到dash.no_update、PreventUpdate、dash.callback_context等对象来辅助回调函数功能逻辑的完成,而在浏览器端回调中,这些对象的写法要做一定变化:

  • dash.no_update

dash.no_update在浏览器端回调中写作window.dash_clientside.no_update,你也可以用feffery-dash-snippets插件中的dash.no_update快捷短语生成:

  • PreventUpdate

PreventUpdate在浏览器端回调中写作PreventUpdate,你也可以用feffery-dash-snippets插件中的PreventUpdate快捷短语生成(注意,在浏览器端回调中throw window.dash_clientside.PreventUpdate等价于常规回调中的raise PreventUpdate):

  • dash.callback_context

dash.callback_context在浏览器端回调中写作window.dash_clientside.callback_context,你也可以用feffery-dash-snippets插件中的dash.callback_context快捷短语生成:

3.3 在浏览器端回调中返回组件元素

我们在常规回调函数中,经常会以一些组件的children或其他组件型 参数为Output目标,直接返回组件元素,在Python中这样做很稀疏平常,但是在浏览器端回调中,我们如果有此类需求,则需要返回规定的JSON数据格式,来表示一个组件元素:

json 复制代码
{
    props: {
        // 定义当前组件的各属性,如
        id: '组件id'
    },
    type: '组件完整名称,如AntdButton',
    namespace: '组件所属组件库完整名称,如feffery_antd_components'
}

我们还是结合实际案例来做演示,这里我们的演示功能实现了通过按钮点击触发新的消息提示弹出:

具体代码如下,可以看到只要我们按照格式返回相应的组件JSON数据,Dash就会在浏览器中自动进行转换及渲染:

app4.py

python 复制代码
import dash
from dash import html
import feffery_antd_components as fac
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        fac.AntdButton(
            '新的消息',
            id='new-message',
            type='primary'
        ),
        html.Div(id='new-message-container')
    ],
    style={
        'padding': '50px 100px'
    }
)

app.clientside_callback(
    '''(nClicks) => ({
        props: {
            content: "新的消息,nClicks:" + nClicks,
            type: "info"
        },
        type: "AntdMessage",
        namespace: "feffery_antd_components"
    })''',
    Output('new-message-container', 'children'),
    Input('new-message', 'nClicks'),
    prevent_initial_call=True
)

if __name__ == '__main__':
    app.run(debug=True)

以上就是本文的全部内容,更多有关dash应用开发的前沿知识和技巧欢迎持续关注玩转dash公众号。

相关推荐
alphaTao21 分钟前
LeetCode 每日一题 2026/9/21-2026/9/27
python·算法·leetcode
计算机源码社1 小时前
【27届大数据毕设】基于Spark的AI艺术创作者价值评估与市场趋势预测研究 基于Python与Spark的AI生成艺术受众画像及异常热度识别可视化平台
大数据·人工智能·hadoop·python·spark·毕业设计·课程设计
huisheng_qaq3 小时前
【Python基础篇-04】深入理解python的函数、参数与作用域
python·ai·函数·参数作用域
宸津-代码粉碎机7 小时前
OpenAI 连夜迎战 Grok Bot 和 Muse:AI 智能体从 “会聊天” 到 “能办事”,现在入场还来得及吗
java·大数据·人工智能·分布式·python
Y3815326627 小时前
SERP API 计费口径:credits_charged、执行失败扣费与退款边界
python·搜索引擎
蒸鱼Yuzheng9 小时前
设备端性能工件可靠导出:断点续传、哈希、manifest 与失败恢复
android·自动化测试·python·adb·数据完整性
圆圆讲门店10 小时前
挑选同城获客服务机构时需要考量的核心因素都有哪些?
大数据·网络·人工智能·python
傻啦嘿哟10 小时前
Python的默认参数把我坑惨了,原来写[]和写None的区别这么大
开发语言·python·机器学习
微小冷10 小时前
Python凸优化cvxpy初步
python·机器人·凸优化·数学规划·cvxpy
在世修行11 小时前
干货:显式映射 vs 自动判据
python·插件