DEV Community

Uncle Pushui
Uncle Pushui

Posted on

Come on, provide an ioc container for Vue3 that is comparable to Angular

Why provide an ioc container for Vue3

Vue3 is fully capable of developing large-scale business systems due to its excellent responsive system and convenient functional features. However, we must not only be able to do it, but also do it better. The key to large-scale business systems is decoupling, thereby slowing down the growth of messy code. The ioc container is currently the best decoupling tool. Angular introduced the ioc container from the beginning, so it has always been in a leading position in business engineering, and has been waving to other front-end frameworks: "I'm waiting for you in front, and I hope to see you again in three years." So, I will try to take two steps forward, provide the ioc container in Vue3, and introduce other engineering capabilities based on it to get a new framework: Zova. Is it easy to use? I look forward to your trial and feedback

IOC Containers

There are two types of ioc containers in Zova:

  1. global ioc container(referred to as app container): During system initialization, a unique global bean container will be automatically created. Bean instances created in this container are all singleton mode
  2. component instance ioc container(referred to as ctx container): When creating Vue component instances, the system will create a bean container for each of them. Bean instances created in this container can share data and logic within the scope of the component instance

Bean Class

Zova adopts a modular system, and Bean Classes are provided by different modules. When using the Bean Class inside the same module, you can directly locate it based on Class type. When using cross-module, you can locate it based on Bean identifier instead of Class type/file path, which is conducive to achieving loose coupling between modules

Therefore, Zova provides two types of Bean Classes:

  1. Anonymous bean: The class decorated with @Local is an anonymous bean. This type of bean is only used within the module, there is no naming conflict, and it is easy to define and use

  2. Named bean: Except for @Local, the classes decorated by the other decorator functions are named beans. Zova provides a unified naming convention for such beans, which can avoid naming conflicts and facilitate cross-module usage

Injection mechanism

Zova provides the following injection mechanisms:

1. Bean Class

Use Bean Class to lookup and inject bean instance in the ioc container, and automatically create one if not exist. This mechanism is generally used for same module injection

import { ModelTodo } from '../../bean/model.todo.js';

class ControllerTodo {
  @Use()
  $$modelTodo: ModelTodo;
}
Enter fullscreen mode Exit fullscreen mode

2. Bean identifier

Use Bean identifier to lookup and inject bean instance in the ioc container, and automatically create one if not exist. This mechanism is generally used for cross-module injection and hierarchical injection

import type { ModelTabs } from 'zova-module-a-tabs';

class ControllerLayout {
  @Use('a-tabs.model.tabs')
  $$modelTabs: ModelTabs;
}
Enter fullscreen mode Exit fullscreen mode
  • Lookup and inject bean instance through a-tabs.model.tabs
  • Therefore, only the type of ModelTabs needs to be imported to maintain the loose coupling relationship between modules

3. Registration name

Lookup and inject bean instance in the ioc container through the registration name, and return a null value if not exist. This mechanism is generally used for same module injection and hierarchical injection

import type { ModelTodo } from '../../bean/model.todo.js';

class ControllerTodo {
  @Use({ name: '$$modelTodo' })
  $$modelTodo: ModelTodo;
}
Enter fullscreen mode Exit fullscreen mode
  • Lookup and inject the bean instance by the registration name $$modelTodo. Generally speaking, you should ensure that the bean instance has been injected in the ioc container in advance, otherwise a null value will be returned

4. Variable name

Lookup and inject the bean instance in the ioc container by the variable name, and return a null value if not exist. This mechanism is generally used for same module injection and hierarchical injection

import type { ModelTodo } from '../../bean/model.todo.js';

class ControllerTodo {
  @Use()
  $$modelTodo: ModelTodo;
}
Enter fullscreen mode Exit fullscreen mode
  • Lookup and inject the bean instance by the variable name $$modelTodo. Generally speaking, you should ensure that the Bean instance has been injected in the ioc container in advance, otherwise a null value will be returned

Injection scope

The default injection scope of anonymous bean is ctx, and the default injection scope of named bean can be specified when defining it. Different scenes have different default injection scopes. In addition, when injecting, you can also override the default injection scope through the injectionScope option in @Use

Zova provides the following injection scopes: app/ctx/new/host/skipSelf

1. app

If the injection scope is app, then inject the bean instance in the global ioc container to achieve the singleton effect

// in module: test-module1
@Store()
class StoreCounter {}
Enter fullscreen mode Exit fullscreen mode
// in module: test-module2
import type { StoreCounter } from 'zova-module-test-module1';

class Test {
  @Use('test-module1.store.counter')
  $$storeCounter: StoreCounter;
}
Enter fullscreen mode Exit fullscreen mode
  • The injection scope of Store is app by default, so the bean instance will be lookuped and injected in the global ioc container through the bean identifier test-module1.store.counter

2. ctx

If the injection scope is ctx, then inject the bean instance into the ioc container of the current component instance

// in module: a-tabs
@Model()
class ModelTabs {}
Enter fullscreen mode Exit fullscreen mode
// in module: test-module2
import type { ModelTabs } from 'zova-module-a-tabs';

class ControllerLayout {
  @Use('a-tabs.model.tabs')
  $$modelTabs: ModelTabs;
}
Enter fullscreen mode Exit fullscreen mode
  • The injection scope of Model is ctx by default, so the bean instance will be lookuped and injected in the ioc container of the current component instance through the bean identifier a-tabs.model.tabs

3. new

If the injection scope is new, then directly create a new bean instance

// in module: a-tabs
@Model()
class ModelTabs {}
Enter fullscreen mode Exit fullscreen mode
// in module: test-module2
import type { ModelTabs } from 'zova-module-a-tabs';

class ControllerLayout {
  @Use({ beanFullName: 'a-tabs.model.tabs', injectionScope: 'new' })
  $$modelTabs: ModelTabs;
}
Enter fullscreen mode Exit fullscreen mode
  • Since the injectionScope option is specified as new, a new bean instance will be directly created through the bean identifier a-tabs.model.tabs

Hierarchical injection

Injection scope supports not only app/ctx/new, but also host/skipSelf which is called hierarchical injection

4. host

If the injection scope is host, the bean instance will be lookuped in the ioc container of the current component instance and all parent containers in turn. If it does not exist, a null value is returned

// in parent component
import type { ModelTabs } from 'zova-module-a-tabs';

class Parent {
  @Use('a-tabs.model.tabs')
  $$modelTabs: ModelTabs;
}
Enter fullscreen mode Exit fullscreen mode
// in child component
import type { ModelTabs } from 'zova-module-a-tabs';

class Child {
  @Use({ injectionScope: 'host' })
  $$modelTabs: ModelTabs;
}
Enter fullscreen mode Exit fullscreen mode
  • Since the parent component has already injected the ModelTabs bean instance, the child component can directly lookup and inject it
  • Hierarchical injection also supports all injection mechanisms: Bean Class/Bean identifier/Registration name/Variable name

5. skipSelf

If the injection scope is skipSelf, then lookup the bean instance in all parent containers in turn. If it does not exist, a null value is returned

Github: https://github.com/cabloy/zova
Demo: https://zova.js.org/zova-demo/
Docs: https://zova.js.org/guide/essentials/ioc/introduction.html

Top comments (0)