gpt4 book ai didi

javascript - 更模块化的 D3.js 编码

转载 作者:行者123 更新时间:2023-12-04 10:50:04 25 4
gpt4 key购买 nike

考虑代码片段

let circles = svg.selectAll("circle")
.data(data)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 2);

三行 attr-cx , attr-cy , 和 attr-r使用以下伪代码在内部进行操作:
foreach d in update-selection:
d.cx = (expression)
foreach d in update-selection:
d.cy = (expression)
foreach d in update-selection:
d.r = (constant)

现在假设我们想做不同的事情。我们想改为运行:
foreach d in update-selection:
d.cx = (expression)
d.cy = (expression)
d.r = (constant)

通过写
let circles = svg.selectAll("circle")
.data(data)
.myfunction(d => d);

或者
let circles = svg.selectAll("circle")
.data(data)
.myfunction(d);

我们可能想要这样做,因为:
  • 不管迭代控制有多快,如果我们迭代一次而不是三次,它仍然更快。
  • attr-cx的序列, attr-cy , 和 attr-r不仅仅是三个语句,而是由几十个或数百个语句组成的序列(操纵属性以及其他更改),我们希望将它们隔离到一个单独的 block 中以提高可读性和可测试性。
  • 作为一个练习,以更好地理解在 D3 中编码时可用的选项。

  • 您如何隔离 attr 的三元组?通过单个函数调用声明?

    更新

    Towards Reusable Charts是 Mike Bostock 发表的一篇罕见的文章,它提出了一种通过将大部分代码分离到一个单独的模块中来组织可视化的方法。你知道其余的:模块化促进重用,通过针对 API 进行编程来增强团队合作,支持测试等。其他 D3.js 示例在很大程度上依赖于更适合可丢弃的一次性可视化的单体编程。你知道模块化 D3.js 代码的其他努力吗?

    最佳答案

    TL;DR : 改变链式 attr 没有性能提升一次设置所有属性的单个函数的方法。

    我们可以同意,一个典型的 D3 代码是相当重复的,有时有十几个 attr方法链接。作为一名 D3 程序员,我现在已经习惯了,但我明白很多程序员都将其作为他们对 D3 的主要提示。

    在这个答案中,我不会讨论这是好是坏,丑还是美,好还是不愉快。那将只是一种意见,而且毫无值(value)。在这个答案中,我将重点关注 性能 只要。

    首先,让我们考虑一些假设的解决方案:

  • 使用 d3-selection-multi :这似乎是完美的解决方案,但实际上它没有改变:在其源代码中,d3-selection-multi只需获取传递的对象并调用 selection.attr几次,就像你的第一个片段一样。

    但是,如果性能(您的 #1)不是问题,并且您唯一关心的是可读性和可测试性(如您的 #2),我会选择 d3-selection-multi .
  • 使用 selection.each : 相信大部分D3程序员都会马上想到封装链式attreach方法。但实际上这并没有改变:
    selection.each((d, i, n)=>{
    d3.select(n[i])
    .attr("foo", foo)
    .attr("bar", bar)
    //etc...
    });

    如您所见,链式 attr还在那里。更糟糕的是,我们没有额外的each (attr 在内部使用 selection.each)
  • 使用 selection.call或任何其他替代方案并传递相同的链接 attr选择的方法。

  • 在性能方面,这些都不是足够的选择。所以,让我们尝试另一种提高性能的方法。

    检查 source codeattr我们可以看到,在内部,它使用 Element.setAttributeElement.setAttributeNS .有了这些信息,让我们尝试使用仅循环一次选择的方法重新创建伪代码。为此,我们将使用 selection.each , 像这样:
    selection.each((d, i, n) => {
    n[i].setAttribute("cx", d.x);
    n[i].setAttribute("cy", d.y);
    n[i].setAttribute("r", 2);
    })

    最后,让我们测试一下。对于这个基准,我写了一个非常简单的代码,设置 cx , cyr一些圈子的属性。这是默认方法:

    const data = d3.range(100).map(() => ({
    x: Math.random() * 300,
    y: Math.random() * 150
    }));

    const svg = d3.select("body")
    .append("svg");

    const circles = svg.selectAll(null)
    .data(data)
    .enter()
    .append("circle")
    .attr("cx", d=>d.x)
    .attr("cy", d=>d.y)
    .attr("r", 2)
    .style("fill", "teal");
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>


    这是使用 setAttribute 的方法在一个循环中:

    const data = d3.range(100).map(() => ({
    x: Math.random() * 300,
    y: Math.random() * 150
    }));

    const svg = d3.select("body")
    .append("svg");

    const circles = svg.selectAll(null)
    .data(data)
    .enter()
    .append("circle")
    .each((d, i, n) => {
    n[i].setAttribute("cx", d.x);
    n[i].setAttribute("cy", d.y);
    n[i].setAttribute("r", 2);
    })
    .style("fill", "teal")
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>


    最后,最重要的时刻:让我们对其进行基准测试。我通常使用 jsPerf,但它不适合我,所以我正在使用另一个在线工具。这里是:

    https://measurethat.net/Benchmarks/Show/6750/0/multiple-attributes

    结果令人失望,几乎没有区别:

    enter image description here

    有一些波动,有时一个代码更快,但大多数时候它们是相当的。

    然而,情况变得更糟:正如另一位用户正确指出 their comment ,正确和动态的方法将涉及 再次循环 在你的第二个伪代码中。这会使性能更差:

    enter image description here

    因此,问题在于您的主张(“无论迭代控制多快,如果我们迭代一次而不是三次,它仍然更快”)不一定是正确的。可以这样想:如果您选择了 15 个元素和 4 个属性,那么问题将是“执行 15 个外部循环和每个 4 个内部循环还是执行 4 个外部循环和每个 15 个内部循环更快?”。如您所见,没有什么可以让我们说一个比另一个快。

    结论 : 改变链式 attr 没有性能提升一次设置所有属性的单个函数的方法。

    关于javascript - 更模块化的 D3.js 编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59529046/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com