shiny如何判断依赖关系
How does shiny determine dependencies
在开发应用程序时,我注意到 shiny 在没有必要时重新计算输出(在我看来),这似乎取决于 reactiveValues
.
中的嵌套
UI:
library(shiny)
n <- 5
o <- paste0('o', 1:n)
ui <- fluidPage(
lapply(o, function(x) textOutput(x)),
actionButton('a1', 'a1')
)
服务器一(按照我的预期重新计算输出):
server <- function(input, output){
rv <- reactiveValues(o1='a', o2='b', o3='c', o4='d', o5='e')
lapply(o, function(x){
output[[x]] <- renderText({
cat('rendering', x, '\n')
rv[[x]]
})
})
observeEvent(input$a1, {
rv$o1 <- rnorm(1)
})
}
服务器二(单击按钮时重新计算每个输出):
server <- function(input, output){
rv <- reactiveValues(
# difference with server one is that o1-5 are nested in l
l=list(o1='a', o2='b', o3='c', o4='d', o5='e')
)
lapply(o, function(x){
output[[x]] <- renderText({
cat('rendering', x, '\n')
rv$l[[x]]
})
})
observeEvent(input$a1, {
rv$l$o1 <- rnorm(1)
})
}
当 运行 服务器为 1 的应用程序时,每次单击按钮时,只会重新计算 o1
(如控制台中打印的那样)。但是,当 运行 应用程序有两个服务器时,每次单击按钮时,都会重新计算所有输出。
我想知道 shiny 是如何决定依赖关系的。是不是只能区分reactiveValues
上层的依赖,或者有没有办法让输出只依赖更深层次的reactiveValues
?换句话说,如果我 need/want 在服务器二中的情况,我可以防止 o1
以外的输出在单击按钮时重新计算吗?
对于 shiny
的 reactiveValues
,每个单独的对象 作为一个整体 是反应性组件:如果其结构中有任何东西(无论是 vector
, list
, data.frame
, etc) 改变了,那么整个对象就被认为改变了(因此事物会对其做出反应)。
要讨论是否可以对子组件做出反应,关于这个:任何对象都会被递归地。第一个对 list
的顶级组件做出反应的示例,还不错……但是如果要跟踪的事物有多个级别(并且没有理由或保护措施阻止这种情况发生),那么每次 某事 发生时,shiny
深入查看对象的每个单独组件都会产生大量开销。它的扩展性很差。
出于组织目的,您可以拥有多个独立的 reactiveValues
组件。虽然我怀疑它对性能有多大影响,但可以这样做:
server <- function(input, output){
rv1 <- reactiveValues(o1='a', o2='b', o3='c')
rv2 <- reactiveValues(o4='d', o5='e')
# ...
}
坦率地说,在某些情况下,这可能会更好:尽可能紧凑(一个超大 reactiveValues
调用)可能会稍微快一点(尽管我仍然不知道这一点是真的),有人可能会争辩说 可读性 直接影响 可维护性 和 故障排除 。如果反应值具有声明性分组,那么当您(或其他人)在几个月不活动后查看您的代码时,它可能会产生很大的不同。
在开发应用程序时,我注意到 shiny 在没有必要时重新计算输出(在我看来),这似乎取决于 reactiveValues
.
UI:
library(shiny)
n <- 5
o <- paste0('o', 1:n)
ui <- fluidPage(
lapply(o, function(x) textOutput(x)),
actionButton('a1', 'a1')
)
服务器一(按照我的预期重新计算输出):
server <- function(input, output){
rv <- reactiveValues(o1='a', o2='b', o3='c', o4='d', o5='e')
lapply(o, function(x){
output[[x]] <- renderText({
cat('rendering', x, '\n')
rv[[x]]
})
})
observeEvent(input$a1, {
rv$o1 <- rnorm(1)
})
}
服务器二(单击按钮时重新计算每个输出):
server <- function(input, output){
rv <- reactiveValues(
# difference with server one is that o1-5 are nested in l
l=list(o1='a', o2='b', o3='c', o4='d', o5='e')
)
lapply(o, function(x){
output[[x]] <- renderText({
cat('rendering', x, '\n')
rv$l[[x]]
})
})
observeEvent(input$a1, {
rv$l$o1 <- rnorm(1)
})
}
当 运行 服务器为 1 的应用程序时,每次单击按钮时,只会重新计算 o1
(如控制台中打印的那样)。但是,当 运行 应用程序有两个服务器时,每次单击按钮时,都会重新计算所有输出。
我想知道 shiny 是如何决定依赖关系的。是不是只能区分reactiveValues
上层的依赖,或者有没有办法让输出只依赖更深层次的reactiveValues
?换句话说,如果我 need/want 在服务器二中的情况,我可以防止 o1
以外的输出在单击按钮时重新计算吗?
对于 shiny
的 reactiveValues
,每个单独的对象 作为一个整体 是反应性组件:如果其结构中有任何东西(无论是 vector
, list
, data.frame
, etc) 改变了,那么整个对象就被认为改变了(因此事物会对其做出反应)。
要讨论是否可以对子组件做出反应,关于这个:任何对象都会被递归地。第一个对 list
的顶级组件做出反应的示例,还不错……但是如果要跟踪的事物有多个级别(并且没有理由或保护措施阻止这种情况发生),那么每次 某事 发生时,shiny
深入查看对象的每个单独组件都会产生大量开销。它的扩展性很差。
出于组织目的,您可以拥有多个独立的 reactiveValues
组件。虽然我怀疑它对性能有多大影响,但可以这样做:
server <- function(input, output){
rv1 <- reactiveValues(o1='a', o2='b', o3='c')
rv2 <- reactiveValues(o4='d', o5='e')
# ...
}
坦率地说,在某些情况下,这可能会更好:尽可能紧凑(一个超大 reactiveValues
调用)可能会稍微快一点(尽管我仍然不知道这一点是真的),有人可能会争辩说 可读性 直接影响 可维护性 和 故障排除 。如果反应值具有声明性分组,那么当您(或其他人)在几个月不活动后查看您的代码时,它可能会产生很大的不同。