Pinia Integration
fluth-vue recommends extracting all business logic to the logic layer and modeling the business, with business models driving the view layer, where the view layer is only responsible for consuming data.
In the Vue ecosystem, the best implementation of the logic layer is Pinia. Pinia is Vue's official state management library, providing powerful state management capabilities that can meet most business requirements.
fluth-vue is perfectly compatible with Pinia. Stream data can be directly viewed in the Pinia panel of vue-devtools.
In Pinia, through modular design principles and fluth-vue's reactive programming approach, business layer and view layer decoupling can be easily achieved.
Example
Through a simple example, we demonstrate how to implement business models in Pinia combined with fluth-vue's reactive data:
Code Directory
The logic layer is uniformly placed in the store directory. useModuleA and useModuleB represent two business modules, with each module's logic existing in the form of services.
└── store
├── index.ts
│
├── useModuleA
│ ├── services
│ │ ├── useServiceA.ts
│ │ └── useServiceB.ts
│ │ └── ...
│ └── index.ts
│
├── useModuleB
│ ├── services
│ │ ├── useServiceC.ts
│ │ └── useServiceD.ts
│ │ └── ...
│ └── index.ts
└── ...
Module
The module only does two things:
- Define dependencies between modules
- Define dependencies between services
If logic is a book, then the module is the chapters of the book, and services are the content of the book's chapters.
import { defineStore } from "pinia";
import { reactive } from "vue";
import { $ } from "fluth-vue";
import { useModuleB } from "./useModuleB";
const useModuleAStore = defineStore("moduleA", () => {
// Define dependencies between modules
const moduleB = useModuleB();
// Define dependencies between services
const serviceA = useServiceA(moduleB.serviceC);
const serviceB = useServiceB(serviceA);
// Services must be wrapped with reactive
// This way vue-devtools can directly view the reactive data of services in Pinia
return {
serviceA: reactive(serviceA),
serviceB: reactive(serviceB),
};
});
Service
Implementation of useServiceA:
import { useServiceC } from "../../useModuleB/services/useServiceC";
import { recover$, merge } from "fluth-vue";
const useServiceA = (serviceC: ReturnType<typeof useServiceC>) => {
// Recover stream instances from reactive
const { dataC$ } = recover$(serviceC);
const dataA$ = $("A");
const dataB$ = $(1);
merge(dataA$, dataC$).then((data) => {
// Detailed logic
});
dataB$.then((dataB) => {
// Detailed logic
});
return {
dataA$,
dataB$,
};
};
Implementation of useServiceB:
// Define business service B
const useServiceB = (serviceA: ReturnType<typeof useServiceA>) => {
const { dataA$ } = serviceA;
const dataC$ = $("C");
const dataD$ = $(3);
dataC$.then((dataC) => {
// Detailed logic
});
promiseAll(dataA$, dataD$).then(([dataA, dataD]) => {
// Detailed logic
});
return {
dataC$,
dataD$,
};
};
View Layer Consumption
<template>
<div>
<div>{{ dataA$ }}</div>
<div>{{ dataC$ }}</div>
<button @click="addDataB">Click</button>
<button @click="addDataD">Click 2</button>
</div>
</template>
<script setup lang="ts">
import { useModuleAStore } from "@/store";
import { recover$ } from "fluth-vue";
const moduleAStore = useModuleAStore();
// Recover stream instances from reactive
const { dataA$, dataB$ } = recover$(moduleAStore.serviceA);
const { dataC$, dataD$ } = recover$(moduleAStore.serviceB);
const addDataB = () => {
dataB$.next(dataB$.value + 1);
};
const addDataD = () => {
dataD$.next(dataD$.value + 1);
};
</script>