Skip to content

Commit 5e27879

Browse files
feat: support Vue3
1 parent 1d57a12 commit 5e27879

34 files changed

+1448
-664
lines changed

README.md

Lines changed: 168 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# FlexDI
22

3-
A flexible, efficient, and lightweight dependency injection library for React applications. Future versions plan to add Vue support. The current version only implements the React part.
3+
A flexible, efficient, and lightweight dependency injection library for <b>React/Vue3</b> applications.
44

55
The library is inspired by the principles and architectural approach of NestJS and Angular, but adapted for frontend applications.
66

@@ -17,19 +17,14 @@ npm install flexdi reflect-metadata
1717
yarn add flexdi reflect-metadata
1818
```
1919

20-
### React Installation
20+
### React/Vue3 useObservable under hood use RxJs.
21+
#### Additional installation
2122
```bash
22-
npm install flexdi reflect-metadata react
23+
npm install rxjs
2324
# or
24-
yarn add flexdi reflect-metadata react
25+
yarn add rxjs
2526
```
2627

27-
### React (useObservable hook) Installation
28-
```bash
29-
npm install flexdi reflect-metadata react rxjs
30-
# or
31-
yarn add flexdi reflect-metadata react rxjs
32-
```
3328
## Project configuration
3429
### Reflect Metadata
3530
Add the reflect-metadata import at your application's entry point (before using any decorators):
@@ -51,6 +46,15 @@ Make sure your tsconfig.json includes:
5146
}
5247
```
5348

49+
### Vue3 setup:
50+
```vue
51+
import { flexdiPlugin } from 'flexdi/vue3'
52+
53+
createApp(App)
54+
.use(flexdiPlugin) // add flexdi decorators
55+
.mount('#app')
56+
```
57+
5458
## Core Concepts
5559

5660
FlexDI is built on the following concepts:
@@ -62,10 +66,12 @@ FlexDI is built on the following concepts:
6266

6367
## Working Example
6468

