mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-05-01 01:10:44 -05:00
13a9f6f597
* docs: (ru) update build-targets.md * docs: (ru) ui-api.md update * docs: (ru) ui-localization.md update * docs: (ru) deployment.md update * docs: (ru) config/readme.md update
1350 lines
47 KiB
Markdown
1350 lines
47 KiB
Markdown
# API плагина для UI
|
||
|
||
С помощью API `cli-ui` возможно дополнять конфигурацию и задачи проекта, а также обмениваться данными и взаимодействовать с другими процессами.
|
||
|
||

|
||
|
||
## Файлы UI
|
||
|
||
Внутри каждого установленного vue-cli плагина cli-ui попытается загрузить опциональный файл `ui.js` из корневого каталога плагина. Обратите внимание, также можно использовать каталоги (например, `ui/index.js`).
|
||
|
||
Файл должен экспортировать функцию, которая получает объект `api` в качестве аргумента:
|
||
|
||
```js
|
||
module.exports = api => {
|
||
// Используйте API здесь...
|
||
}
|
||
```
|
||
|
||
**⚠️ Файлы будут перезагружены при получении списка плагинов на странице «Плагины проекта». Чтобы применить изменения, нажмите кнопку «Плагины проекта» в боковой панели слева в UI.**
|
||
|
||
Вот пример структуры каталога для vue-cli плагина, использующего UI API:
|
||
|
||
```
|
||
- vue-cli-plugin-test
|
||
- package.json
|
||
- index.js
|
||
- generator.js
|
||
- prompts.js
|
||
- ui.js
|
||
- logo.png
|
||
```
|
||
|
||
### Локальные плагины проекта
|
||
|
||
Если необходим доступ к API плагина в вашем проекте и вы не хотите создавать полноценный плагин для этого, вы можете использовать опцию `vuePlugins.ui` в файле `package.json`:
|
||
|
||
```json
|
||
{
|
||
"vuePlugins": {
|
||
"ui": ["my-ui.js"]
|
||
}
|
||
}
|
||
```
|
||
|
||
Каждый файл должен экспортировать функцию, получающую API плагина первым аргументом.
|
||
|
||
## Режим разработки
|
||
|
||
При разработке плагина может потребоваться запустить cli-ui в режиме разработки, чтобы использовать логи с полезной информацией:
|
||
|
||
```
|
||
vue ui --dev
|
||
```
|
||
|
||
Или:
|
||
|
||
```
|
||
vue ui -D
|
||
```
|
||
|
||
## Конфигурации проекта
|
||
|
||

