---
title: "Частые проблемы и руководство по устранению неполадок"
description: "Частые проблемы при разработке клиентских плагинов NocoBase: плагин не отображается, блок не появляется, перевод не работает, маршрут не находится, горячая перезагрузка не действует, ошибки сборки и упаковки, сбой запуска после развёртывания и др."
keywords: "FAQ,частые проблемы,устранение неполадок,Troubleshooting,NocoBase,сборка,развёртывание,tar,axios"
---

# Частые проблемы и руководство по устранению неполадок

Здесь собраны ловушки, в которые легко попасть при разработке клиентских плагинов. Если у Вас ситуация «вроде бы написано правильно, а не работает» — приходите сюда первым делом.

## Плагин

### После создания плагин не виден в менеджере

Убедитесь, что Вы выполняли `yarn pm create`, а не создавали каталог вручную. `yarn pm create`, помимо генерации файлов, регистрирует плагин в таблице `applicationPlugins` базы данных. Если каталог был создан вручную, можно выполнить `yarn nocobase upgrade` для повторного сканирования.

### После включения плагина страница не изменилась

Проверьте по порядку:

1. Убедитесь, что Вы выполнили `yarn pm enable <pluginName>`
2. Обновите браузер (иногда нужна принудительная перезагрузка `Ctrl+Shift+R`)
3. Проверьте консоль браузера на наличие ошибок

### После изменения кода страница не обновляется

Поведение горячей перезагрузки разное для разных типов файлов:

| Тип файла | Что нужно после изменения |
| --- | --- |
| tsx/ts в `src/client-v2/` | Автоматическая горячая перезагрузка, ничего не нужно |
| Файлы перевода в `src/locale/` | **Перезапустить приложение** |
| Добавление или изменение collection в `src/server/collections/` | Выполнить `yarn nocobase upgrade` |

Если код клиента изменён, но горячая перезагрузка не сработала, сначала попробуйте обновить браузер.

## Маршрутизация

### Зарегистрированный маршрут страницы недоступен

В NocoBase v2 маршруты по умолчанию имеют префикс `/v`. Например, если Вы зарегистрировали `path: '/hello'`, фактический адрес — `/v/hello`:

```ts
this.router.add('hello', {
  path: '/hello', // 实际访问 -> /v/hello
  componentLoader: () => import('./pages/HelloPage'),
});
```

Подробнее см. в [Router (Маршрутизация)](../router).

### Страница настроек плагина пустая

Если меню страницы настроек появилось, но содержимое пустое, обычно одна из двух причин:

**Причина первая: в v1 client использован `componentLoader`**

`componentLoader` — это синтаксис client-v2, в v1 client нужно использовать `Component` с прямой передачей компонента:

```ts
// ❌ v1 client 不支持 componentLoader
this.pluginSettingsManager.addPageTabItem({
  menuKey: 'my-settings',
  key: 'index',
  componentLoader: () => import('./pages/MyPage'),
});

// ✅ v1 client 用 Component
import MyPage from './pages/MyPage';
this.pluginSettingsManager.addPageTabItem({
  menuKey: 'my-settings',
  key: 'index',
  Component: MyPage,
});
```

**Причина вторая: компонент страницы не экспортирован через `export default`**

`componentLoader` требует, чтобы у модуля был экспорт по умолчанию — без `default` загрузить не получится.

## Блоки

### Пользовательский блок не виден в меню «Добавить блок»

Убедитесь, что в `load()` зарегистрирована модель:

```ts
this.flowEngine.registerModelLoaders({
  MyBlockModel: {
    loader: () => import('./models/MyBlockModel'),
  },
});
```

Если используется `registerModels` (без ленивой загрузки), убедитесь, что в `models/index.ts` модель корректно экспортирована.

### При добавлении блока в списке выбора таблицы данных нет моей таблицы

Таблица, определённая через `defineCollection`, является внутренней серверной таблицей и по умолчанию не появляется в списке таблиц данных в UI.

**Рекомендуемый подход**: добавьте соответствующую таблицу данных в [Управлении источниками данных](../../../data-sources/data-source-main/index.md) интерфейса NocoBase, после настройки полей и типа интерфейса таблица автоматически появится в списке выбора таблиц данных в блоке.

Если действительно нужно регистрировать в коде плагина (например, в демонстрационном сценарии плагина-примера), можно вручную зарегистрировать через `addCollection`. Подробнее см. в [Создание плагина управления данными с интеграцией фронтенда и бэкенда](../examples/fullstack-plugin). Внимание: регистрировать нужно через шаблон `eventBus`, нельзя напрямую вызывать в `load()` — `ensureLoaded()` будет выполнено после `load()` и очистит и заново установит все collection.

### Хочу, чтобы пользовательский блок был привязан только к определённой таблице данных

Переопределите `static filterCollection` на модели — в списке выбора появятся только collection, для которых функция вернула `true`:

```ts
export class MyBlockModel extends TableBlockModel {
  static filterCollection(collection: Collection) {
    return collection.name === 'myTable';
  }
}
```

## Поля

### Пользовательский компонент поля не виден в выпадающем меню «Компонент поля»

Проверьте по порядку:

1. Убедитесь, что вызван `DisplayItemModel.bindModelToInterface('ModelName', ['input'])`, и тип interface совпадает — например, `input` соответствует полю однострочного текста, `checkbox` — флажку
2. Убедитесь, что модель зарегистрирована в `load()` (`registerModels` или `registerModelLoaders`)
3. Убедитесь, что для модели поля вызван `define({ label })`

### В выпадающем меню «Компонент поля» отображается имя класса

Забыли вызвать `define({ label })` на модели поля — добавьте это:

```ts
MyFieldModel.define({
  label: tExpr('My field'),
});
```

Также убедитесь, что в файлах перевода `src/locale/` есть соответствующий ключ — иначе в китайской среде будет отображаться английский оригинал.

## Действия

### Пользовательская кнопка действия не видна в «Настройка действий»

Убедитесь, что на модели правильно установлен `static scene`:

| Значение | Где появится |
| --- | --- |
| `ActionSceneEnum.collection` | Панель действий вверху блока (например, рядом с кнопкой «Создать») |
| `ActionSceneEnum.record` | Столбец действий каждой строки таблицы (например, рядом с «Редактировать», «Удалить») |
| `ActionSceneEnum.both` | Появляется в обоих сценариях |

### Клик по кнопке действия не реагирует

Убедитесь, что `on` в `registerFlow` установлено в `'click'`:

```ts
MyActionModel.registerFlow({
  key: 'myFlow',
  on: 'click', // 监听按钮点击
  steps: {
    doSomething: {
      async handler(ctx) {
        // 你的逻辑
      },
    },
  },
});
```

:::warning Внимание

`uiSchema` в `registerFlow` — это UI панели конфигурации (режим настройки), а не модальное окно во время выполнения. Если Вы хотите по клику кнопки открыть форму, в `handler` следует использовать `ctx.viewer.dialog()` для открытия модального окна.

:::

## Интернационализация

### Перевод не работает

Самые частые причины:

- **Первое добавление** каталога или файла `src/locale/` — нужно перезапустить приложение
- **Несовпадение ключей перевода** — убедитесь, что ключ полностью совпадает со строкой в коде, обращайте внимание на пробелы и регистр
- **В компоненте напрямую использован `ctx.t()`** — `ctx.t()` не подставляет namespace плагина автоматически; в компоненте следует использовать хук `useT()` (импортируется из `locale.ts`)

### Неправильное использование `tExpr()`, `useT()` и `this.t()`

У этих трёх методов перевода разные сценарии — при неправильном использовании либо будет ошибка, либо перевод не сработает:

| Метод | Где используется | Описание |
| --- | --- | --- |
| `tExpr()` | Статические определения `define()`, `registerFlow()` и т.д. | На этапе загрузки модуля i18n ещё не инициализирован, нужен отложенный перевод |
| `useT()` | Внутри React-компонентов | Возвращает функцию перевода с привязкой к namespace плагина |
| `this.t()` | В `load()` плагина | Автоматически подставляет имя пакета плагина в качестве namespace |

Подробнее см. в [i18n Интернационализация](../component/i18n).

## API-запросы

### Запрос возвращает 403 Forbidden

Обычно причина в отсутствии настройки ACL на сервере. Например, если Ваш collection называется `todoItems`, в `load()` серверного плагина нужно разрешить соответствующие операции:

```ts
// 只允许查询
this.app.acl.allow('todoItems', ['list', 'get'], 'loggedIn');

// 允许完整增删改查
this.app.acl.allow('todoItems', ['list', 'get', 'create', 'update', 'destroy'], 'loggedIn');
```

`'loggedIn'` означает, что доступ имеет любой авторизованный пользователь. Без `acl.allow` по умолчанию операции может выполнять только администратор.

### Запрос возвращает 404 Not Found

Проверьте по порядку:

- Если используется `defineCollection`, убедитесь в правильности написания имени collection
- Если используется `resourceManager.define`, убедитесь в правильности имён resource и action
- Проверьте формат URL запроса — формат API в NocoBase: `resourceName:actionName`, например `todoItems:list`, `externalApi:get`

