shinyWidgets::pickerInput大选项量下如何实现类似updateSelectizeInput的服务端更新加速
关于shinyWidgets::pickerInput承载大量选项的性能优化方案
问题本质
shinyWidgets::pickerInput 底层依赖 Bootstrap-select 前端组件,原生不支持 selectize.js 内置的服务端选项分片加载能力,默认全量选项会被序列化为HTML节点一次性推送到前端渲染,百万级节点的DOM操作会导致加载、交互严重卡顿。
可行优化方案
方案1:开启原生虚拟滚动(最简便,改动最小)
新版shinyWidgets已经适配了Bootstrap-select的虚拟滚动特性,开启后前端仅渲染可视区域内的选项,不会全量生成DOM节点,可直接支持百万级选项流畅加载。
示例代码:
library(shiny) library(shinyWidgets) ui = fluidPage( pickerInput( inputId = "select", label = "Select:", choices = NULL, options = pickerOptions( virtualScroll = TRUE, # 核心:开启虚拟滚动 liveSearch = TRUE, # 可选:开启搜索 liveSearchNormalize = TRUE, maxOptions = 10 # 可选:限制最大选择数 ) ) ) server = function(input, output, session) { observe({ # 百万级选项直接更新也不会卡顿 updatePickerInput( session = session, inputId = "select", choices = 1:1000000, selected = 1 ) }) } shinyApp(ui, server)
注意:需要确保你的shinyWidgets版本 >= 0.7.0,Bootstrap-select版本 >= 1.14.0才能支持虚拟滚动参数。
方案2:服务端动态筛选(性能最优,适配超大规模数据)
如果数据量超过200万,或者需要更高的加载速度,可以实现搜索触发的服务端动态过滤:仅在用户输入搜索关键词后,服务端返回匹配的前N条选项,永远只传输少量数据到前端。
示例代码:
library(shiny) library(shinyWidgets) # 模拟全量数据集 all_choices <- as.character(1:1000000) ui = fluidPage( pickerInput( inputId = "select", label = "Select:", choices = "请输入关键词搜索", options = pickerOptions( liveSearch = TRUE, liveSearchNormalize = TRUE, `liveSearchPlaceholder` = "输入数字搜索" ) ) ) server = function(input, output, session) { # 监听搜索框输入值(pickerInput的搜索值会存在input$select_search中) observeEvent(input$select_search, { search_val <- input$select_search if(nchar(search_val) == 0) { choices <- "请输入关键词搜索" } else { # 服务端过滤匹配的选项,最多返回200条 matches <- grep(search_val, all_choices, value = TRUE, fixed = TRUE)[1:200] choices <- matches } updatePickerInput(session, "select", choices = choices) }) } shinyApp(ui, server)
方案3:selectizeInput自定义渲染(兼顾性能和自定义能力)
如果你需要的pickerInput自定义能力(比如选项着色、多列布局、自定义HTML格式),selectizeInput也可以通过options中的render参数实现同等效果,同时原生支持服务端加载,性能最优:
核心代码示例:
selectizeInput( inputId = "select", label = "Select:", choices = NULL, options = list( render = I("{ option: function(item, escape) { return '<div style=\"color: red; padding: 5px;\">' + escape(item.label) + '</div>'; } }") ) ) # 服务端更新开启server=TRUE即可,和你之前的示例逻辑一致
内容的提问来源于stack exchange,提问作者ForceLeft415
相关产品推荐
相关产品推荐

