笔者目前是一个 Vue.js 和 React 都在用的前端打工人,像 Vue Composition API 和 React Hooks 这些技术真的是拯救人生啊,感受前端的开发体验愈来愈像丝绸般顺滑。html
另外一方面呢,因为目前的项目里面有着很是复杂的数据处理逻辑,因此咱们封装了不少可复用的 Service 类。可是有一点不太方便的是,必须在业务组件或者 Service 里面手动建立它们所依赖的 Service 实例。受 Angular、Nest 以及 Spring 这些框架的启发,开始尝试在 Vue.js 和 React 应用中经过依赖注入的方式解决这个问题。前端
Vesselize (文档:vesselize.js.org)就是笔者最近业余时间写的一个 JavaScript IoC 容器,目前已经在项目中正式使用。能够直接在 Vue.js 或 React 应用中,直接经过相似 useInstance('ServiceName')
的 API 来解决对服务实例的依赖。此次造轮子的过程当中学到了很多新知识,分享出来但愿对你们有所帮助。vue
下面的内容是它的基本概念级入门指南。react
Provider 一般是能够被实例化的 JavaScript 构造器函数,此外也能够是任意的工厂方法及已经声明好的值。它们会被注册到容器里面,用于依赖注入及查找。git
Container 的职责是用于初始化实例并解决他们的依赖关系。github
默认状况下,容器建立的实例都是单例的。经过指定一个 Context 对象,咱们能够建立一个跟该上下文绑定的实例。vue-router
下面咱们经过代码的形式展现如何在 Vue.js 应用中集成 Vesselize。npm
yarn add @vesselize/vue # OR npm i @vesselize/vue 复制代码
假设应用中须要如下三个服务:json
UserAPI
用于请求接口数据UserService
调用 UserAPI 获取数据并进行业务逻辑的处理AuthService
用于判断用户的角色,好比是否为管理员// 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); // 数据处理逻辑... return user; } // 经过 Vesselize 容器注入 userAPI 实例 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; } } 复制代码
下面的代码经过 createVesselize
方法建立 Vue.js 插件,同时它也是一个容器实例。api
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); } } ] }); 复制代码
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'); 复制代码
经过 useInstance
这个 Composition API,咱们能够在组件中很方便地获取组件实例。
<template> <div>Profile</div> <p>{{ JSON.stringify(user) }}</p> <p>Role: {{ isAdmin ? 'Administrator' : 'User' }}</p> </template> <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); // 经过 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> 复制代码
最后,若是你想直接在项目中尝试,能够看一下这个示例项目:vesselize-vue-starter.
仍是上面的例子,咱们看一下如何在 React 中实现。
yarn add @vesselize/react # OR npm i @vesselize/react 复制代码
与上面同样也是须要 UserAPI
, UserService
, AuthService
三个服务。
咱们先将全部的 Provider 组合为一个数组:
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; 复制代码
经过 Vesselize 提供的 VesselizeProvider
来包裹项目的 App
组件,同时传入组合好的全部 Provider。
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; 复制代码
经过 useInstance
这个 hook, 能够很是便捷地获取到服务实例。
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); // 经过 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; 复制代码
最后,若是你想直接在项目中尝试,能够看一下这个经过 create-react-app 建立的示例项目: vesselize-react-starter.
在创造 Vesselize 的过程当中,我学习到了不少东西。本文将它分享出来,也但愿对你有所帮助。
感谢你的阅读,祝你生活愉快!
Github 代码仓库: github.com/vesselize
文档及使用指南: vesselize.js.org
项目示例: