筆者目前是一個 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
項目示例: