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 以外的输出在单击按钮时重新计算吗?

对于 shinyreactiveValues,每个单独的对象 作为一个整体 是反应性组件:如果其结构中有任何东西(无论是 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 调用)可能会稍微快一点(尽管我仍然不知道这一点是真的),有人可能会争辩说 可读性 直接影响 可维护性 故障排除 。如果反应值具有声明性分组,那么当您(或其他人)在几个月不活动后查看您的代码时,它可能会产生很大的不同。