快速理解D3js 数据绑定之 enter 与 exit 与 update

D3.js is a JavaScript library for manipulating documents based on data.

就像我之前文章提到的,D3js 给自己的定位并不是图表,如官网所言,他是数据驱动dom。能理解这一点,就能将之灵活运用到各自场景。比如,给普通table的<td/>加上数据背景色变成数据透视表;给文本font-size绑定数据,变成简易词云;或者你就是要画一些数据指标,等等。在这些操作中,首先要用到的就是将dom与数据关联起来,并对dom进行增删改。那么enter 与 exit 两个函数就是起到这个作用。(如果在react或者vue中,你可以理解为dom的diff,只是在d3中我们是显式地直接操作dom)


enter 与 exit 与 update

code depend d3 version: v5 github.com/d3/d3-selec…

首先我们先理解一下概念:假设集合 collectionA,集合 collectionB,判断二者之间是不是有交集 equalBy 。

这张图,初学d3的同学都见过。可能解释的比较少的是中间的equalBy部分。collectionA 即指上一次绘制所棒定的数据,如果上次未绑定数据即[undefine, undefine ....],如果没有图形就是空数组[]。collectionB 是我们要刷新视图的新数据集合。
现在我们来看代码,这是官网的demo如下:

const circle = svg.selectAll("circle").data(data) // UPDATE
    .style("fill", "blue");

circle.exit().remove(); // EXIT

circle = circle.enter().append("circle") // ENTER
    .style("fill", "green")
    .merge(circle) // ENTER + UPDATE
    .style("stroke", "black");
复制代码

其实这里有个默认选项 即上图提到的equalBy。 上面的代码我们把默认的equalBy补齐如下:

const equalBy = (d, i) => i; // 根据索引判断元素是否为同一个元素
const circle = svg.selectAll("circle").data(data, equalBy) // UPDATE
    .style("fill", "blue");

circle.exit().remove(); // EXIT

circle = circle.enter().append("circle") // ENTER
    .style("fill", "green")
    .merge(circle) // ENTER + UPDATE
    .style("stroke", "black");
复制代码

所以,在没有指定equalBy的时候,是根据索引判断元素是否为同一个元素。

const collectionA = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 3, text: 3 }];
const collectionB = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 4, text: 4 }, { id: 5, text: 5 }];
复制代码

_但我们正常更新数据时候,equal(collectionA[2],collectionB[2]) == false ,但你未设置equalBy的时候,即默认index为标记,这里就认为是同一个元素 ,equal(collectionA[2],collectionB[2]) == true(就好像一个程序员,10岁的和30岁的他,他的身份证号没有变,只是头发可能因为写代码剩的不太多,他的特征属性发生了变化),所以它属于update部分。
机智的你肯定可以想到,那么如果我给数据集每个对象一个身份证。

const equalBy = obj => obj.id;
const circle = svg.selectAll("circle").data(data, equalBy) // UPDATE
    .style("fill", "blue");
复制代码

这时候对比属于enter exit 还是update 则是根据 obj id是不是还是旧的那个。(也就是,判断昨天的你和今天的你是否是一个人,是跟据你的身份证id来判断)。
我们来个实践:

// 伪代码
const equalBy = (d, i) => i; // 为设置,即d3默认规则 selection.data(data)
const collectionA = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 3, text: 3 }];
const collectionB = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 4, text: 4 }, { id: 5, text: 5 }];
// enter() = [{ id: 5, text: 5 }]
// exit() = []
// update =  [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 4, text: 4 }]

const equalBy = obj => obj.id; // 自定义设置规则 selection.data(data, equalBy)
const collectionA = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 3, text: 3 }];
const collectionB = [{ id: 1, text: 1 }, { id: 2, text: 2 }, { id: 4, text: 4 }, { id: 5, text: 5 }];
// enter() = [{ id: 4, text: 4 }, { id: 5, text: 5 }]
// exit() divsext: 3 }]
// update = [{ id: 1, text: 1 }, { id: 2, text: 2 x}]
复制代码

结合react组件中如何使用

enter exit update 其实就是对dom元素的增删该,这很容易让我们联想到react或者vue。这里我以react为例来说说。
首先,equalBy 相当于react组件key (reactjs.org/docs/lists-… 即标识这个组件的身份id。通常对于exit我们会做删除操作,enter与update 做render component操作(假设我们的需求仅管理dom,复杂操作本文暂不提,后续独立篇幅)。

那么 exit 在react中我们并不需要做什么,数据不存在,那么自然就被销毁。上诉的两种写法相当于如下代码:


class Demo extens React.Component {
    render() {
        const { data } =this.props;
        return (<div>
          {data.map((obj,i) => <div ref={c => (c.__data__ = obj)} key={i}>obj.text</div>)}
        </div>)
    }
}

class Demo extens React.Component {
    render() {
        const { data } =this.props;
        return (<div>
          {data.map((obj,i) => <div ref={c => (c.__data__ = obj)} key={obj.id}>obj.text</div>)}
        </div>)
    }
}

复制代码

细心的你可能发现了一行代码ref={c => (c.__data__ = obj)} 我们用react组件创建的元素,如果后续需要使用d3-selection 继续做一系列操作,比如可以在componensDidUpdate后执行d3-transition动画等等,我们要做的就是将数据关联到dom中去。 实际上,d3js 就是通过dom的propties __data__ 来关联数据的(注:dom的propties与attribute 的区别)。所以我们可以在ref 中获取dom实例,并赋值挂载。

附录

d3js: d3js.org/
demo工具:beta.observablehq.com/
demo地址: beta.observablehq.com/@leannechn/…
react keys: reactjs.org/docs/lists-…

转载于:https://juejin.im/post/5bffdccf5188252f170e1c78

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值