Provide/Inject Have Nothing to Do With Dependency Injection

You've probably learned that provide and inject allow you to do dependency injection.

In fact, the official docs tell you this specifically.

But that is completely wrong.

The provide and inject features have nothing to do with dependency injection, and I'll show you why.

But before we can get into that, we have to quickly cover what dependency injection is.

What is dependency injection?

Dependency injection is a concept that comes from the Object Oriented Programming world.

Here is the definition:

A technique where one object supplies the dependencies of another object

The goal of dependency injection is to decouple these objects — to make it easier to change which objects are being used to get stuff done.

The dependencies aren't created by the object itself. Instead they are injected into the object when the program is running. These dependencies are the objects that supply the logic and behaviour of the program.

If you had a Javascript Trip class that needed to get a route between two points, you could hard code the object it needs like this:

class Trip {
constructor(pointA, pointB) {
this.pointA = pointA;
this.pointB = pointB;
this.Router = new Router();
}
calculateRoute() {
return this.Router.route(this.pointA, this.pointB);
}
}

Here the Router class can never change. The Trip class will always use the Router class to calculate the route.

But if you wanted to be able to swap how the route was calculated, for example, between an algorithm for driving and an algorithm for walking, you can't do it like this. You need to use depenendency injection.

To do this, we'll refactor our class so that we pass in the Router object instead:

class Trip {
constructor(pointA, pointB, Router) {
this.pointA = pointA;
this.pointB = pointB;
this.Router = Router;
}
calculateRoute() {
return this.Router.route(this.pointA, this.pointB);
}
}

Now we can pass in whatever kind of Router we want to use.

By using dependency injection like this, our Trip class is no longer tied to a specific Router class. We have the flexibility of changing it whenever and however we want.

That's why we use dependency injection.

There are several other benefits as well:

  • Decreases coupling between objects
  • Allows the application to be more configurable
  • Easier to write unit tests because injected objects can be stubbed/mocked

Now that we've covered what dependency injection is, we can move on to showing how it would be used in Vue.

Dependency injection in Vue

In Vue, we're using components, not objects, so true dependency injection would look a little different.

Our definition here would be:

A technique where one component supplies the dependencies of another component

Again, these dependencies are the logic and behaviour of the program, not the data or state that's being processed.

Our main problem is that provide/inject cannot be used for dependency injection in this way for a few reasons:

  • Increases coupling — The goal of dependency injection is to decrease coupling between components, but using provide/inject to pass data actually increases the coupling between components.
  • Acts on a subtree — Dependency injection acts on one component at a time, so you can have fine-grained control over each object. Providing a value through provide makes it available to the whole subtree.
  • Used for passing data — Typically, the reason that provide/inject is used is so that we can pass data deep into our component tree more easily. Dependency injection has nothing to do with the data of an application.

Real dependency injection in Vue would either:

Conclusion

If provide and inject are not meant to be used for dependency injection, then what are they supposed to be used for?

I have lots of thoughts on this as well, but you'll have to wait for my post on that.