|
||
|
||
Вы можете добавить конфигурацию проекта с помощью метода `api.describeConfig`.
|
||
|
||
Сначала вам нужно передать некоторую информацию:
|
||
|
||
```js
|
||
api.describeConfig({
|
||
// Уникальный ID для конфигурации
|
||
id: 'org.vue.eslintrc',
|
||
// Отображаемое имя
|
||
name: 'Конфигурация ESLint',
|
||
// Показывается под названием
|
||
description: 'Проверка ошибок & качество кода',
|
||
// Ссылка "More info"
|
||
link: 'https://eslint.org'
|
||
})
|
||
```
|
||
|
||
::: danger Убедитесь!
|
||
В правильно указанном пространстве имён; оно должно быть уникальным для всех плагинов. Рекомендуется применять [нотацию перевёрнутого доменного имени](https://en.wikipedia.org/wiki/Reverse_domain_name_notation).
|
||
:::
|
||
|
||
### Конфигурация иконки
|
||
|
||
Может быть кодом [иконки из Material](https://material.io/tools/icons) или пользовательским изображением (см. [Публичные статические файлы](#пубnичные-статические-файnы)):
|
||
|
||
```js
|
||
api.describeConfig({
|
||
/* ... */
|
||
// Конфигурация иконки
|
||
icon: 'application_settings'
|
||
})
|
||
```
|
||
|
||
Если не указать иконку, будет отображаться логотип плагина, если таковой есть (см. [Логотип](./ui-info.md#логотип)).
|
||
|
||
### Файлы конфигураций
|
||
|
||
По умолчанию конфигурация UI может читать и записывать в один или несколько файлов, например как в `.eslintrc.js` так и в `vue.config.js`.
|
||
|
||
Вы можете указать, какие файлы обнаруживать в проекте пользователя:
|
||
|
||
```js
|
||
api.describeConfig({
|
||
/* ... */
|
||
// Все возможные файлы для этой конфигурации
|
||
files: {
|
||
// eslintrc.js
|
||
eslint: {
|
||
js: ['.eslintrc.js'],
|
||
json: ['.eslintrc', '.eslintrc.json'],
|
||
// Будет читать из `package.json`
|
||
package: 'eslintConfig'
|
||
},
|
||
// vue.config.js
|
||
vue: {
|
||
js: ['vue.config.js']
|
||
}
|
||
},
|
||
})
|
||
```
|
||
|
||
Поддерживаемые типы: `json`, `yaml`, `js`, `package`. Порядок важен: первое имя файла в списке будет использоваться для создания файла конфигурации, если он не существует.
|
||
|
||
### Отображение подсказок конфигурации
|
||
|
||
Используйте хук `onRead` чтобы вернуть список подсказок, которые будут отображаться для конфигурации:
|
||
|
||
```js
|
||
api.describeConfig({
|
||
/* ... */
|
||
onRead: ({ data, cwd }) => ({
|
||
prompts: [
|
||
// объекты подсказок
|
||
]
|
||
})
|
||
})
|
||
```
|
||
|
||
Эти подсказки будут отображаться на панели конфигурации.
|
||
|
||
Для получения дополнительной информации см. [Интерактивные подсказки](#интерактивные-подсказки).
|
||
|
||
Объект `data` содержит JSON с результатом контента каждого файла конфигурации.
|
||
|
||
Например, допустим, что у пользователя есть следующий `vue.config.js` в проекте:
|
||
|
||
```js
|
||
module.exports = {
|
||
lintOnSave: false
|
||
}
|
||
```
|
||
|
||
Мы объявляем конфигурационный файл в плагине следующим образом:
|
||
|
||
```js
|
||
api.describeConfig({
|
||
/* ... */
|
||
// Все возможные файлы в этой конфигурации
|
||
files: {
|
||
// vue.config.js
|
||
vue: {
|
||
js: ['vue.config.js']
|
||
}
|
||
},
|
||
})
|
||
```
|
||
|
||
Объект `data` будет:
|
||
|
||
```js
|
||
{
|
||
// Файл
|
||
vue: {
|
||
// Данные файла
|
||
lintOnSave: false
|
||
}
|
||
}
|
||
```
|
||
|
||
Пример с несколькими файлами: если мы добавим файл `eslintrc.js` в проект пользователя:
|
||
|
||
```js
|
||
module.exports = {
|
||
root: true,
|
||
extends: [
|
||
'plugin:vue/essential',
|
||
'@vue/standard'
|
||
]
|
||
}
|
||
```
|
||
|
||
И изменим опцию `files` в нашем плагине на это:
|
||
|
||
```js
|
||
api.describeConfig({
|
||
/* ... */
|
||
// Все возможные файлы в этой конфигурации
|
||
files: {
|
||
// eslintrc.js
|
||
eslint: {
|
||
js: ['.eslintrc.js'],
|
||
json: ['.eslintrc', '.eslintrc.json'],
|
||
// Будет читать из `package.json`
|
||
package: 'eslintConfig'
|
||
},
|
||
// vue.config.js
|
||
vue: {
|
||
js: ['vue.config.js']
|
||
}
|
||
},
|
||
})
|
||
```
|
||
|
||
Объект `data` будет:
|
||
|
||
```js
|
||
{
|
||
eslint: {
|
||
root: true,
|
||
extends: [
|
||
'plugin:vue/essential',
|
||
'@vue/standard'
|
||
]
|
||
},
|
||
vue: {
|
||
lintOnSave: false
|
||
}
|
||
}
|
||
```
|
||
|
||
### Вкладки конфигурации
|
||
|
||
Вы можете организовать подсказки на нескольких вкладках:
|
||
|
||
```js
|
||
api.describeConfig({
|
||
/* ... */
|
||
onRead: ({ data, cwd }) => ({
|
||
tabs: [
|
||
{
|
||
id: 'tab1',
|
||
label: 'Моя вкладка',
|
||
// Опционально
|
||
icon: 'application_settings',
|
||
prompts: [
|
||
// Объекты подсказок
|
||
]
|
||
},
|
||
{
|
||
id: 'tab2',
|
||
label: 'Моя вторая вкладка',
|
||
prompts: [
|
||
// Объекты подсказок
|
||
]
|
||
}
|
||
]
|
||
})
|
||
})
|
||
```
|
||
|
||
### Сохранение изменений конфигурации
|
||
|
||
Используйте хук `onWrite` для записи данных в файл (или выполнения любого кода nodejs):
|
||
|
||
```js
|
||
api.describeConfig({
|
||
/* ... */
|
||
onWrite: ({ prompts, answers, data, files, cwd, api }) => {
|
||
// ...
|
||
}
|
||
})
|
||
```
|
||
|
||
Аргументы:
|
||
|
||
- `prompts`: текущие объекты подсказок для runtime (см. ниже)
|
||
- `answers`: данные ответов от пользовательского ввода
|
||
- `data`: начальные данные только для чтения, считанные из файлов конфигурации
|
||
- `files`: дескрипторы найденных файлов (`{ type: 'json', path: '...' }`)
|
||
- `cwd`: текущий рабочий каталог
|
||
- `api`: `onWrite API` (см. ниже)
|
||
|
||
Объекты подсказок для runtime:
|
||
|
||
```js
|
||
{
|
||
id: data.name,
|
||
type: data.type,
|
||
name: data.short || null,
|
||
message: data.message,
|
||
group: data.group || null,
|
||
description: data.description || null,
|
||
link: data.link || null,
|
||
choices: null,
|
||
visible: true,
|
||
enabled: true,
|
||
// Текущее значение (не фильтруется)
|
||
value: null,
|
||
// true если было изменено пользователем
|
||
valueChanged: false,
|
||
error: null,
|
||
tabId: null,
|
||
// Оригинальный объект подсказок inquirer
|
||
raw: data
|
||
}
|
||
```
|
||
|
||
`onWrite` API:
|
||
|
||
- `assignData(fileId, newData)`: использует `Object.assign` для обновление данных конфигурации перед записью.
|
||
- `setData(fileId, newData)`: каждый ключ из `newData` будет установлен (или удалён если значение `undefined`) в данные конфигурации перед записью.
|
||
- `async getAnswer(id, mapper)`: получает ответ для заданного id подсказки и обрабатывает его с помощью функции `mapper`, если она предоставлена (например, `JSON.parse`).
|
||
|
||
Пример (из плагина для ESLint):
|
||
|
||
```js
|
||
api.describeConfig({
|
||
// ...
|
||
|
||
onWrite: async ({ api, prompts }) => {
|
||
// Обновление правил ESLint
|
||
const result = {}
|
||
for (const prompt of prompts) {
|
||
result[`rules.${prompt.id}`] = await api.getAnswer(prompt.id, JSON.parse)
|
||
}
|
||
api.setData('eslint', result)
|
||
}
|
||
})
|
||
```
|
||
|
||
## Задачи проекта
|
||
|
||

|
||
|
||
Задачи создаются из поля `scripts` файла `package.json` проекта.
|
||
|
||
Можно «расширять» задачи дополнительной информацией и хуками через `api.describeTask`:
|
||
|
||
```js
|
||
api.describeTask({
|
||
// RegExp выполняется по командам скриптов для определения задачи описываемой здесь
|
||
match: /vue-cli-service serve/,
|
||
description: 'Компиляция и горячая замена модулей для разработки',
|
||
// Ссылка "More info"
|
||
link: 'https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#serve'
|
||
})
|
||
```
|
||
|
||
Также можно использовать функцию для `match`:
|
||
|
||
```js
|
||
api.describeTask({
|
||
match: (command) => /vue-cli-service serve/.test(command),
|
||
})
|
||
```
|
||
|
||
### Иконка задачи
|
||
|
||
Может быть кодом [иконки из Material](https://material.io/tools/icons) или пользовательским изображением (см. [Публичные статические файлы](#пубnичные-статические-файnы)):
|
||
|
||
```js
|
||
api.describeTask({
|
||
/* ... */
|
||
// Иконка задачи
|
||
icon: 'application_settings'
|
||
})
|
||
```
|
||
|
||
Если не указать иконку, будет отображаться логотип плагина, если таковой есть (см. [Логотип](./ui-info.md#логотип)).
|
||
|
||
### Параметры задачи
|
||
|
||
Вы можете добавлять подсказки для изменения аргументов команды. Они будут отображаться в модальном окне «Параметры».
|
||
|
||
Например:
|
||
|
||
```js
|
||
api.describeTask({
|
||
// ...
|
||
|
||
// Опциональные параметры (подсказки inquirer)
|
||
prompts: [
|
||
{
|
||
name: 'open',
|
||
type: 'confirm',
|
||
default: false,
|
||
description: 'Открывать браузер при старте сервера'
|
||
},
|
||
{
|
||
name: 'mode',
|
||
type: 'list',
|
||
default: 'development',
|
||
choices: [
|
||
{
|
||
name: 'development',
|
||
value: 'development'
|
||
},
|
||
{
|
||
name: 'production',
|
||
value: 'production'
|
||
},
|
||
{
|
||
name: 'test',
|
||
value: 'test'
|
||
}
|
||
],
|
||
description: 'Указать режим env'
|
||
}
|
||
]
|
||
})
|
||
```
|
||
|
||
См. [Интерактивные подсказки](#интерактивные-подсказки) для более подробной информации.
|
||
|
||
### Хуки задачи
|
||
|
||
Доступно несколько хуков:
|
||
|
||
- `onBeforeRun`
|
||
- `onRun`
|
||
- `onExit`
|
||
|
||
Например, использовать ответы подсказок (см. выше) для добавления аргументов в команду:
|
||
|
||
```js
|
||
api.describeTask({
|
||
// ...
|
||
|
||
// Хуки
|
||
// Изменяем аргументы здесь
|
||
onBeforeRun: async ({ answers, args }) => {
|
||
// Аргументы
|
||
if (answers.open) args.push('--open')
|
||
if (answers.mode) args.push('--mode', answers.mode)
|
||
args.push('--dashboard')
|
||
},
|
||
// Сразу после запуска задачи
|
||
onRun: async ({ args, child, cwd }) => {
|
||
// child: дочерний процесс node
|
||
// cwd: рабочий каталог процесса
|
||
},
|
||
onExit: async ({ args, child, cwd, code, signal }) => {
|
||
// code: код выхода
|
||
// signal: сигнал kill, если использовался
|
||
}
|
||
})
|
||
```
|
||
|
||
### Страницы задачи
|
||
|
||
Вы можете отображать пользовательские представления в панели сведений задачи с помощью `ClientAddon` API:
|
||
|
||
```js
|
||
api.describeTask({
|
||
// ...
|
||
|
||
// Дополнительные представления (например для панели webpack)
|
||
// По умолчанию есть представление 'output' которое отображает вывод терминала
|
||
views: [
|
||
{
|
||
// Уникальный ID
|
||
id: 'vue-webpack-dashboard-client-addon',
|
||
// Текст кнопки
|
||
label: 'Dashboard',
|
||
// Иконка кнопки
|
||
icon: 'dashboard',
|
||
// Динамический компонент для загрузки (см. секцию "Клиентское дополнение" ниже)
|
||
component: 'vue-webpack-dashboard'
|
||
}
|
||
],
|
||
// Стартовый вид отображения сведений о задаче (по умолчанию это output)
|
||
defaultView: 'vue-webpack-dashboard-client-addon'
|
||
})
|
||
```
|
||
|
||
См. [Клиентское дополнение](#кnиентское-допоnнение) для более подробной информации.
|
||
|
||
|
||
### Добавление новых задач
|
||
|
||
Также можно добавлять совершенно новые задачи, которые не указаны в `package.json` с помощью `api.addTask` вместо `api.describeTask`. Эти задачи будут отображаться только в пользовательском интерфейсе cli UI.
|
||
|
||
**Вам необходимо указать опцию `command` вместо `match`.**
|
||
|
||
Например:
|
||
|
||
```js
|
||
api.addTask({
|
||
// Обязательно
|
||
name: 'inspect',
|
||
command: 'vue-cli-service inspect',
|
||
// Опционально
|
||
// Остальное похоже на `describeTask` без опции `match`
|
||
description: '...',
|
||
link: 'https://github.com/vuejs/vue-cli/...',
|
||
prompts: [ /* ... */ ],
|
||
onBeforeRun: () => {},
|
||
onRun: () => {},
|
||
onExit: () => {},
|
||
views: [ /* ... */ ],
|
||
defaultView: '...'
|
||
})
|
||
```
|
||
|
||
**⚠️ `command` запускается в контексте node. Это означает, что вы можете использовать команды к бинарникам node как обычно, будто из скриптов `package.json`.**
|
||
|
||
## Интерактивные подсказки
|
||
|
||
Объекты подсказок должен быть корректными объектами [inquirer](https://github.com/SBoudrias/Inquirer.js).
|
||
|
||
Однако, вы можете добавить следующие дополнительные поля (которые являются опциональными и используются только пользовательским интерфейсом):
|
||
|
||
```js
|
||
{
|
||
/* ... */
|
||
// Используется для группировки подсказок на разделы
|
||
group: 'Настоятельно рекомендуется',
|
||
// Дополнительное описание
|
||
description: 'Принудительный стиль именования атрибутов в шаблоне (`my-prop` или `myProp`)',
|
||
// Ссылка "More info"
|
||
link: 'https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/attribute-hyphenation.md',
|
||
}
|
||
```
|
||
|
||
Поддерживаемые inquirer типы: `checkbox`, `confirm`, `input`, `password`, `list`, `rawlist`.
|
||
|
||
В дополнение к ним пользовательский интерфейс поддерживает специальные типы, которые работают только с ним:
|
||
|
||
- `color`: интерфейс выбора цвета.
|
||
|
||
### Пример с переключателем
|
||
|
||
```js
|
||
{
|
||
name: 'open',
|
||
type: 'confirm',
|
||
default: false,
|
||
description: 'Открыть приложение в браузере'
|
||
}
|
||
```
|
||
|
||
### Пример со списком вариантов
|
||
|
||
```js
|
||
{
|
||
name: 'mode',
|
||
type: 'list',
|
||
default: 'development',
|
||
choices: [
|
||
{
|
||
name: 'Режим разработки',
|
||
value: 'development'
|
||
},
|
||
{
|
||
name: 'Режим production',
|
||
value: 'production'
|
||
},
|
||
{
|
||
name: 'Режим тестирования',
|
||
value: 'test'
|
||
}
|
||
],
|
||
description: 'Режим сборки',
|
||
link: 'https://link-to-docs'
|
||
}
|
||
```
|
||
|
||
### Пример с полем ввода
|
||
|
||
```js
|
||
{
|
||
name: 'host',
|
||
type: 'input',
|
||
default: '0.0.0.0',
|
||
description: 'Хост для сервера разработки'
|
||
}
|
||
```
|
||
|
||
### Пример с чекбоксом
|
||
|
||
Отображает несколько переключателей.
|
||
|
||
```js
|
||
{
|
||
name: 'lintOn',
|
||
message: 'Выберите дополнительные возможности линтинга:',
|
||
when: answers => answers.features.includes('linter'),
|
||
type: 'checkbox',
|
||
choices: [
|
||
{
|
||
name: 'Линтинг при сохранении',
|
||
value: 'save',
|
||
checked: true
|
||
},
|
||
{
|
||
name: 'Линтинг и исправление при коммите' + (hasGit() ? '' : chalk.red(' (требуется Git)')),
|
||
value: 'commit'
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### Пример с выбором цвета
|
||
|
||
```js
|
||
{
|
||
name: 'themeColor',
|
||
type: 'color',
|
||
message: 'Цвет темы',
|
||
description: 'Используется для изменения цвета интерфейса системы вокруг приложения',
|
||
default: '#4DBA87'
|
||
}
|
||
```
|
||
|
||
### Подсказки для вызова
|
||
|
||
В плагине vue-cli может быть файл `prompts.js`, который задаёт пользователю несколько вопросов при установке плагина (через CLI или UI). Можно добавить дополнительные поля только для UI (см. выше) к этим объектам подсказок, чтобы они предоставили больше информации, если пользователь использует UI.
|
||
|
||
**⚠️ В настоящее время типы inquirer, которые не поддерживаются (см. выше), не будут работать в UI.**
|
||
|
||
## Клиентское дополнение
|
||
|
||
Клиентское дополнение — это сборка JS, которая динамически загружается в cli-ui. Она полезна для загрузки пользовательских компонентов и маршрутов.
|
||
|
||
### Создание клиентского дополнения
|
||
|
||
Рекомендуемый способ создания клиентского дополнения — создать новый проект с помощью vue-cli 3. Вы можете либо сделать это в подкаталоге вашего плагина, либо в другом npm пакете.
|
||
|
||
Установите `@vue/cli-ui` в качестве зависимости для разработки (dev dependency).
|
||
|
||
Затем добавьте файл `vue.config.js` со следующим содержимым:
|
||
|
||
```js
|
||
const { clientAddonConfig } = require('@vue/cli-ui')
|
||
|
||
module.exports = {
|
||
...clientAddonConfig({
|
||
id: 'org.vue.webpack.client-addon',
|
||
// Порт разработки (по умолчанию 8042)
|
||
port: 8042
|
||
})
|
||
}
|
||
```
|
||
|
||
Метод `clientAddonConfig` генерирует необходимую конфигурацию vue-cli. Кроме того, он отключает извлечение CSS и выводит код в `./dist/index.js` в папку клиентского дополнения.
|
||
|
||
::: danger Убедитесь!
|
||
В правильно указанном пространстве имён; оно должно быть уникальным для всех плагинов. Рекомендуется применять [нотацию перевёрнутого доменного имени](https://en.wikipedia.org/wiki/Reverse_domain_name_notation).
|
||
:::
|
||
|
||
Затем измените файл `.eslintrc.json`, чтобы добавить несколько глобальных объектов:
|
||
|
||
```json
|
||
{
|
||
// ...
|
||
"globals": {
|
||
"ClientAddonApi": false,
|
||
"mapSharedData": false,
|
||
"Vue": false
|
||
}
|
||
}
|
||
```
|
||
|
||
Теперь можно запустить скрипт `serve` для разработки и `build`, когда будете готовы опубликовать свой плагин.
|
||
|
||
### ClientAddonApi
|
||
|
||
Откройте файл `main.js` в исходном коде клиентского дополнения и удалите весь код.
|
||
|
||
**⚠️ Не импортируйте Vue в исходном коде клиентского дополнения, используйте глобальный объект `Vue` из `window` браузера.**
|
||
|
||
Вот пример кода для `main.js`:
|
||
|
||
```js
|
||
import VueProgress from 'vue-progress-path'
|
||
import WebpackDashboard from './components/WebpackDashboard.vue'
|
||
import TestView from './components/TestView.vue'
|
||
|
||
// Вы можете устанавливать дополнительные Vue-плагины
|
||
// используя глобальную переменную 'Vue'
|
||
Vue.use(VueProgress, {
|
||
defaultShape: 'circle'
|
||
})
|
||
|
||
// Регистрировать пользовательские компоненты
|
||
// (работает аналогично 'Vue.component')
|
||
ClientAddonApi.component('org.vue.webpack.components.dashboard', WebpackDashboard)
|
||
|
||
// Добавлять маршруты во vue-router в соответствии с родительским маршрутом /addon/<id>.
|
||
// Например, addRoutes('foo', [ { path: '' }, { path: 'bar' } ])
|
||
// будет добавлять маршруты /addon/foo/ и the /addon/foo/bar во vue-router.
|
||
// Здесь мы создаём новый маршрут '/addon/vue-webpack/' с именем 'test-webpack-route'
|
||
ClientAddonApi.addRoutes('org.vue.webpack', [
|
||
{ path: '', name: 'org.vue.webpack.routes.test', component: TestView }
|
||
])
|
||
|
||
// Вы можете переводить компоненты своего плагина
|
||
// Загрузите файлы локализаций (используется vue-i18n)
|
||
const locales = require.context('./locales', true, /[a-z0-9]+\.json$/i)
|
||
locales.keys().forEach(key => {
|
||
const locale = key.match(/([a-z0-9]+)\./i)[1]
|
||
ClientAddonApi.addLocalization(locale, locales(key))
|
||
})
|
||
```
|
||
|
||
::: danger Убедитесь!
|
||
В правильно указанном пространстве имён; оно должно быть уникальным для всех плагинов. Рекомендуется применять [нотацию перевёрнутого доменного имени](https://en.wikipedia.org/wiki/Reverse_domain_name_notation).
|
||
:::
|
||
|
||
Также cli-ui регистрирует `Vue` и `ClientAddonApi` глобальными переменными в `window`.
|
||
|
||
В компонентах можно использовать все компоненты и CSS классы [@vue/ui](https://github.com/vuejs/ui) и [@vue/cli-ui](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-ui/src/components), чтобы обеспечить одинаковый внешний вид. Переводить тексты можно с помощью [vue-i18n](https://github.com/kazupon/vue-i18n), который используется по умолчанию.
|
||
|
||
### Регистрация клиентского дополнения
|
||
|
||
Возвращаясь к файлу `ui.js`, используйте метод `api.addClientAddon` с строкой запроса к встроенному каталогу:
|
||
|
||
```js
|
||
api.addClientAddon({
|
||
id: 'org.vue.webpack.client-addon',
|
||
// Каталог содержащий все собранные JS файлы
|
||
path: '@vue/cli-ui-addon-webpack/dist'
|
||
})
|
||
```
|
||
|
||
Будет использован Node.js API `require.resolve` для поиска каталога в файловой системе и использоваться файл `index.js`, созданный из клиентского дополнения.
|
||
|
||
Или укажите URL-адрес при разработке плагина (в идеале вы захотите сделать это в файле `vue-cli-ui.js` в вашем тестовом проекте vue):
|
||
|
||
```js
|
||
// Полезно для разработки
|
||
// Перезапишет путь, если он уже определён в плагине
|
||
api.addClientAddon({
|
||
id: 'org.vue.webpack.client-addon',
|
||
// Используйте тот же порт, который указали ранее
|
||
url: 'http://localhost:8042/index.js'
|
||
})
|
||
```
|
||
|
||
### Использование клиентского дополнения
|
||
|
||
Теперь можно использовать клиентское дополнение в представлениях. Например, вы можете указать представление в описании задачи:
|
||
|
||
```js
|
||
api.describeTask({
|
||
/* ... */
|
||
// Дополнительные представления (например для панели webpack)
|
||
// По умолчанию есть представление 'output', которое отображает вывод терминала
|
||
views: [
|
||
{
|
||
// Уникальный ID
|
||
id: 'org.vue.webpack.views.dashboard',
|
||
// Текст кнопки
|
||
label: 'Dashboard',
|
||
// Иконка кнопки (material-icons)
|
||
icon: 'dashboard',
|
||
// Динамический компонент для загрузки, зарегистрированный через ClientAddonApi
|
||
component: 'org.vue.webpack.components.dashboard'
|
||
}
|
||
],
|
||
// Стартовое представление при отображении сведений о задаче (по умолчанию output)
|
||
defaultView: 'org.vue.webpack.views.dashboard'
|
||
})
|
||
```
|
||
|
||
Вот код клиентского дополнения, который регистрирует компонент `'org.vue.webpack.components.dashboard'` (как мы видели ранее):
|
||
|
||
```js
|
||
/* В `main.js` */
|
||
// Импортируем компонент
|
||
import WebpackDashboard from './components/WebpackDashboard.vue'
|
||
// Регистрируем пользовательский компонент
|
||
// (работает аналогично 'Vue.component')
|
||
ClientAddonApi.component('org.vue.webpack.components.dashboard', WebpackDashboard)
|
||
```
|
||
|
||

|
||
|
||
## Пользовательские страницы
|
||
|
||
Можно добавить новую страницу под стандартными «Плагины проекта», «Конфигурация проекта» и «Задачи проекта» с помощью метода `api.addView`:
|
||
|
||
```js
|
||
api.addView({
|
||
// Уникальный ID
|
||
id: 'org.vue.webpack.views.test',
|
||
|
||
// Имя маршрута (из vue-router)
|
||
// Использует то же имя, как и в методе 'ClientAddonApi.addRoutes' (см. выше в разлеле клиентское дополнение)
|
||
name: 'org.vue.webpack.routes.test',
|
||
|
||
// Иконка кнопки (material-icons)
|
||
icon: 'pets',
|
||
// Можно указать собственное изображение (см. ниже раздел публичных статичных файлов):
|
||
// icon: 'http://localhost:4000/_plugin/%40vue%2Fcli-service/webpack-icon.svg',
|
||
|
||
// Подсказка для кнопки
|
||
tooltip: 'Тестовая страница из дополнения webpack'
|
||
})
|
||
```
|
||
|
||
Вот пример кода в клиентском дополнении, который регистрирует `'org.vue.webpack.routes.test'` (как мы видели ранее):
|
||
|
||
```js
|
||
/* В `main.js` */
|
||
// Импортируем компонент
|
||
import TestView from './components/TestView.vue'
|
||
// Добавляем маршруты в vue-router под родительским маршрутом /addon/<id>.
|
||
// Например, addRoutes('foo', [ { path: '' }, { path: 'bar' } ])
|
||
// добавит маршруты /addon/foo/ и /addon/foo/bar во vue-router.
|
||
// Теперь создаём новый маршрут '/addon/vue-webpack/' с именем 'test-webpack-route'
|
||
ClientAddonApi.addRoutes('org.vue.webpack', [
|
||
{ path: '', name: 'org.vue.webpack.routes.test', component: TestView }
|
||
])
|
||
```
|
||
|
||

|
||
|
||
## Общие данные
|
||
|
||
Используйте общие данные для обмена информацией с пользовательскими компонентами.
|
||
|
||
> Например, панель Webpack предоставляет данные статистики сборки как UI-клиенту так и UI-серверу с помощью этого API.
|
||
|
||
В файле `ui.js` плагина (Node.js):
|
||
|
||
```js
|
||
// Установка или обновление
|
||
api.setSharedData('com.my-name.my-variable', 'some-data')
|
||
|
||
// Получение
|
||
const sharedData = api.getSharedData('com.my-name.my-variable')
|
||
if (sharedData) {
|
||
console.log(sharedData.value)
|
||
}
|
||
|
||
// Удаление
|
||
api.removeSharedData('com.my-name.my-variable')
|
||
|
||
// Отслеживание изменений
|
||
const watcher = (value, id) => {
|
||
console.log(value, id)
|
||
}
|
||
api.watchSharedData('com.my-name.my-variable', watcher)
|
||
// Прекращение отслеживания изменений
|
||
api.unwatchSharedData('com.my-name.my-variable', watcher)
|
||
|
||
// Версии для пространства имён
|
||
const {
|
||
setSharedData,
|
||
getSharedData,
|
||
removeSharedData,
|
||
watchSharedData,
|
||
unwatchSharedData
|
||
} = api.namespace('com.my-name.')
|
||
```
|
||
|
||
::: danger Убедитесь!
|
||
В правильно указанном пространстве имён; оно должно быть уникальным для всех плагинов. Рекомендуется применять [нотацию перевёрнутого доменного имени](https://en.wikipedia.org/wiki/Reverse_domain_name_notation).
|
||
:::
|
||
|
||
В пользовательском компоненте:
|
||
|
||
```js
|
||
// Компонент Vue
|
||
export default {
|
||
// Синхронизируем общие данные
|
||
sharedData () {
|
||
return {
|
||
// Можно использовать `myVariable` в шаблоне
|
||
myVariable: 'com.my-name.my-variable'
|
||
// Можно указывать общие данные нужного пространства имён
|
||
...mapSharedData('com.my-name.', {
|
||
myVariable2: 'my-variable2'
|
||
})
|
||
}
|
||
},
|
||
|
||
// Ручные методы
|
||
async created () {
|
||
const value = await this.$getSharedData('com.my-name.my-variable')
|
||
|
||
this.$watchSharedData(`com.my-name.my-variable`, value => {
|
||
console.log(value)
|
||
})
|
||
|
||
await this.$setSharedData('com.my-name.my-variable', 'new-value')
|
||
}
|
||
}
|
||
```
|
||
|
||
При использовании опции `sharedData` общие данные можно обновлять просто присвоением нового значения соответствующему свойству.
|
||
|
||
```html
|
||
<template>
|
||
<VueInput v-model="message"/>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
sharedData: {
|
||
// Синхронизирует общие данные 'my-message' на сервере
|
||
message: 'com.my-name.my-message'
|
||
}
|
||
}
|
||
</script>
|
||
```
|
||
|
||
Это очень удобно, например при создании компонента настроек.
|
||
|
||
## Действия плагина
|
||
|
||
Действия плагина — это вызовы между cli-ui (браузером) и плагинами (nodejs).
|
||
|
||
> Например, может быть кнопка в пользовательском компоненте (см. [клиентское дополнение](#кnиентское-допоnнение)), которая вызывает некоторый код nodejs на сервере с помощью этого API.
|
||
|
||
В файле `ui.js` в плагине (nodejs), вы можете использовать два метода из `PluginApi`:
|
||
|
||
```js
|
||
// Вызов действия
|
||
api.callAction('com.my-name.other-action', { foo: 'bar' }).then(results => {
|
||
console.log(results)
|
||
}).catch(errors => {
|
||
console.error(errors)
|
||
})
|
||
```
|
||
|
||
```js
|
||
// Отслеживание действия
|
||
api.onAction('com.my-name.test-action', params => {
|
||
console.log('test-action called', params)
|
||
})
|
||
```
|
||
|
||
::: danger Убедитесь!
|
||
В правильно указанном пространстве имён; оно должно быть уникальным для всех плагинов. Рекомендуется применять [нотацию перевёрнутого доменного имени](https://en.wikipedia.org/wiki/Reverse_domain_name_notation).
|
||
:::
|
||
|
||
Можно указывать версии для пространства имён через `api.namespace` (как и в общих данных):
|
||
|
||
```js
|
||
const { onAction, callAction } = api.namespace('com.my-name.')
|
||
```
|
||
|
||
В клиентском дополнении (браузере) можно получить доступ к `$onPluginActionCalled`, `$onPluginActionResolved` и `$callPluginAction`:
|
||
|
||
```js
|
||
// Компонент Vue
|
||
export default {
|
||
created () {
|
||
this.$onPluginActionCalled(action => {
|
||
// Когда действие вызывается
|
||
// до того как будет выполнено
|
||
console.log('called', action)
|
||
})
|
||
this.$onPluginActionResolved(action => {
|
||
// После того как действие запущено и завершено
|
||
console.log('resolved', action)
|
||
})
|
||
},
|
||
|
||
methods: {
|
||
testPluginAction () {
|
||
// Вызов действия плагина
|
||
this.$callPluginAction('com.my-name.test-action', {
|
||
meow: 'meow'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## Коммуникация между процессами (IPC)
|
||
|
||
IPC означает коммуникацию между процессами. Эта система позволяет легко отправлять сообщения из дочерних процессов (например, задач!). И это довольно быстро и просто.
|
||
|
||
> Для отображения данных в UI на панели управления webpack, команды `serve` и `build` из `@vue/cli-service` отправляют IPC-сообщения на сервер cli-ui nodejs, когда передаётся аргумент `--dashboard`.
|
||
|
||
В коде процесса (который может быть Webpack-плагином или скриптом задачи для nodejs), можно использовать класс `IpcMessenger` из `@vue/cli-shared-utils`:
|
||
|
||
```js
|
||
const { IpcMessenger } = require('@vue/cli-shared-utils')
|
||
|
||
// Создание нового экземпляра IpcMessenger
|
||
const ipc = new IpcMessenger()
|
||
|
||
function sendMessage (data) {
|
||
// Отправка сообщения на сервер cli-ui
|
||
ipc.send({
|
||
'com.my-name.some-data': {
|
||
type: 'build',
|
||
value: data
|
||
}
|
||
})
|
||
}
|
||
|
||
function messageHandler (data) {
|
||
console.log(data)
|
||
}
|
||
|
||
// Отслеживание сообщения
|
||
ipc.on(messageHandler)
|
||
|
||
// Прекращение отслеживания
|
||
ipc.off(messageHandler)
|
||
|
||
function cleanup () {
|
||
// Отключение от сети IPC
|
||
ipc.disconnect()
|
||
}
|
||
```
|
||
|
||
Подключение вручную:
|
||
|
||
```js
|
||
const ipc = new IpcMessenger({
|
||
autoConnect: false
|
||
})
|
||
|
||
// Это сообщение будет добавлено в очередь
|
||
ipc.send({ ... })
|
||
|
||
ipc.connect()
|
||
```
|
||
|
||
Автоотключение при простое (спустя некоторое время без отправляемых сообщений):
|
||
|
||
```js
|
||
const ipc = new IpcMessenger({
|
||
disconnectOnIdle: true,
|
||
idleTimeout: 3000 // По умолчанию
|
||
})
|
||
|
||
ipc.send({ ... })
|
||
|
||
setTimeout(() => {
|
||
console.log(ipc.connected) // false
|
||
}, 3000)
|
||
```
|
||
|
||
Подключение к другой IPC-сети:
|
||
|
||
```js
|
||
const ipc = new IpcMessenger({
|
||
networkId: 'com.my-name.my-ipc-network'
|
||
})
|
||
```
|
||
|
||
В файле `ui.js` плагина vue-cli, можно использовать методы `ipcOn`, `ipcOff` и `ipcSend`:
|
||
|
||
```js
|
||
function onWebpackMessage ({ data: message }) {
|
||
if (message['com.my-name.some-data']) {
|
||
console.log(message['com.my-name.some-data'])
|
||
}
|
||
}
|
||
|
||
// Отслеживание любого IPC-сообщения
|
||
api.ipcOn(onWebpackMessage)
|
||
|
||
// Прекращение отслеживания
|
||
api.ipcOff(onWebpackMessage)
|
||
|
||
// Отправка сообщения во все подключённые экземпляры IpcMessenger
|
||
api.ipcSend({
|
||
webpackDashboardMessage: {
|
||
foo: 'bar'
|
||
}
|
||
})
|
||
```
|
||
|
||
## Локальное хранилище
|
||
|
||
Плагин может сохранять и загружать данные из локальной базы данных [lowdb](https://github.com/typicode/lowdb), используемой сервером UI.
|
||
|
||
```js
|
||
// Сохранение значения в локальной базе данных
|
||
api.storageSet('com.my-name.an-id', { some: 'value' })
|
||
|
||
// Получение значения из локальной базы данных
|
||
console.log(api.storageGet('com.my-name.an-id'))
|
||
|
||
// Полноценный экземпляр lowdb
|
||
api.db.get('posts')
|
||
.find({ title: 'low!' })
|
||
.assign({ title: 'hi!'})
|
||
.write()
|
||
|
||
// Использование версий для пространства имён
|
||
const { storageGet, storageSet } = api.namespace('my-plugin.')
|
||
```
|
||
|
||
::: danger Убедитесь!
|
||
В правильно указанном пространстве имён; оно должно быть уникальным для всех плагинов. Рекомендуется применять [нотацию перевёрнутого доменного имени](https://en.wikipedia.org/wiki/Reverse_domain_name_notation).
|
||
:::
|
||
|
||
## Уведомления
|
||
|
||
Можно показывать уведомления через систему уведомлений операционной системы:
|
||
|
||
```js
|
||
api.notify({
|
||
title: 'Какой-то заголовок',
|
||
message: 'Сообщение пользователю',
|
||
icon: 'path-to-icon.png'
|
||
})
|
||
```
|
||
|
||
Есть несколько встроенных иконок:
|
||
|
||
- `'done'`
|
||
- `'error'`
|
||
|
||
## Экран прогресса
|
||
|
||
Можно показывать экран прогресса с текстом и индикатором:
|
||
|
||
```js
|
||
api.setProgress({
|
||
status: 'Обновление...',
|
||
error: null,
|
||
info: 'Шаг 2 из 4',
|
||
progress: 0.4 // значение от 0 до 1, -1 означает скрытый индикатор прогресса
|
||
})
|
||
```
|
||
|
||
Удаление экрана прогресса:
|
||
|
||
```js
|
||
api.removeProgress()
|
||
```
|
||
|
||
## Хуки
|
||
|
||
Хуки позволяют реагировать на определённые события в интерфейсе `cli-ui`.
|
||
|
||
### onProjectOpen
|
||
|
||
Вызывается когда плагин был загружен впервые для текущего проекта.
|
||
|
||
```js
|
||
api.onProjectOpen((project, previousProject) => {
|
||
// Сброс данных
|
||
})
|
||
```
|
||
|
||
### onPluginReload
|
||
|
||
Вызывается при перезагрузке плагина.
|
||
|
||
```js
|
||
api.onPluginReload((project) => {
|
||
console.log('плагин перезагружен')
|
||
})
|
||
```
|
||
|
||
### onConfigRead
|
||
|
||
Вызывается при открытии или обновлении экрана конфигурации.
|
||
|
||
```js
|
||
api.onConfigRead(({ config, data, onReadData, tabs, cwd }) => {
|
||
console.log(config.id)
|
||
})
|
||
```
|
||
|
||
### onConfigWrite
|
||
|
||
Вызывается при сохранении настроек пользователем на экране конфигурации.
|
||
|
||
```js
|
||
api.onConfigWrite(({ config, data, changedFields, cwd }) => {
|
||
// ...
|
||
})
|
||
```
|
||
|
||
### onTaskOpen
|
||
|
||
Вызывается при открытии пользователем вкладки с детализацией задачи.
|
||
|
||
```js
|
||
api.onTaskOpen(({ task, cwd }) => {
|
||
console.log(task.id)
|
||
})
|
||
```
|
||
|
||
### onTaskRun
|
||
|
||
Вызывается при запуске задачи пользователем.
|
||
|
||
```js
|
||
api.onTaskRun(({ task, args, child, cwd }) => {
|
||
// ...
|
||
})
|
||
```
|
||
|
||
### onTaskExit
|
||
|
||
Вызывается при завершении задачи. Вызывается и при успешном выполнении, и при ошибке.
|
||
|
||
```js
|
||
api.onTaskExit(({ task, args, child, signal, code, cwd }) => {
|
||
// ...
|
||
})
|
||
```
|
||
|
||
### onViewOpen
|
||
|
||
Вызывается при открытии страницы (например «Плагины», «Конфигурации» или «Задачи»).
|
||
|
||
```js
|
||
api.onViewOpen(({ view, cwd }) => {
|
||
console.log(view.id)
|
||
})
|
||
```
|
||
|
||
## Предположения
|
||
|
||
Предположения — это кнопки, предназначенные чтобы предложить действия пользователю. Они отображаются в верхней панели. Например, у нас может быть кнопка, предлагающая установить vue-router, если пакет не был обнаружен в приложении.
|
||
|
||
```js
|
||
api.addSuggestion({
|
||
id: 'com.my-name.my-suggestion',
|
||
type: 'action', // Обязательно (больше типов в будущем)
|
||
label: 'Add vue-router',
|
||
// Это будет показано в модальном окне подробностей
|
||
message: 'A longer message for the modal',
|
||
link: 'http://link-to-docs-in-the-modal',
|
||
// Опциональное изображение
|
||
image: '/_plugin/my-package/screenshot.png',
|
||
// Функция вызывается когда предположение активируется пользователем
|
||
async handler () {
|
||
// ...
|
||
return {
|
||
// По умолчанию удаляет кнопку
|
||
keep: false
|
||
}
|
||
}
|
||
})
|
||
```
|
||
|
||
::: danger Убедитесь!
|
||
В правильно указанном пространстве имён; оно должно быть уникальным для всех плагинов. Рекомендуется применять [нотацию перевёрнутого доменного имени](https://en.wikipedia.org/wiki/Reverse_domain_name_notation).
|
||
:::
|
||
|
||

|
||
|
||
Затем вы можете удалить предположение:
|
||
|
||
```js
|
||
api.removeSuggestion('com.my-name.my-suggestion')
|
||
```
|
||
|
||
Можно открыть страницу, когда пользователь активирует предположение, через `actionLink`:
|
||
|
||
```js
|
||
api.addSuggestion({
|
||
id: 'com.my-name.my-suggestion',
|
||
type: 'action', // Обязательно
|
||
label: 'Add vue-router',
|
||
// Открыть новую вкладку
|
||
actionLink: 'https://vuejs.org/'
|
||
})
|
||
```
|
||
|
||
Как правило, лучше использовать хуки для показа предположений в правильном контексте:
|
||
|
||
```js
|
||
const ROUTER = 'vue-router-add'
|
||
|
||
api.onViewOpen(({ view }) => {
|
||
if (view.id === 'vue-project-plugins') {
|
||
if (!api.hasPlugin('vue-router')) {
|
||
api.addSuggestion({
|
||
id: ROUTER,
|
||
type: 'action',
|
||
label: 'org.vue.cli-service.suggestions.vue-router-add.label',
|
||
message: 'org.vue.cli-service.suggestions.vue-router-add.message',
|
||
link: 'https://router.vuejs.org/',
|
||
async handler () {
|
||
await install(api, 'vue-router')
|
||
}
|
||
})
|
||
}
|
||
} else {
|
||
api.removeSuggestion(ROUTER)
|
||
}
|
||
})
|
||
```
|
||
|
||
В этом примере мы отображаем только предположение vue-router в представлении плагинов, только если в проекте нет уже установленного vue-router.
|
||
|
||
Примечание: `addSuggestion` и `removeSuggestion` могут задаваться к пространству имён с помощью `api.namespace()`.
|
||
|
||
## Другие методы
|
||
|
||
### hasPlugin
|
||
|
||
Возвращает `true` если проект использует плагин.
|
||
|
||
```js
|
||
api.hasPlugin('eslint')
|
||
api.hasPlugin('apollo')
|
||
api.hasPlugin('vue-cli-plugin-apollo')
|
||
```
|
||
|
||
### getCwd
|
||
|
||
Возвращает текущий рабочий каталог.
|
||
|
||
```js
|
||
api.getCwd()
|
||
```
|
||
|
||
### resolve
|
||
|
||
Разрешает файл внутри текущего проекта.
|
||
|
||
```js
|
||
api.resolve('src/main.js')
|
||
```
|
||
|
||
### getProject
|
||
|
||
Получает текущий открытый проект.
|
||
|
||
```js
|
||
api.getProject()
|
||
```
|
||
|
||
## Публичные статические файлы
|
||
|
||
Вам может потребоваться предоставлять некоторые статические файлы поверх встроенного HTTP-сервера cli-ui (обычно, чтобы указать значок для пользовательского представления).
|
||
|
||
Любой файл в опциональном каталоге `ui-public` в корневом каталоге пакета плагина станет доступен по HTTP-маршруту `/_plugin/:id/*`.
|
||
|
||
Например, если поместить файл `my-logo.png` в `vue-cli-plugin-hello/ui-public/`, он будет доступен по URL `/_plugin/vue-cli-plugin-hello/my-logo.png`, когда cli-ui загружает плагин.
|
||
|
||
```js
|
||
api.describeConfig({
|
||
/* ... */
|
||
// Пользовательское изображение
|
||
icon: '/_plugin/vue-cli-plugin-hello/my-logo.png'
|
||
})
|
||
```
|