如何在 R Markdown 和 Shiny 中使用 htmlwidgets
更新时间
htmlwidgets 是 R 的一个框架,把 JavaScript 可视化库封装成 R 函数,用法和普通绘图函数类似:写一两行 R 代码就能生成 D3 图形或 Leaflet 地图。同一份 widget 对象可以在三个地方使用——R 控制台、R Markdown 文档、Shiny 应用。如果你已经会用 plot(),迁移成本几乎为零;需要额外配置的只有仪表盘组合(flexdashboard)和组件联动(crosstalk)这两类进阶场景。
先理解它的工作方式
htmlwidgets 本身不是可视化库,而是一座桥:R 端负责准备数据、生成配置,JavaScript 端负责渲染成交互式 HTML。官方页面把它和 R 绘图直接类比——"HTML widgets work just like R plots except they produce interactive web visualizations"。
这意味着两件事:
- 你不需要写 JavaScript,除非你要自己开发新 widget。
- 渲染结果依赖浏览器环境,所以它在 RStudio Viewer、R Markdown 输出和 Shiny 页面里表现一致,但在纯文本终端里看不到图形。
在 R 控制台运行
这是验证环境和熟悉 API 最快的方式。
- 安装一个具体 widget 包,例如
install.packages("leaflet")。 - 加载并调用:
library(leaflet)
leaflet() %>%
addTiles() %>%
addMarkers(lng = 116.4, lat = 39.9, popup = "示例点")
预期结果:RStudio 的 Viewer 面板出现一张可缩放、可点击标记的地图。如果只在控制台看到一段 HTML 或什么都没出现,通常是没有在支持 HTML 输出的环境里运行,而不是代码写错了。
嵌入 R Markdown 文档
在代码块里直接返回 widget 对象即可,不需要 print() 或额外包装:
```{r}
library(dygraphs)
dygraph(nhtemp, main = "年降水量") %>%
dyRangeSelector()
预期结果:knit 之后文档中出现可交互的时间序列图,鼠标拖动即可缩放区间。
常见卡点:
- 输出格式必须是 HTML 类(`html_document` 等)。输出 PDF 或 Word 时 widget 无法交互,只会退化成静态图或直接失败。
- 自包含文档(`self_contained: true`,默认值)会把 JavaScript 依赖内嵌进 HTML,文件变大但便于分享;依赖外部 CDN 时体积小,但离线打开会失效。
## 嵌入 Shiny 应用
在 `ui` 里用对应的 `xxxOutput()` 占位,在 `server` 里用 `renderXxx()` 渲染:
```r
library(shiny)
library(plotly)
ui <- fluidPage(
plotlyOutput("p")
)
server <- function(input, output, session) {
output$p <- renderPlotly({
plot_ly(mtcars, x = ~wt, y = ~mpg, type = "scatter", mode = "markers")
})
}
shinyApp(ui, server)
预期结果:页面中出现可悬停、可框选的散点图。
常见卡点:widget 包通常提供自己的 render 函数(如 renderPlotly、renderLeaflet),不要用通用的 renderPlot 去接,否则不会渲染。另外,Shiny 中的交互(如点击事件)需要通过 widget 包暴露的 event 接口回传到 R,而不是自动变成 input$ 值。
组合多个 widget:flexdashboard 与 crosstalk
单个 widget 只能解决一张图的问题。官方给出两条组合路径:
| 需求 | 方案 | 说明 |
|---|---|---|
| 把多个 widget 排成仪表盘 | flexdashboard | 基于行列的灵活布局,适合多图并列展示 |
| 让多个 widget 联动 | crosstalk | 支持 linked brushing 和客户端筛选,无需 Shiny 服务器 |
| 更复杂的交互逻辑 | Shiny | 需要服务端参与计算或状态管理时使用 |
crosstalk 的定位值得注意:它做的是客户端联动,筛选和刷选在浏览器内完成,因此不需要 Shiny 后端。如果你的联动逻辑需要重新查询数据库或跑模型,那就该用 Shiny 而不是 crosstalk。
选哪个 widget
官方 showcase 列出了常用组件,gallery 里有 50 多个可选。按任务对应:
- 地理映射:Leaflet
- 时间序列:dygraphs
- 通用交互图形(基于 D3):Plotly
- 表格展示:DataTables
- 网络/图数据:visNetwork、networkD3
- 图表库绑定:Highcharter(Highcharts)、rbokeh(Bokeh)
- 3D 与场景:threejs、rglwidget
- 流程图:DiagrammeR
选择时优先看该 widget 是否仍在维护、是否支持你需要的交互事件回传,而不是只看默认样式。
自己开发 widget
当现有包覆盖不了你需要的 JavaScript 库时,可以按官方 Develop 文档为它写一个 R binding,写好后同样能在控制台、R Markdown 和 Shiny 三处使用。这是框架层面的工作,需要同时理解 R 端的数据传递和 JS 端的渲染约定,不属于日常使用范围。