There are many articles about using Vue and D3 together, but they usually tell you to write the SVG in Vue instead of using D3's SVG functions:
- Composing D3 Visualizations with Vue.js[1] — uses Vue for the SVG, Vue watch for reactivity, D3 for the geometry
- 5 simple rules to data visualization with Vue.js and D3.js[2] — uses Vue for the SVG, Vue computed for reactivity
- Dynamic Data Visualizations With Vue.js and D3[3] - uses Vue for the SVG, D3 for transitions
- D3.js and Vue.JS[4] — uses Vue for the SVG, D3 for calculations
- Headless D3[5] — lists which parts of D3 do pure calculations (as opposed to SVG)
However, you can use D3 for the SVG, and Vue for the reactivity. I'm not using watch
or template ref
. It's based on my Vue + Canvas code[6], where I use computed
for reactivity. As of 2018 I haven't seen this technique described anywhere.
The code is below (also here). The main idea is that there's a reusable Vue component that produces a <g> element. You pass a function to that component to perform the one-time setup, and then return a function that should be called on update.
Look at the draw(parent)
. It's d3 code. It doesn't do any Vue-specific things. In theory, this will make it easier for you to reuse this code in another project that doesn't use Vue.
Motivation: I try to avoid watchers. There are two errors I make:
- (correctness) As I change the code, I might introduce a dependency that I forgot to watch. I change that value but the diagram doesn't update, even though it should.
- (performance) As I change the code, I might no longer have a dependency that I have been watching. I change that value and the diagram updates, even though it shouldn't.
By using Vue's automatic dependency tracking, these dependencies are always accurate, and I avoid both of these issues.
Caveats: the code assumes that the element is created only once. This is probably fine for most cases, but you can't turn it on and off with v-if, or add a variable number of them with v-for, etc. Maybe :key
would help; I'm not sure.
TODO: I need to write a better example. Above, one function does all the drawing. But the same technique should work when you have different parts of the visualization, each depending on some subset of data, and then only those parts will redraw. For example, if I have a line that depends on x,y and a horizontal axis that depends on x and a vertical axis that depends on y, then when I change x, it will redraw the line and horizontal axis but not the vertical axis. Each would be a separate <vue-d3>
component with its own draw function passed in.
I haven't used this in a real project and there may be more caveats.