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.