- Нет глобальных views - все views только в модулях
- Нет глобальных stores - все stores только в модулях
- Все маршруты в модулях - каждый модуль управляет своими маршрутами
- Модульная изоляция - каждый модуль самодостаточен
- Единая структура - все модули следуют одинаковой структуре
Каждый модуль должен иметь следующую структуру:
src/modules/[module-name]/
├── index.ts # Экспорт модуля
├── routes.ts # Маршруты модуля
├── store.ts # Stores модуля
├── depends.ts # Зависимости модуля
├── views/ # Страницы модуля
│ └── [ViewName].vue
├── components/ # Компоненты модуля
│ └── [ComponentName].vue
└── services/ # Сервисы модуля (опционально)
└── [ServiceName].ts
mkdir src/modules/my-module
mkdir src/modules/my-module/views
mkdir src/modules/my-module/components
mkdir src/modules/my-module/servicesimport Module from "@base/Module"
import routes from "./routes"
import store from "./store"
import dependencies from "./depends"
export default new Module("my-module", { routes, store, dependencies })import { type RouteRecordRaw } from "vue-router"
import MyView from "./views/MyView.vue"
export default ({ invoke }: { invoke: (address: string) => unknown }): RouteRecordRaw[] => {
const accessManager = invoke("@utils/AccessManager") as { isLogged: () => boolean }
return [
{
path: "/my-module",
name: "MyModule",
component: MyView,
beforeEnter: (to, from, next) => {
if (accessManager.isLogged()) {
next()
} else {
next({ name: "login" })
}
}
}
]
}import { defineStore } from "pinia"
import { type PiniaStoreModule } from "@types"
// Создаем store с помощью defineStore
export const useMyModuleStore = defineStore("my-module", {
state: () => ({
data: []
}),
getters: {
getData: state => state.data
},
actions: {
setData(data: unknown[]) {
this.data = data
}
}
})
// Экспортируем для модульной системы
export default (): PiniaStoreModule => {
return {
MyModule: {
id: "my-module",
state: () => ({
data: []
}),
getters: {
getData: state => state.data
},
actions: {
setData(data: unknown[]) {
this.data = data
}
}
}
}
}// Зависимости модуля
export default ({ invoke }: { invoke: (address: string) => unknown }) => {
const httpClient = invoke("@utils/HttpClient")
return {
"@my-module/services/MyService": () => new MyService(httpClient)
}
}<template>
<div class="my-view">
<h1>{{ title }}</h1>
<MyComponent />
</div>
</template>
<script setup lang="ts">
import { computed } from "vue"
import { useMyModuleStore } from "../store"
import MyComponent from "../components/MyComponent.vue"
// Имя компонента
defineOptions({
name: "MyView"
})
// Используем store модуля
const myModuleStore = useMyModuleStore()
// Computed свойства
const title = computed(() => "My Module")
</script>
<style scoped>
.my-view {
padding: 2rem;
}
</style><template>
<div class="my-component">
<p>{{ message }}</p>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue"
import { useMyModuleStore } from "../store"
// Имя компонента
defineOptions({
name: "MyComponent"
})
// Используем store модуля
const myModuleStore = useMyModuleStore()
// Computed свойства
const message = computed(() => "Hello from MyComponent")
</script>
<style scoped>
.my-component {
border: 1px solid #ddd;
padding: 1rem;
border-radius: 4px;
}
</style>Добавить модуль в src/modules/index.js:
import myModule from "./my-module"
export default [base, app, hello, counter, myModule]- Модули:
kebab-case(например:my-module) - Компоненты:
PascalCase(например:MyComponent.vue) - Views:
PascalCase(например:MyView.vue) - Stores:
camelCase(например:useMyModuleStore)
- Store ID должен совпадать с именем модуля
- Использовать
kebab-case(например:"my-module")
- path:
/module-name(например:/my-module) - name:
PascalCase(например:MyModule)
- Все функции должны иметь типы параметров и возвращаемых значений
- Использовать
defineOptionsдля имен компонентов - Типизировать все computed свойства и методы
// Store actions
async login(loginData: { email: string; password: string }): Promise<void>
// Component methods
const updateData = (): void => { /* ... */ }
// Computed properties
const data = computed(() => store.getData)- Создавать файлы в
src/views/илиsrc/components/ - Создавать stores в
src/stores/ - Использовать глобальные переменные между модулями
- Прямые импорты между модулями (использовать Context)
- Создавать модули в
src/modules/ - Использовать Context для обмена данными между модулями
- Создавать переиспользуемые утилиты в
src/utils/
- Создана папка модуля в
src/modules/ - Создан
index.tsс экспортом модуля - Создан
routes.tsс маршрутами - Создан
store.tsс stores - Создан
depends.tsс зависимостями - Созданы views в папке
views/ - Созданы компоненты в папке
components/ - Все файлы типизированы
- Модуль зарегистрирован в
modules/index.js - Протестирована функциональность
При обновлении модуля:
- Сохранить структуру папок
- Обновить типизацию
- Проверить совместимость с Context
- Обновить документацию
- Протестировать изменения
base- базовый модуль с маршрутами
app- модуль с глобальными storeshello- модуль с простым store
counter- модуль с store, views, components
Следование этим правилам обеспечивает:
- Консистентность архитектуры
- Масштабируемость проекта
- Легкость поддержки кода
- Изоляцию модулей
- Переиспользование компонентов