-
Notifications
You must be signed in to change notification settings - Fork 201
Обновление гайда NuxtJS + FSD #798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -7,173 +7,250 @@ sidebar_position: 10 | |||||
|
||||||
- Изначально, NuxtJS предлагает файловую структуру проекта без папки `src`, то есть в корне проекта. | ||||||
- Файловый роутинг находится в папке `pages`, а в FSD эта папка отведена под плоскую структуру слайсов. | ||||||
- Автоимпорты работают для всех компонентов, в то время как FSD пропагандирует концепцию Public API (делится только необходимым из index файлов). | ||||||
|
||||||
Но NuxtJS обладает довольно гибкой настройкой конфигурации, что поможет нам полноценно внедрить в него FSD, не теряя его основных достоинств со стороны разработки: | ||||||
|
||||||
## Добавление алиаса для `src` директории | ||||||
- Автоимпорты. | ||||||
- Файловый роутинг. | ||||||
|
||||||
Добавьте обьект `alias` в ваш конфиг: | ||||||
```ts title="nuxt.config.ts" | ||||||
export default defineNuxtConfig({ | ||||||
devtools: { enabled: true }, // Не относятся к FSD, включёны при старте проекта | ||||||
alias: { | ||||||
"@": '../src' | ||||||
}, | ||||||
}) | ||||||
``` | ||||||
## Выбор способа настройки роутера | ||||||
## Автоимпорты | ||||||
|
||||||
В NuxtJS есть два способа настройки роутинга - с помощью конфига и с помощью файловой структуры. | ||||||
В случае с файловым роутингом вы будете создавать index.vue файлы в папках внутри директории app/routes, а в случае конфига - настраивать роуты в `router.options.ts` файле. | ||||||
На первый взгляд автоимпорты противоречат концепции Public Api и FSD, т.к. создает импорты для всех компонентов, но этого можно избежать, правильно настроив конфигурацию. | ||||||
|
||||||
### Компоненты | ||||||
|
||||||
### Роутинг с помощью конфига | ||||||
По умолчанию в NuxtJS для компонентов выделена дирректория `components/`. | ||||||
|
||||||
В слое `app` создайте файл `router.options.ts`, и экспортируйте из него обьект конфига: | ||||||
```ts title="app/router.options.ts" | ||||||
import type { RouterConfig } from '@nuxt/schema'; | ||||||
Дирректории для компонентов можно переопределить настройкой `components`. | ||||||
|
||||||
export default <RouterConfig> { | ||||||
routes: (_routes) => [], | ||||||
}; | ||||||
#### shared | ||||||
|
||||||
```ts | ||||||
components: { | ||||||
dirs: [ | ||||||
{ | ||||||
path: "~/src/shared", // shared дирректория | ||||||
pattern: "**/*index.vue", // Файл для автоимпорта | ||||||
extensions: ["vue"], | ||||||
}, | ||||||
]; | ||||||
} | ||||||
``` | ||||||
|
||||||
Чтобы добавить страницу `Home` в проект, вам нужно сделать следующие шаги: | ||||||
- Добавить слайс страницы внутри слоя `pages` | ||||||
- Добавить соответствующий роут в конфиг `app/router.config.ts` | ||||||
#### entities | ||||||
|
||||||
```ts | ||||||
components: { | ||||||
dirs: [ | ||||||
{ | ||||||
path: "~/src/entities", // entities дирректория | ||||||
extendComponent(component) { | ||||||
component.pascalName = component.pascalName.replaceAll("Ui", ""); // Убираем Ui дирректории из имени компонента | ||||||
return component; | ||||||
}, | ||||||
pattern: "**/*index.vue", // Файл для автоимпорта | ||||||
extensions: ["vue"], | ||||||
}, | ||||||
]; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
``` | ||||||
|
||||||
Для того чтобы создать слайс страницы, воспользуемся [CLI](https://github.com/feature-sliced/cli): | ||||||
#### features | ||||||
|
||||||
```shell | ||||||
fsd pages home | ||||||
```ts | ||||||
components: { | ||||||
dirs: [ | ||||||
{ | ||||||
path: "~/src/features", // features дирректория | ||||||
extendComponent(component) { | ||||||
component.pascalName = component.pascalName.replaceAll("Ui", ""); // Убираем Ui дирректории из имени компонента | ||||||
component.pascalName = component.pascalName + "Feature"; // Добавляем Feature постфикс | ||||||
return component; | ||||||
}, | ||||||
pattern: "**/*index.vue", // Файл для автоимпорта | ||||||
extensions: ["vue"], | ||||||
}, | ||||||
]; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
``` | ||||||
|
||||||
Создайте файл `home-page.vue` внутри сегмента ui, откройте к нему доступ с помощью Public API | ||||||
#### widgets | ||||||
|
||||||
```ts title="src/pages/home/index.ts" | ||||||
export { default as HomePage } from './ui/home-page'; | ||||||
```ts | ||||||
components: { | ||||||
dirs: [ | ||||||
{ | ||||||
path: "~/src/widgets", // widgets дирректория | ||||||
extendComponent(component) { | ||||||
component.pascalName = component.pascalName.replaceAll("Ui", ""); // Убираем Ui дирректории из имени компонента | ||||||
component.pascalName = component.pascalName + "Widget"; // Добавляем Widget постфикс | ||||||
return component; | ||||||
}, | ||||||
pattern: "**/*index.vue", // Файл для автоимпорта | ||||||
extensions: ["vue"], | ||||||
}, | ||||||
]; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
``` | ||||||
|
||||||
Таким образом, файловая структура будет выглядеть так: | ||||||
```sh | ||||||
|── src | ||||||
│ ├── app | ||||||
│ │ ├── router.config.ts | ||||||
│ ├── pages | ||||||
│ │ ├── home | ||||||
│ │ │ ├── ui | ||||||
│ │ │ │ ├── home-page.vue | ||||||
│ │ │ ├── index.ts | ||||||
#### pages | ||||||
|
||||||
```ts | ||||||
components: { | ||||||
dirs: [ | ||||||
{ | ||||||
path: "~/src/pages", // pages дирректория | ||||||
pattern: "**/*index.vue", // Файл для автоимпорта | ||||||
extensions: ["vue"], | ||||||
}, | ||||||
]; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
``` | ||||||
Наконец, добавим роут в конфиг: | ||||||
|
||||||
```ts title="app/router.config.ts" | ||||||
import type { RouterConfig } from '@nuxt/schema' | ||||||
### Типы, функции и т.п. | ||||||
|
||||||
export default <RouterConfig> { | ||||||
routes: (_routes) => [ | ||||||
{ | ||||||
name: 'home', | ||||||
path: '/', | ||||||
component: () => import('@/pages/home.vue').then(r => r.default || r) | ||||||
} | ||||||
По умолчанию в NuxtJS для этого выделена дирректория `utils/` и т.п. | ||||||
|
||||||
Такие дирректории можно переопределить настройкой `imports`. | ||||||
|
||||||
```ts | ||||||
imports: { | ||||||
dirs: [ | ||||||
"./src/widgets/*/*/index.ts", // Автоимпорты для widgets слоя | ||||||
"./src/features/*/*/index.ts", // Автоимпорты для features слоя | ||||||
"./src/entities/*/*/index.ts", // Автоимпорты для entities слоя | ||||||
"./src/shared/*/index.ts", // Автоимпорты для shared слоя | ||||||
], | ||||||
} | ||||||
``` | ||||||
|
||||||
### Файловый роутинг | ||||||
### Использование | ||||||
|
||||||
В первую очередь, создайте `src` директорию в корне проекта, а также создайте внутри этой директории слои app и pages и папку routes внутри слоя app. | ||||||
Таким образом, ваша файловая структура должна выглядеть так: | ||||||
1. Создайте `src` дирректорию. | ||||||
2. Добавьте в `src` дирректорию FSD слои. | ||||||
|
||||||
```sh | ||||||
├── src | ||||||
│ ├── app | ||||||
│ │ ├── routes | ||||||
│ ├── pages # Папка pages, закреплённая за FSD | ||||||
```bash | ||||||
.../ | ||||||
├── src/ | ||||||
│ ├── app/ | ||||||
│ ├── entities/ | ||||||
│ ├── features/ | ||||||
│ ├── shared/ | ||||||
│ ├── widgets/ | ||||||
``` | ||||||
|
||||||
Для того чтобы NuxtJS использовал папку routes внутри слоя `app` для файлового роутинга, вам нужно изменить `nuxt.config.ts` следующим образом: | ||||||
```ts title="nuxt.config.ts" | ||||||
export default defineNuxtConfig({ | ||||||
devtools: { enabled: true }, // Не относятся к FSD, включёны при старте проекта | ||||||
alias: { | ||||||
"@": '../src' | ||||||
}, | ||||||
dir: { | ||||||
pages: './src/app/routes' | ||||||
} | ||||||
}) | ||||||
#### shared | ||||||
|
||||||
Рассмотрим компонент `UiButton` для примера. | ||||||
|
||||||
##### Структурв папок | ||||||
|
||||||
```bash | ||||||
.../ | ||||||
├── src/ | ||||||
│ ├── shared/ | ||||||
│ │ ├── ui/ | ||||||
│ │ │ ├── button/ | ||||||
│ │ │ │ ├── interfaces/ | ||||||
│ │ │ │ │ ├── ... | ||||||
│ │ │ │ ├── index.ts # Для автоимпортов типов компонента | ||||||
│ │ │ │ ├── index.vue # Для автоимпорта компонента | ||||||
│ │ ├── utils/ # Любой сегмент | ||||||
│ │ │ ├── sum.ts | ||||||
│ │ │ ├── index.ts # Для автоимпорта функции и т.п. сегмента | ||||||
``` | ||||||
|
||||||
Теперь, вы можете создавать роуты для страниц внутри `app` и подключать к ним страницы из `pages`. | ||||||
`ui/button/index.ts` - файл для автоимпорта типов компонента. | ||||||
|
||||||
Например, чтобы добавить страницу `Home` в проект, вам нужно сделать следующие шаги: | ||||||
- Добавить слайс страницы внутри слоя `pages` | ||||||
- Добавить соответствующий роут внутрь слоя `app` | ||||||
- Совместить страницу из слайса с роутом | ||||||
```ts | ||||||
export type { IUiButtonProps } from "./interfaces"; | ||||||
``` | ||||||
|
||||||
Для того чтобы создать слайс страницы, воспользуемся [CLI](https://github.com/feature-sliced/cli): | ||||||
`ui/button/index.vue` - файл для автоимпорта компонента. | ||||||
|
||||||
```shell | ||||||
fsd pages home | ||||||
``` | ||||||
```vue | ||||||
<script setup lang="ts"> | ||||||
import type { IUiButtonProps } from "./interfaces"; | ||||||
|
||||||
Создайте файл `home-page.vue` внутри сегмента ui, откройте к нему доступ с помощью Public API | ||||||
const props = withDefaults(defineProps<IUiButtonProps>(), { | ||||||
type: "button", | ||||||
}); | ||||||
</script> | ||||||
|
||||||
```ts title="src/pages/home/index.ts" | ||||||
export { default as HomePage } from './ui/home-page'; | ||||||
<template> | ||||||
<button :type="type"> | ||||||
<slot /> | ||||||
</button> | ||||||
</template> | ||||||
``` | ||||||
|
||||||
Создайте роут для этой страницы внутри слоя `app`: | ||||||
`utils/index.ts` - файл для автоимпорта функции и т.п. сегмента. | ||||||
|
||||||
```sh | ||||||
|
||||||
├── src | ||||||
│ ├── app | ||||||
│ │ ├── routes | ||||||
│ │ │ ├── index.vue | ||||||
│ ├── pages | ||||||
│ │ ├── home | ||||||
│ │ │ ├── ui | ||||||
│ │ │ │ ├── home-page.vue | ||||||
│ │ │ ├── index.ts | ||||||
```ts | ||||||
export { sum } from "./sum"; | ||||||
``` | ||||||
|
||||||
Добавьте внутрь `index.vue` файла компонент вашей страницы: | ||||||
##### Использование | ||||||
|
||||||
```html title="src/app/routes/index.vue" | ||||||
<script setup> | ||||||
import { HomePage } from '@/pages/home'; | ||||||
```vue | ||||||
<script setup lang="ts"> | ||||||
const props: IUiButtonProps = { | ||||||
type: "submit", | ||||||
}; | ||||||
</script> | ||||||
|
||||||
<template> | ||||||
<HomePage/> | ||||||
<UiButton v-bind="props"> Total: {{ sum(1, 4) }} </UiButton> | ||||||
</template> | ||||||
``` | ||||||
|
||||||
## Что делать с `layouts`? | ||||||
|
||||||
Вы можете разместить layouts внутри слоя `app`, для этого нужно изменить конфиг следующим образом: | ||||||
|
||||||
```ts title="nuxt.config.ts" | ||||||
export default defineNuxtConfig({ | ||||||
devtools: { enabled: true }, // Не относятся к FSD, включёны при старте проекта | ||||||
alias: { | ||||||
"@": '../src' | ||||||
}, | ||||||
dir: { | ||||||
pages: './src/app/routes', | ||||||
layouts: './src/app/layouts' | ||||||
} | ||||||
}) | ||||||
#### entities | ||||||
|
||||||
Все также, как с `shared`, но компоненты не имеют `Ui` префикса и в структуру папок добавляется папка `слайса`. | ||||||
|
||||||
#### features, widgets | ||||||
|
||||||
Все также, как с `entities`, но компоненты имеют `Feature` и `Widget` постфикс. | ||||||
|
||||||
#### pages | ||||||
|
||||||
```bash | ||||||
.../ | ||||||
├── src/ | ||||||
│ ├── pages/ | ||||||
│ │ ├── product/ # Любая страница | ||||||
│ │ │ ├── index.vue # Для автоимпорта компонента страницы | ||||||
``` | ||||||
|
||||||
## Роутинг, шаблоны, middleware | ||||||
|
||||||
Необходимо переопределить дирректории по умолчанию. | ||||||
|
||||||
```ts | ||||||
dir: { | ||||||
pages: "./src/app/routes", // Роутинг | ||||||
layouts: "./src/app/layouts", // Шаблоны | ||||||
middleware: "./src/app/middlewares", // middlewares | ||||||
}, | ||||||
``` | ||||||
|
||||||
### app | ||||||
|
||||||
```bash | ||||||
.../ | ||||||
├── src/ | ||||||
│ ├── app/ | ||||||
│ │ ├── routes/ # Роуты приложения | ||||||
│ │ │ ├── index.vue # Роут для пути "/" | ||||||
│ │ ├── middlewares/ # Middleware приложения | ||||||
│ │ │ ├── auth.ts # Middleware авторизации | ||||||
│ │ ├── layouts/ # Шаблоны приложения | ||||||
│ │ │ ├── default.vue # Шаблон приложения по умолчанию | ||||||
``` | ||||||
|
||||||
## См. также | ||||||
## Хочешь начать быстрее | ||||||
|
||||||
- [Документация по изменению конфига директорий в NuxtJS](https://nuxt.com/docs/api/nuxt-config#dir) | ||||||
- [Документация по изменению конфига роутера в NuxtJS](https://nuxt.com/docs/guide/recipes/custom-routing#router-config) | ||||||
- [Документация по изменению алиасов в NuxtJS](https://nuxt.com/docs/api/nuxt-config#alias) | ||||||
Чтобы не настраивать все вручную, можно использовать модуль, который автоматически меняет конфигурацию. | ||||||
|
||||||
[@dand.dev/nuxt-fsd](https://www.npmjs.com/package/@dand.dev/nuxt-fsd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.