You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+171Lines changed: 171 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -214,3 +214,174 @@ class Nav {
214
214
}
215
215
}
216
216
```
217
+
218
+
# Dependency Injection
219
+
220
+
Mocking dependencies and imports in tests might be really tedious. Using [inject-loader](https://github.com/plasticine/inject-loader) can help a lot but requires lots of ugly coding. Another option is to involve dependency injection, such as [inversify](https://github.com/inversify/InversifyJS). They are primarily focused on Typescript but they support [vanilla JS](https://github.com/inversify/inversify-vanillajs-helpers) too. In scenarios where you don't have control over instantiating components and services, there is another very handy [toolbox](https://github.com/inversify/inversify-inject-decorators) which gives you a set of decorators usable in Vue components.
221
+
222
+
## Setting up DI in VUE
223
+
224
+
We need to create a container instance for holding all registered injections. We need only one instance per application:
225
+
```js
226
+
import { Container } from'inversify';
227
+
let container =newContainer();
228
+
exportdefaultcontainer;
229
+
```
230
+
and then just execute it in the bootstrap phase of the application:
231
+
```js
232
+
import'./di';
233
+
```
234
+
Let's create a service:
235
+
```js
236
+
exportdefaultclassAuthService {
237
+
login (username, password) {
238
+
// do something
239
+
}
240
+
}
241
+
```
242
+
... register it in `di.js`:
243
+
```js
244
+
import { Container } from'inversify';
245
+
importAuthServicefrom'services/auth';
246
+
let container =newContainer();
247
+
container.bind('authService').to(AuthService);
248
+
exportdefaultcontainer;
249
+
```
250
+
... and then use it in VUE component:
251
+
```js
252
+
importcontainerfrom'./di';
253
+
254
+
classLoginViewextendsVue {
255
+
constructor() {
256
+
this.authService=container.get('authService');
257
+
}
258
+
login (username, password) {
259
+
returnthis.authService.login(username, password);
260
+
}
261
+
}
262
+
```
263
+
There are a couple of issues with this approach:
264
+
1. If we register all our services in `di.js`, then we nullified code splitting because everything is required during the application bootstrap. To solve this issue, let's register service only when is required for the first time:
265
+
```js
266
+
importcontainerfrom'./di';
267
+
268
+
exportdefaultclassAuthService {
269
+
login (username, password) {
270
+
// do something
271
+
}
272
+
}
273
+
274
+
container.bind('authService').to(AuthService);
275
+
```
276
+
2. We have lots of [magic strings](http://deviq.com/magic-strings/) everywhere (e.g. what we would do if `authService` changes the name? How we prevent naming collisions). Well, ES6 introduced [symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) that we can use. Don't forget to export the Symbol:
277
+
```js
278
+
importcontainerfrom'./di';
279
+
280
+
exportconstAUTH_SERVICE_ID=Symbol('authService');
281
+
282
+
exportdefaultclassAuthService {
283
+
login (username, password) {
284
+
// do something
285
+
}
286
+
}
287
+
288
+
container.bind(AUTH_SERVICE_ID).to(AuthService);
289
+
```
290
+
... and use it in VUE component:
291
+
```js
292
+
importcontainerfrom'./di';
293
+
import { AUTH_SERVICE_ID } from'./services/auth';
294
+
295
+
classLoginViewextendsVue {
296
+
constructor() {
297
+
this.authService=container.get(AUTH_SERVICE_ID);
298
+
}
299
+
login (username, password) {
300
+
returnthis.authService.login(username, password);
301
+
}
302
+
}
303
+
```
304
+
305
+
## Using decorators
306
+
Decorators can help us to eliminate lots of code repetition and make our code cleaner.
307
+
308
+
### @Register decorator
309
+
`inversify-vanillajs-helpers` can create Register helper which can be used as a decorator anywhere in our code. Let's add this code to `di.js`:
export { registerasRegister } // we are exporting decorator with capital R because other decorators we are already using (e.g. for Vuex) also have a capital letter
317
+
```
318
+
319
+
... and then use it:
320
+
```js
321
+
import { Register } from'@di';
322
+
323
+
exportconstAUTH_SERVICE_ID=Symbol('authService');
324
+
325
+
@Register(AUTH_SERVICE_ID)
326
+
exportdefaultclassAuthService {
327
+
login (username, password) {
328
+
// do something
329
+
}
330
+
}
331
+
332
+
```
333
+
334
+
If your service depends on other services, you can pass IDs as a second parameters:
Check out documentation for [inversify-vanillajs-helpers](https://github.com/inversify/inversify-vanillajs-helpers#usage) to see all possibilities
354
+
355
+
### @LazyInject decorator
356
+
We can improve injection in our VUE components too by using LazyInject decorators from [inversify-inject-decorators](https://github.com/inversify/inversify-inject-decorators). Let's create it and export it in `di.js` first:
`LazyInject` caches the instance of `authService` until the component is destroyed. Check out the documentation for [inversify-inject-decorators](https://github.com/inversify/inversify-inject-decorators#basic-property-lazy-injection-with-lazyinject) to see more options and other decorators.
0 commit comments