RMarkdown自包含HTML文档中能否仅保留指定外部文件而非全部外置?
你遇到的这个“全有或全无”的困境确实很常见——想要把自己的大体积JSON/JS文件外置来减小主HTML体积,又不想把Plotly、CSS这些依赖也全拆成外部文件,乱糟糟的。好消息是,默认的自包含模式虽然看起来是二选一,但有几个变通技巧能实现“只外置指定文件”的需求,下面给你详细说:
方法1:用htmltools::htmlDependency注册自定义非嵌入依赖
这是最推荐的原生方案,RMarkdown的依赖管理系统允许你标记特定文件为“不嵌入”,即使在self_contained: yes的情况下也会保留为外部文件。
核心思路是把你的自定义JS/JSON包装成一个HTML依赖,明确告诉knitr不要把它嵌入到主文档里。比如针对你的test.json例子,修改R代码块如下:
library(htmltools) library(here) # 注册自定义JSON依赖 custom_json_dep <- htmlDependency( name = "my-plotly-data", # 随便起个唯一名字 version = "1.0.0", # 版本号随便填 src = here(), # 你的JSON文件所在目录(这里用当前工作目录) # 定义脚本标签的属性:src、type、id都能指定 script = list( src = "test.json", type = "application/json", id = "script2" ) ) # 将依赖附加到输出中(用一个空的span占位就行,不会显示内容) attachDependencies(tags$span(), custom_json_dep)
然后在YAML里保持self_contained: yes,生成的HTML里这个test.json会被保留为外部文件,而Plotly、CSS这些依赖依然会嵌入到主文档里,完全符合你的需求。
方法2:用Pandoc的排除参数(需Pandoc 2.10+)
如果你用的Pandoc版本在2.10及以上,可以直接通过pandoc_args给自包含模式添加排除规则,指定哪些文件不要被嵌入。
修改你的RMarkdown的YAML部分:
--- title: "test" author: "nobody" date: "2025-07-16" output: html_document: self_contained: yes pandoc_args: ["--exclude-from-self-contained", "test.js,test.json"] ---
这个参数会告诉Pandoc,在生成自包含HTML时,跳过指定的test.js和test.json,保留它们的外部引用。注意文件路径要相对于你的输出目录,要是文件在子目录里,路径也要对应写对。
方法3:手动后处理HTML文档(兜底方案)
如果上面两个方法都不适用(比如Pandoc版本太低,或者依赖注册有问题),可以先生成全自包含的HTML,再用脚本把base64编码的内容还原成外部文件。
比如用R的xml2和base64enc包来处理你的例子:
library(xml2) library(base64enc) # 读取生成的自包含HTML html_doc <- read_html("test.html") # 找到目标JSON脚本标签 target_script <- xml_find_first(html_doc, "//script[@id='script2']") # 提取并解码base64内容 base64_str <- sub("data:application/json;base64,", "", xml_attr(target_script, "src")) json_content <- rawToChar(base64decode(base64_str)) # 保存为外部JSON文件 writeLines(json_content, "test.json") # 替换脚本标签的src为外部文件路径 xml_set_attr(target_script, "src", "test.json") # 保存修改后的HTML write_xml(html_doc, "test-optimized.html")
这个方法虽然麻烦点,但胜在灵活,不管什么情况都能凑合用。
总结
默认的self_contained确实是“全有或全无”,但通过上面的技巧,你完全可以只把自己的大体积JSON/JS文件外置,其他依赖依然保持自包含。优先推荐方法1,因为是RMarkdown原生支持的,最稳定;方法2如果Pandoc版本够的话也很省心;方法3作为兜底就好。
内容来源于stack exchange