## Сборка и развёртывание

### `yarn build --tar` ошибка "no paths specified to add to archive"

При выполнении `yarn build <pluginName> --tar` появляется ошибка:

```bash
TypeError: no paths specified to add to archive
```

Однако отдельное выполнение `yarn build <pluginName>` (без `--tar`) проходит нормально.

Эта проблема обычно возникает из-за того, что в `.npmignore` плагина **используется синтаксис отрицания** (префикс `!` в npm). При упаковке через `--tar` NocoBase читает каждую строку `.npmignore` и добавляет перед ней `!`, чтобы преобразовать в шаблон исключения `fast-glob`. Если в `.npmignore` уже использован синтаксис отрицания, например:

```
*
!dist
!package.json
```

После обработки получится `['!*', '!!dist', '!!package.json', '**/*']`. При этом `!*` исключает все файлы корневого уровня (включая `package.json`), а `!!dist` не распознаётся `fast-glob` как «снова включить dist» — отрицание не работает. Если каталог `dist/` оказался пустым или сборка не выдала файлов, итоговый список собранных файлов будет пустым, и `tar` выбросит эту ошибку.

**Решение:** не используйте синтаксис отрицания в `.npmignore`, перечислите только каталоги, которые нужно исключить:

```
/node_modules
/src
```

Логика упаковки преобразует это в шаблоны исключения (`!./node_modules`, `!./src`), плюс `**/*` для всех остальных файлов. Так и проще, и без проблем с отрицанием.

### После загрузки плагина в продакшен включение завершается ошибкой (локально работает)

Плагин при локальной разработке работает нормально, но после загрузки через «Менеджер плагинов» в продакшен включение завершается ошибкой, в логе появляется примерно такое:

```bash
TypeError: Cannot assign to read only property 'constructor' of object '[object Object]'
```

Эта проблема обычно возникает из-за того, что **плагин запаковал встроенные зависимости NocoBase в собственный `node_modules/`**. Система сборки NocoBase поддерживает [список external](../../dependency-management) — пакеты в нём (например, `react`, `antd`, `axios`, `lodash` и т.д.) предоставляются хостом NocoBase и не должны упаковываться в плагин. Если плагин содержит свою копию, во время выполнения может возникнуть конфликт с уже загруженной хостом версией, вызывающий разные странные ошибки.

**Почему локально нет проблем:** при локальной разработке плагин находится в каталоге `packages/plugins/`, у него нет собственного `node_modules/`, зависимости разрешаются в уже загруженные версии в корне проекта, конфликта не возникает.

**Решение:** перенесите все `dependencies` в `package.json` плагина в `devDependencies` — система сборки NocoBase автоматически обработает зависимости плагина:

```diff
{
- "dependencies": {
-   "axios": "1.7.7"
- },
+ "devDependencies": {
+   "axios": "1.7.7"
+ },
}
```

Затем пересоберите и упакуйте. Так в `dist/node_modules/` плагина не попадут эти пакеты, и во время выполнения будут использоваться версии, предоставленные хостом NocoBase.

:::tip Общий принцип

Система сборки NocoBase поддерживает [список external](../../dependency-management) — пакеты в нём (например, `react`, `antd`, `axios`, `lodash` и т.д.) предоставляются хостом NocoBase, плагин не должен их упаковывать сам. Все зависимости плагина следует помещать в `devDependencies`, система сборки автоматически определит, какие нужно упаковать в `dist/node_modules/`, а какие предоставит хост.

:::

## Связанные ссылки

- [Plugin (Плагин)](../plugin) — точка входа плагина и жизненный цикл
- [Router (Маршрутизация)](../router) — регистрация маршрутов и префикс `/v`
- [Обзор FlowEngine](../flow-engine/index.md) — базовое использование FlowModel
- [FlowEngine → Расширение блоков](../flow-engine/block) — BlockModel, TableBlockModel, filterCollection
- [FlowEngine → Расширение полей](../flow-engine/field) — FieldModel, bindModelToInterface
- [FlowEngine → Расширение действий](../flow-engine/action) — ActionModel, ActionSceneEnum
- [i18n Интернационализация](../component/i18n) — файлы перевода, useT, использование tExpr
- [Context → Распространённые возможности](../ctx/common-capabilities) — ctx.api, ctx.viewer и т.д.
- [Сервер → Collections (Таблицы данных)](../../server/collections) — defineCollection и addCollection
- [Сервер → ACL Контроль доступа](../../server/acl) — настройка прав доступа интерфейсов
- [Сборка плагина](../../build) — конфигурация сборки, список external, процесс упаковки
