Создание собственных компонентов

Naive UI — это современная библиотека компонентов для Vue 3, ориентированная на удобство, производительность и лёгкость кастомизации. Одной из сильных сторон Naive UI является возможность создавать собственные компоненты, интегрирующиеся с её системой стилей и логики.

Создание собственного компонента начинается с импорта базовых функций Vue и компонентов Naive UI, если требуется их использование внутри нового компонента:

import { defineComponent, ref, computed } from 'vue';
import { NButton } from 'naive-ui';

Структура компонента

Стандартная структура компонента включает:

  1. Props — свойства, передаваемые извне, с их типами и значениями по умолчанию.
  2. Emits — события, которые компонент может испускать.
  3. Setup — функция для логики компонента.
  4. Render / Template — визуальное представление.

Пример базового компонента:

export default defineComponent({
  name: 'MyCustomButton',
  props: {
    label: {
      type: String,
      required: true
    },
    type: {
      type: String,
      default: 'primary'
    }
  },
  emits: ['click'],
  setup(props, { emit }) {
    const handleClick = () => {
      emit('click');
    };
    return { handleClick };
  },
  template: `
    <n-button :type="type" @click="handleClick">{{ label }}</n-button>
  `
});

В этом примере создаётся компонент MyCustomButton, который оборачивает стандартный NButton и добавляет обработку собственного события click.

Использование слотов для гибкости

Naive UI активно использует концепцию слотов для кастомизации содержимого компонентов. Слоты позволяют внедрять произвольное содержимое в заранее определённую структуру компонента:

export default defineComponent({
  name: 'CardWithHeader',
  props: {
    title: String
  },
  setup() {},
  template: `
    <div class="card">
      <div class="card-header">{{ title }}</div>
      <div class="card-body">
        <slot></slot>
      </div>
      <div class="card-footer">
        <slot name="footer"></slot>
      </div>
    </div>
  `
});
  • <slot> — основной слот для содержимого.
  • <slot name="footer"> — именованный слот для футера.

Такой подход позволяет комбинировать базовые стили Naive UI с произвольной логикой.

Реактивные состояния и композиция с Naive UI

Для управления состоянием в компоненте используется reactive, ref, computed. Важно сохранять совместимость с реактивностью Vue 3, чтобы компоненты Naive UI корректно реагировали на изменения:

export default defineComponent({
  name: 'ToggleSwitch',
  props: {
    modelValue: Boolean
  },
  emits: ['update:modelValue'],
  setup(props, { emit }) {
    const isChecked = ref(props.modelValue);

    const toggle = () => {
      isChecked.value = !isChecked.value;
      emit('update:modelValue', isChecked.value);
    };

    return { isChecked, toggle };
  },
  template: `
    <n-switch v-model:checked="isChecked" @update:checked="toggle" />
  `
});
  • v-model:checked связывает состояние компонента с реактивной переменной.
  • Использование emit('update:modelValue') обеспечивает поддержку двухсторонней привязки.

Кастомизация стилей через CSS-переменные

Naive UI поддерживает темизацию и CSS-переменные, что позволяет адаптировать внешний вид компонента без изменения исходного кода:

.my-card {
  --n-card-padding: 24px;
  --n-card-border-radius: 12px;
  background-color: var(--n-card-color, #fff);
}

Внутри компонента можно использовать классы с префиксом my- для изоляции стилей, сохраняя совместимость с глобальными стилями Naive UI.

Расширение существующих компонентов

В некоторых случаях требуется расширить стандартный компонент Naive UI. Это делается через композицию или наследование:

export default defineComponent({
  name: 'AdvancedButton',
  extends: NButton,
  props: {
    loading: Boolean
  },
  setup(props, { slots }) {
    return () => (
      <NButton type="primary" disabled={props.loading}>
        {props.loading ? 'Loading...' : slots.default?.()}
      </NButton>
    );
  }
});

Такой подход позволяет добавлять новые свойства, не переписывая базовый функционал.

Организация больших компонентов

При создании сложных компонентов рекомендуется:

  • Разделять логику на композиционные функции (composables).
  • Использовать подкомпоненты для повторяющихся частей интерфейса.
  • Применять TypeScript для строгой типизации props и emits.

Пример composable для управления формой:

import { reactive } from 'vue';

export function useForm(initialValues) {
  const state = reactive({ ...initialValues });
  const reset = () => Object.assign(state, initialValues);
  return { state, reset };
}

В компоненте форма подключается так:

const { state, reset } = useForm({ name: '', email: '' });

Итоговая схема интеграции

Собственный компонент Naive UI обычно сочетает:

  1. Props и Emits для внешнего взаимодействия.
  2. Reactivity для управления состоянием.
  3. Слоты для кастомизации содержимого.
  4. Темизацию и стили через CSS-переменные.
  5. Композицию с другими компонентами и composables.

Такой подход обеспечивает масштабируемость и повторное использование, а также сохраняет согласованность с экосистемой Naive UI.