我创建了一个包含许多在R中生成的leaflet地图的Quarto博客文章。由于每个地图的数据都嵌入在html文件中,所以文件本身非常大。这导致托管文件的服务器出现问题。
我想要减小html文件的大小。Quarto中的"embed-resources: false" YAML选项意味着库(例如leaflet.js)存储在单独的文件中。这有所帮助,但数据仍然存储在html中(每个地图一次)。我正在尝试从单独的文件中加载数据本身。这是一个最简示例的qmd文件:
当我使用
然而,我现在知道这是不可能的。相反,我尝试添加一个脚本将json注入到所需元素的innerHTML中(使用Live Server进行测试):
这个方法的作用是将精确的JSON内容加载到标签中,就像在HTML文件中硬编码一样(需要使用
然而,仅有这个方法并不能显示地图,控制台会报错:
即在加载的脚本寻找数据的时候,
考虑到这一点,我将
如果我将这些从标签移到和标签之间,地图渲染的时间会减少一半。所以看起来加载它们和将json注入到标签的脚本之间存在某种竞争关系。
为了确保加载的顺序正确,我从html的标签中移除了脚本,并使用this async loadScript()函数动态加载脚本,以确保它们只在数据加载之后加载。
现在的脚本只在将json注入到
我是否忽略了一些关于静态Quarto生成的网页上事件发生顺序的问题,以及
是否有一种方法可以让Quarto html文件从json文件中加载数据,并渲染由R生成的
我想要减小html文件的大小。Quarto中的"embed-resources: false" YAML选项意味着库(例如leaflet.js)存储在单独的文件中。这有所帮助,但数据仍然存储在html中(每个地图一次)。我正在尝试从单独的文件中加载数据本身。这是一个最简示例的qmd文件:
---
format:
html:
embed-resources: false
---
```{r}
leaflet::leaflet(elementId = "map1") |>
leaflet::addTiles() |>
leaflet::addMarkers(lng = 174.768, lat = -36.852, popup = "The birthplace of R")
```
当我使用
quarto render
时,它会创建一个HTML文件,在浏览器中打开后显示地图。该文件包含以下<div>
中的地图数据:<div class="leaflet html-widget html-fill-item-overflow-hidden html-fill-item" id="map1" style="width:100%;height:464px;"></div>
<script type="application/json" data-for="map1">{**json**}</script>
</div>
在我写的地方 {**json**}
,有一长串的json,包含地图坐标、CRS和各种选项。
我觉得我可以将json内容复制到一个文件中,然后更改 <script>
标签,从该文件加载数据:
<script src="page_data/map1.json" type="application/json" data-for="map1"></script>
然而,我现在知道这是不可能的。相反,我尝试添加一个脚本将json注入到所需元素的innerHTML中(使用Live Server进行测试):
<script>
fetch('./page_data/map1.json')
.then((response) => response.json())
.then((json) => (
document.querySelectorAll('[data-for="map1"]')[0].innerHTML =
JSON.stringify(json).replaceAll("\/", "/"))
);
</script>
这个方法的作用是将精确的JSON内容加载到标签中,就像在HTML文件中硬编码一样(需要使用
replaceAll()
使其与之前添加反斜杠之前的内容完全相同,因为添加了一些转义字符)。然而,仅有这个方法并不能显示地图,控制台会报错:
Uncaught SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at htmlwidgets.js:646:27
htmlwidgets.js
的相关行是:
var scriptData = document.querySelector("script[data-for='" + el.id + "'][type='application/json']");
var data = JSON.parse(scriptData.textContent || scriptData.text);
即在加载的脚本寻找数据的时候,
fetch()
请求尚未更新<script data-for="map1"></script>
标签的innerHTML
,因此没有可解析的内容。考虑到这一点,我将
fetch()
请求以及htmlwidgets.js
和其他<script>
标签移动,以尝试延迟它们的加载。目前,在<head>
中有大约10行标签,例如:<script src="page_files/libs/htmlwidgets-1.6.2/htmlwidgets.js"></script>
<script src="page_files/libs/jquery-1.12.4/jquery.min.js"></script>
如果我将这些从标签移到和标签之间,地图渲染的时间会减少一半。所以看起来加载它们和将json注入到标签的脚本之间存在某种竞争关系。
为了确保加载的顺序正确,我从html的标签中移除了脚本,并使用this async loadScript()函数动态加载脚本,以确保它们只在数据加载之后加载。
fetch('./map1.json')
.then((response) => response.json())
.then((json) => (
document.querySelectorAll('[data-for="map1"]')[0].innerHTML = JSON.stringify(json).replaceAll("\/", "/")))
.then(() =>
loadScript("page_files/libs/htmlwidgets-1.6.2/htmlwidgets.js")
).then(() =>
loadScript("page_files/libs/jquery-1.12.4/jquery.min.js"));
/*etc - for all scripts on the page in the order they appear in the html*/
现在的脚本只在将json注入到
<script data-for="map1"></script>
标签后加载。然而,它根本不渲染地图,而且html小部件也没有注册(即控制台中的document.getElementById("map1").htmlwidget_data_init_result
返回undefined
)。我是否忽略了一些关于静态Quarto生成的网页上事件发生顺序的问题,以及
htmlwidgets
的方式?是否有一种方法可以让Quarto html文件从json文件中加载数据,并渲染由R生成的
leaflet
地图?