iocraft
iocraft is a dependency injection container for Vue 3 built on the Composition API.
Table of Contents
- Installation
- TypeScript Configuration
- Setup
- Services
- Obtaining Services
- Context (Scoped) Services
- Router Integration
Installation
npm install iocraft
TypeScript Configuration
iocraft uses decorators, so you must enable experimental decorators in your TypeScript config.
Update your tsconfig.app.json (typical Vue / Vite setup):
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"noUncheckedIndexedAccess": true,
"paths": {
"@/*": ["./src/*"]
},
"experimentalDecorators": true, // Add this
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
}
}
Setup
import { createApp } from "vue";
import { iocraft } from "iocraft";
import App from "./App.vue";
const app = createApp(App);
app.use(iocraft);
app.mount("#app");
With options:
app.use(iocraft, {
router, // enables the built-in Nav service
eagerLoad: [AuthService], // Eagerly initiate services
});
Services
Decorate a class with @attach() to register it with the DI system.
import { ref, computed } from "vue";
import { attach } from "iocraft";
@attach()
export class CounterService {
count = ref(0);
double = computed(() => this.count.value * 2);
increment() {
this.count.value++;
}
reset() {
this.count.value = 0;
}
}
@attach() assigns a unique symbol token to the class that the registry uses for lookup. Without it, iocraft cannot resolve the service and will throw.
Obtaining Services
obtain
obtain Resolves a singleton from the registry. Initiates the class once on first call, and reuses that instance for the lifetime of the app. It returns a facade of that class instance, which is a thin wrapper that proxies each property through getters/setters linked to the original instance, preserving reactivity even when destructuring.
<script setup lang="ts">
import { obtain } from "iocraft";
import { CounterService } from "./services";
const counter = obtain(CounterService);
const { count, increment, reset } = obtain(CounterService);
</script>
<template>
<p>Count: {{ count }} — Double: {{ counter.double }}</p>
<button @click="increment">+1</button>
<button @click="reset">Reset</button>
</template>
obtain.raw
obtain.raw Resolves a singleton from the root registry, just like obtain, but returns the actual class instance with no facade applied.
const auth = obtain.raw(AuthService);
obtain.instance
obtain.instance Returns a fresh facade of the given service on each call.
<script setup lang="ts">
import { obtain } from "iocraft";
import { FormService } from "./services";
const { values, errors, submit, reset } = obtain.instance(FormService);
</script>
<template>
<form @submit.prevent="submit">
<input v-model="values.email" />
<span v-if="errors.email">{{ errors.email }}</span>
<button type="submit">Submit</button>
</form>
</template>
Context (Scoped) Services
Provide a service from a parent component and inject it into any descendant. Uses Vue's provide / inject under the hood.
Parent:
<script setup lang="ts">
import { expose, obtain } from "iocraft";
import { CartService } from "./services";
const cartService = obtain.instance(CartService);
expose(cartService);
</script>
Child:
<script setup lang="ts">
import { obtain } from "iocraft";
import { CartService } from "./services";
const cart = obtain.exposed(CartService); // CartService | undefined
</script>
Router Integration
iocraft provides a built-in Nav service that exposes all routing features as a plain injectable service — no need to call useRouter() and useRoute() separately. To enable it, pass your router when registering the plugin:
app.use(iocraft, { router });
Once registered, Nav is available as a global singleton anywhere in your app:
<script setup lang="ts">
import { obtain, Nav } from "iocraft";
const nav = obtain(Nav);
</script>
<template>
<p>
Path: <code>{{ nav.path }}</code>
</p>
<p>
Name: <code>{{ nav.name?.toString() ?? "-" }}</code>
</p>
<p>
Full path: <code>{{ nav.fullPath }}</code>
</p>
<button @click="nav.push('/home')">Home</button>
<button @click="nav.back()">Back</button>
<button @click="nav.forward()">Forward</button>
</template>
Nav API
Reactive route properties: path · name · params · query · hash · fullPath · meta · matched · redirectedFrom · currentRoute
Router state: options · listening (readable and writable)
Navigation: push(to) · replace(to) · go(delta) · back() · forward()
Route resolution: resolve(to)
Route registry: addRoute() · removeRoute() · getRoutes() · hasRoute() · clearRoutes()
Guards: beforeEach() · beforeResolve() · afterEach() · onError()
Lifecycle: isReady()
License
MIT · istiuak-0
Related Packages
IoC and DI for Vue powered by InversifyJS and inspired by Angular @Module syntac...
Vue bindings for @nonnajs/di - a component plus useInjection()/useOptionalInject...
Class-based Vue 3 components with constructor dependency injection powered by ts...
Class-based Pinia stores with constructor dependency injection powered by tsyrin...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.4.0 | ^3.0.0 | MIT | |
| 0.3.0 | ^3.0.0 | MIT | |
| 0.2.11 | ^3.0.0 | MIT | |
| 0.2.10 | ^3.0.0 | MIT | |
| 0.2.9 | ^3.0.0 | MIT | |
| 0.2.8 | ^3.0.0 | MIT | |
| 0.2.7 | ^3.0.0 | MIT | |
| 0.2.6 | ^3.0.0 | MIT | |
| 0.2.5 | ^3.0.0 | MIT | |
| 0.2.4 | ^3.0.0 | MIT | |
| 0.2.3 | ^3.0.0 | MIT | |
| 0.2.1 | ^3.0.0 | MIT | |
| 0.2.0 | ^3.0.0 | MIT |