Now that we have a good understanding of the limitations of Vue's current DI system, we can take a look at alternatives and possible improvements.
One thing that we can do to improve the code is to leverage a third-party dependency injection library. In this section, we'll be using InversifyJS (http://inversify.io), a lightweight DI library written in TypeScript that acts as an IoC container.
InversifyJS supports setter injection and using symbols, just like Vue does, but it is much more powerful as it also supports the following:
- Controller injection
- Optional dependencies
- Default values
- Injecting factories
- Middleware
- Tagged and named bindings
- Circular dependencies
- Testing
By using InversifyJS, we will be able to configure all of our injectable elements, not just the Vue-related ones. Moreover, we'll become more versatile, given how powerful InversifyJS is compared to Vue's built-in DI support.
To integrate InversifyJS with Vue, we'll leverage symbols, provide and inject, and a design pattern called Service Locator (SL).
The SL pattern is an alternative way of reducing coupling between application components. With this design pattern, an object can use a service locator to create/retrieve the things it needs, without necessarily having to know too much about how those are constructed.
The SL pattern is not as great as pure DI/IoC, as we have seen with Angular, because it requires our code to be aware of the service locator. With pure DI/IoC, our code would receive its dependencies and would not need to have any knowledge about where those are coming from or how they're constructed.
The reason why using this pattern makes sense here is because Vue does not support constructor injection. Because of this, and in order to avoid abusing definite assignment assertions, we'll simply inject the service locator into our components. Once our components get hold of the service locator, they'll be able to use it to get instances of their dependencies, limiting the knowledge they require.