65-
A full-featured React project built using FlexDI can be found in the repository:
69+
#### React A full-featured React project built using FlexDI can be found in the repository:
6670
[https://github.com/AndreyShashlovDev/scalpel-frontend](https://github.com/AndreyShashlovDev/scalpel-frontend/tree/master)
6771

68-
## Using in a React Application
72+
#### Vue3 Sample project
73+
[https://github.com/AndreyShashlovDev/flexdi-vue3](https://github.com/AndreyShashlovDev/flexdi-vue3/tree/master)
74+
## Usage
6975

7076
### Defining a Module
7177

@@ -104,6 +110,16 @@ export class UserServiceImpl {
104110
}
105111
```
106112

113+
### Different modules for React/Vue3
114+
```typescript
115+
// react project
116+
import { RootModuleLoader } from 'flexdi/react'
117+
118+
// vue3 project
119+
import { RootModuleLoader } from 'flexdi/vue3'
120+
```
121+
122+
## React
107123
### Setting Up the Root Module
108124

109125
Applications should always start with a root module:
@@ -127,6 +143,31 @@ root.render(
127143
)
128144
```
129145

146+
## Vue3
147+
### Setting Up the Root Module
148+
149+
Applications should always start with a root module:
150+
```vue
151+
<script setup lang="ts">
152+
import { RootModuleLoader } from 'flexdi/vue3'
153+
import ErrorComponent from './common/app-ui/ErrorComponent.vue'
154+
import LoadingComponent from './common/app-ui/LoadingComponent.vue'
155+
import { RootModule } from './RootModule.ts'
156+
157+
const rootModule = RootModule
158+
</script>
159+
160+
<template>
161+
<RootModuleLoader
162+
:module="rootModule"
163+
:loading-component="LoadingComponent"
164+
:error-component="ErrorComponent"
165+
>
166+
<router-view></router-view>
167+
</RootModuleLoader>
168+
</template>
169+
170+
```
130171
### Using Dependencies in React Components
131172

132173
```tsx
@@ -155,6 +196,45 @@ export const UserList = () => {
155196
}
156197
```
157198

199+
### Using Dependencies in Vue3 Components
200+
201+
```vue
202+
<script setup lang="ts">
203+
import { useInject, useObservable, usePresenter } from 'flexdi/vue3'
204+
import { ServiceA } from '../../../common/service/ServiceA.ts'
205+
import { HomePagePresenter } from '../domain/HomePagePresenter.ts'
206+
207+
const presenter = usePresenter(HomePagePresenter, {userId: 123})
208+
const user = useObservable(presenter.getUser(), null)
209+
const error = useObservable(presenter.error(), null)
210+
const isLoading = useObservable(presenter.isLoading(), true)
211+
212+
const serviceA = useInject(ServiceA)
213+
214+
function loadUser() {
215+
presenter.onUserLoadClick()
216+
}
217+
218+
function callServiceA() {
219+
serviceA.doSomething()
220+
}
221+
</script>
222+
223+
<template>
224+
<div>
225+
<h1>User home page</h1>
226+
<div v-if="isLoading">Loading...</div>
227+
<div v-else-if="error">Error is: {{ error }}</div>
228+
<div v-else-if="user">
229+
<h2>{{ user.name }}</h2>
230+
<p>Email: {{ user.email }}</p>
231+
<button @click="loadUser()">Load user 123</button>
232+
<button @click="callServiceA()">Call service A</button>
233+
</div>
234+
</div>
235+
</template>
236+
```
237+
158238
## Detailed Guide
159239

160240
### Decorators
@@ -381,7 +461,7 @@ FlexDI supports asynchronous initialization and the use of Promises:
381461
export class ConfigModule {}
382462
```
383463

384-
## React Integration
464+
## Integration
385465

386466
### ErrorBoundaryView Component
387467
```tsx
@@ -418,7 +498,7 @@ export class ErrorBoundaryView extends Component<ErrorBoundaryProps, ErrorBounda
418498
}
419499
```
420500

421-
### RootModuleLoader
501+
### RootModuleLoader React
422502

423503
Component for loading the root module of the application:
424504

@@ -436,7 +516,32 @@ Component for loading the root module of the application:
436516

437517
The `enableStrictMode` parameter should be set to `true` ONLY when `<StrictMode>` is used in the application and you are in development mode. Otherwise, be sure to set it to `false`, otherwise the presenters will not receive ready/destroy events and will not work correctly.
438518

439-
### ModuleLoader
519+
520+
### RootModuleLoader Vue3
521+
522+
Component for loading the root module of the application:
523+
```vue
524+
<script setup lang="ts">
525+
import { RootModuleLoader } from 'flexdi/vue3'
526+
import ErrorComponent from './common/app-ui/ErrorComponent.vue'
527+
import LoadingComponent from './common/app-ui/LoadingComponent.vue'
528+
import { RootModule } from './RootModule.ts'
529+
530+
const rootModule = RootModule
531+
</script>
532+
533+
<template>
534+
<RootModuleLoader
535+
:module="rootModule"
536+
:loading-component="LoadingComponent"
537+
:error-component="ErrorComponent"
538+
>
539+
<router-view></router-view>
540+
</RootModuleLoader>
541+
</template>
542+
```
543+
544+
### ModuleLoader React
440545

441546
Component for loading a module and its dependencies:
442547

@@ -451,6 +556,30 @@ Component for loading a module and its dependencies:
451556
</ModuleLoader>
452557
```
453558

559+
### ModuleLoader Vue3
560+
561+
Component for loading the root module of the application:
562+
```vue
563+
<script setup lang="ts">
564+
import { ModuleLoader } from 'flexdi/vue3'
565+
import ErrorComponent from './common/app-ui/ErrorComponent.vue'
566+
import LoadingComponent from './common/app-ui/LoadingComponent.vue'
567+
import { FeatureModule } from './FeatureModule.ts'
568+
569+
const featureModule = FeatureModule
570+
</script>
571+
572+
<template>
573+
<ModuleLoader
574+
:module="featureModule"
575+
:loading-component="LoadingComponent"
576+
:error-component="ErrorComponent"
577+
>
578+
<router-view></router-view>
579+
</ModuleLoader>
580+
</template>
581+
```
582+
454583
### useInject
455584

456585
Hook for injecting dependencies into functional components:
@@ -476,7 +605,7 @@ Hook for subscribing to Observable with automatic unsubscription:
476605
const users = useObservable(presenter.getUsers(), [])
477606
```
478607

479-
### createModuleRoute
608+
### createModuleRoute React
480609

481610
Function for creating a React Router route with module support and lazy loading of components:
482611

@@ -537,6 +666,29 @@ const appRoutes = [
537666
const router = createBrowserRouter(appRoutes)
538667
```
539668

669+
### createModuleRoute Vue3
670+
671+
Function for creating a Vue Router route with module support and lazy loading of components:
672+
673+
```typescript
674+
import { createModuleRoute } from 'flexdi/vue3'
675+
import { HomePageModule } from '../../feature/home/di/HomePageModule.ts'
676+
import ErrorComponent from '../app-ui/ErrorComponent.vue'
677+
import LoadingComponent from '../app-ui/LoadingComponent.vue'
678+
679+
export default [
680+
createModuleRoute({
681+
path: '/',
682+
name: 'home', // optional
683+
// meta?: Record<string, unknown> -- optional
684+
module: HomePageModule,
685+
component: () => import('../../feature/home/presentation/HomePageView.vue'),
686+
loadingComponent: LoadingComponent,
687+
errorComponent: ErrorComponent
688+
}),
689+
]
690+
```
691+
540692
## Usage Examples
541693

542694
### Basic Application Example with Abstract Service

package.json

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,46 @@
1010
"bugs": {
1111
"url": "https://github.com/AndreyShashlovDev/flexdi/issues"
1212
},
13-
"main": "./dist/cjs/core/index.js",
14-
"module": "./dist/esm/core/index.js",
13+
"main": "./dist/core/index.cjs",
14+
"module": "./dist/core/index.mjs",
1515
"types": "./dist/types/core/index.d.ts",
1616
"type": "module",
1717
"exports": {
1818
".": {
1919
"types": "./dist/types/core/index.d.ts",
20-
"import": "./dist/esm/core/index.js",
21-
"require": "./dist/cjs/core/index.js"
20+
"import": "./dist/index.mjs",
21+
"require": "./dist/index.cjs"
2222
},
2323
"./react": {
2424
"types": "./dist/types/react/index.d.ts",
25-
"import": "./dist/esm/react/index.js",
26-
"require": "./dist/cjs/react/index.js"
25+
"import": "./dist/react/index.mjs",
26+
"require": "./dist/react/index.cjs"
27+
},
28+
"./vue3": {
29+
"types": "./dist/types/vue3/index.d.ts",
30+
"import": "./dist/vue3/index.mjs",
31+
"require": "./dist/vue3/index.cjs"
32+
}
33+
},
34+
"typesVersions": {
35+
"*": {
36+
"*": [
37+
"dist/types/core/index.d.ts"
38+
],
39+
"react": [
40+
"dist/types/react/index.d.ts"
41+
],
42+
"vue3": [
43+
"dist/types/vue3/index.d.ts"
44+
]
2745
}
2846
},
2947
"files": [
3048
"dist"
3149
],
3250
"scripts": {
3351
"clean": "rimraf dist",
34-
"build": "rollup -c rollup.config.mjs && tsc --project tsconfig.json",
35-
"dev": "rollup -c rollup.config.mjs -w",
52+
"build": "vite build",
3653
"test": "vitest run",
3754
"test:watch": "vitest",
3855
"test:coverage": "vitest run --coverage",
@@ -46,22 +63,26 @@
4663
"peerDependencies": {
4764
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
4865
"reflect-metadata": "^0.2.2",
49-
"rxjs": "^6.0.0 || ^7.0.0"
66+
"rxjs": "^6.0.0 || ^7.0.0",
67+
"vue": "^3.0.0"
5068
},
5169
"peerDependenciesMeta": {
5270
"react": {
5371
"optional": true
5472
},
73+
"vue": {
74+
"optional": true
75+
},
5576
"rxjs": {
5677
"optional": true
5778
}
5879
},
80+
"vite": {
81+
"optimizeDeps": {
82+
"disabled": true
83+
}
84+
},
5985
"devDependencies": {
60-
"@rollup/plugin-alias": "^5.1.1",
61-
"@rollup/plugin-commonjs": "^28.0.3",
62-
"@rollup/plugin-node-resolve": "^15.0.2",
63-
"@rollup/plugin-terser": "^0.4.4",
64-
"@rollup/plugin-typescript": "^11.1.0",
6586
"@semantic-release/changelog": "^6.0.3",
6687
"@semantic-release/git": "^10.0.1",
6788
"@semantic-release/npm": "^12.0.1",
@@ -71,21 +92,21 @@
7192
"@testing-library/user-event": "^14.4.3",
7293
"@types/node": "^18.15.11",
7394
"@types/react": "^19.1.0",
74-
"@types/react-dom": "^19.1.1",
75-
"@vitejs/plugin-react": "^4.0.0",
95+
"@vitejs/plugin-react": "^4.3.4",
96+
"@vitejs/plugin-vue": "^5.2.3",
7697
"jsdom": "^26.0.0",
7798
"react": "^19.1.0",
7899
"react-dom": "^19.1.0",
79100
"react-test-renderer": "^19.1.0",
80101
"reflect-metadata": "^0.2.2",
81102
"rimraf": "^5.0.0",
82-
"rollup": "^3.20.2",
83-
"rollup-plugin-dts": "^5.3.0",
84-
"rollup-plugin-peer-deps-external": "^2.2.4",
85103
"rxjs": "^7.8.2",
86104
"semantic-release": "^24.2.3",
87105
"tslib": "^2.5.0",
88106
"typescript": "^5.0.4",
89-
"vitest": "^3.1.1"
107+
"vite": "^6.2.5",
108+
"vite-plugin-dts": "^4.5.3",
109+
"vitest": "^3.1.1",
110+
"vue": "^3.5.13"
90111
}
91112
}

0 commit comments

Comments
 (0)