Vesselize — A JavaScript IoC Container that Works Seamlessly with Vue.js and React
I am a software engineer who uses both Vue.js and React. Technologies like Vue Composition API and React Hooks have really saved my life.
On the other hand, due to the very complex data processing tasks in the application I maintain, I wrote a lot of service classes to help me complete these tasks. But I found something inconvenient is that I have to manually manage the initialization of these classes and their dependencies.
I have used server-side frameworks such as Nest and Spring, and technologies like dependency injection have solved this problem well. So I was thinking, is there a way to combine this design pattern with Vue.js and React.
The Vesselize framework is a result of my practice. It is a lightweight IoC container inspired by Nest that can seamlessly integrate with Vue.js and React applications. If you are interested, you can refer to this complete user guide.
The following are the core concepts of Vesselize and how to use it.
Vesselize Core Concepts
Providers
In Vesselize, providers are generally constructors that can be instantiated. It can also be any factory method and declared values. They will all be registered in the container for dependency lookup and injection.
Container
The responsibility of the container is to initialize the instances and resolve their dependencies.
Context
By default, instances are singletons. By specifying a context object, we can also create an instance bound to this context.
Getting Started with Vesselize and Vue.js
Below we use code to demonstrate how to use Vesselize in a Vue.js application.
Installation
yarn add @vesselize/vue
# OR
npm i @vesselize/vue
Create Providers
We have the following three service classes:
UserAPI
is used to get data from the server.UserService
calls UserAPI to get data and process the data.AuthService
is used to determine the role of the user, such as whether it is an administrator.
// file: api/UserAPI.js
class UserAPI {
async fetchUser(id) {
return fetch(`/path/to/user/${id}`).then(res => res.json());
}
}
// file: services/UserService.js
class UserService {
userAPI = null;
async getUser(id) {
const user = await this.userAPI.fetchUser(id);
// data processing stuff...
return user;
}
// Inject userAPI instance through vesselize
setVesselize(vesselize) {
this.userAPI = vesselize.get('UserAPI');
}
}
// file: services/AuthService.js
class AuthService {
constructor(maxAdminUserId) {
this.maxAdminUserId = maxAdminUserId;
}
isAdmin(user) {
return user.id <= this.maxAdminUserId;
}
}
Create Vesselize Plugin
The following code uses createVesselize
to create a Vue.js plugin, which is also a container.
import { createVesselize } from '@vesselize/vue';
import UserAPI from './api/UserAPI';
import UserService from './services/UserService';
import RoleAuthService from './services/RoleAuthService';
const vesselize = createVesselize({
providers: [
{
token: 'UserAPI',
useClass: UserAPI
},
{
token: 'UserService',
useClass: UserService
},
{
token: 'AuthService',
useFactory() {
const maxAdminUserId = 1;
return new AuthService(maxAdminUserId);
}
}
]
});
Use Vesselize Plugin
import { createApp } from 'vue';
import router from './router';
import store from './store';
import vesselize from './vesselize';
import App from './App.vue';
const app = createApp(App)
.use(store)
.use(router)
.use(vesselize);
app.mount('#app');
Acquire Instance in Component
Through the useInstance
Composition API, service instances can be obtained in components.
<template>
<div>Profile</div>
<p>{{ JSON.stringify(user) }}</p>
<p>Role: {{ isAdmin ? 'Administrator' : 'User' }}</p>
</template>
<style scoped></style>
<script>
import { computed, ref, watchEffect } from 'vue';
import { useRoute } from 'vue-router';
import { useInstance } from '@vesselize/vue';
export default {
setup() {
const route = useRoute();
const userId = computed(() => route.params.id);
const user = ref({});
const isAdmin = ref(false);
// Inject instances through Vue Composition API
const userService = useInstance('UserService');
const authService = useInstance('AuthService');
watchEffect(() => {
if (userId.value) {
userService.getUser(userId.value).then((data) => {
user.value = data;
isAdmin.value = authService.isAdmin(data);
});
}
});
return {
user,
isAdmin,
};
},
};
</script>
Finally, if you want to try vesselize with Vue.js directly, here is a complete sample project: vesselize-vue-starter.
Getting Started with Vesselize and React
Let’s take a look at how the same example can be implemented in React.
Installation
yarn add @vesselize/react
# OR
npm i @vesselize/react
Create Providers
The same UserAPI
, UserService
, AuthService
service classes as above.
Combine Providers
Combine all providers into an array.
import UserAPI from './api/UserAPI';
import UserService from './services/UserService';
import RoleAuthService from './services/RoleAuthService';
const providers = [
{
token: 'UserAPI',
useClass: UserAPI
},
{
token: 'UserService',
useClass: UserService
},
{
token: 'AuthService',
useFactory() {
const maxAdminUserId = 1;
return new AuthService(maxAdminUserId);
}
}
];
export default providers;
Add VesselizeProvider
Use VesselizeProvider
to wrap your App
component.
import { VesselizeProvider } from '@vesselize/react';
import providers from './providers';
import UserProfile from './components/UserProfile';
function App() {
return (
<VesselizeProvider providers={providers}>
<UserProfile />
</VesselizeProvider>
);
}
export default App;
Acquire Instance in Component
Through the useInstance
hook, service instances can be obtained in components.
import { useParams } from 'react-router-dom'
import { useState, useEffect } from 'react';
import { useInstance } from '@vesselize/react';
function UserProfile() {
const { id } = useParams();
const [user, setUser] = useState({});
const [isAdmin, setIsAdmin] = useState(false);
// Inject instances through hook
const userService = useInstance('UserService');
const authService = useInstance('AuthService');
useEffect(() => {
userService.getUser(id).then((data) => {
setUser(data);
setIsAdmin(authService.isAdmin(data));
});
}, [id, userService, authService]);
return (
<div>
<span>{JSON.stringify(user)}</span>
<p>Role: {isAdmin ? 'Administrator' : 'User'}</p>
</div>
);
}
export default UserProfile;
Finally, this is the code repository for the sample project created by create-react-app: vesselize-react-starter.
Final Thoughts
I have learned a lot by creating Vesselize, and I hope it can be helpful to you.
Thanks for reading, have a nice day!
Github repository: https://github.com/vesselize
Documentation: https://vesselize.js.org
The following are the core concepts of Vesselize and how to use it.