从JSON加载Quarto HTML地图数据,用于在R中生成的Leaflet地图。

7
我创建了一个包含许多在R中生成的leaflet地图的Quarto博客文章。由于每个地图的数据都嵌入在html文件中,所以文件本身非常大。这导致托管文件的服务器出现问题。
我想要减小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地图?
1个回答

6
不可避免地,在发布悬赏两天后,我找到了一个解决方案。这种方法将我的真实HTML文件从22.5MB减小到165KB。具体步骤如下:
  1. 从html的中删除所有

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接