Расширение базовых компонентов

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


1. Компонентный подход к расширению

В Naive UI каждый компонент реализован как Vue-компонент с хорошо структурированными props, slots и events. Расширение базового компонента может происходить несколькими способами:

  1. Композиция (Composition) Использование базового компонента внутри нового, оборачивая его дополнительной логикой или стилями. Этот подход безопасен и сохраняет все внутренние возможности компонента.

    <template>
      <n-button :type="type" @click="handleClick">
        <slot />
      </n-button>
    </template>
    
    <script setup>
    import { NButton } from 'naive-ui'
    
    defineProps({
      type: {
        type: String,
        default: 'primary'
      }
    })
    
    const handleClick = () => {
      console.log('Кастомный клик!')
    }
    </script>

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

  2. Использование extends Можно напрямую расширять компонент через опцию extends в Vue. Этот метод удобен для переопределения props и методов:

    import { NButton } from 'naive-ui'
    
    export default {
      extends: NButton,
      methods: {
        handleClick() {
          console.log('Расширенный клик');
          this.$emit('click')
        }
      }
    }

    Такой подход позволяет полностью контролировать внутреннее поведение компонента, но требует аккуратного обращения с внутренними методами и состояниями.


2. Кастомизация стилей через style и class

Naive UI использует CSS Variables для стилизации. Расширяя компонент, можно переопределять стили через props class и style, а также через provideTheme для глобальных изменений:

<template>
  <n-button class="my-button">
    Расширенная кнопка
  </n-button>
</template>

<style scoped>
.my-button {
  background-color: #4caf50;
  color: white;
  border-radius: 12px;
}
</style>

Для более глубокой кастомизации компонентов Naive UI предоставляет возможность работы с theme overrides:

import { createTheme, NButton } from 'naive-ui'

const customTheme = createTheme({
  Button: {
    borderRadius: '16px',
    colorPrimary: '#ff5722'
  }
})

app.use(NButton, { theme: customTheme })

Такой подход позволяет создавать единый визуальный стиль для всех компонентов приложения без дублирования CSS.


3. Расширение функционала через слоты

Слоты — ключевой механизм расширения компонентов. Они позволяют вставлять собственный контент внутрь базового компонента, не меняя его внутреннюю логику.

Пример расширенной панели с пользовательским заголовком:

<template>
  <n-card>
    <template #header>
      <h3>Кастомный заголовок</h3>
    </template>
    <p>Содержимое карточки</p>
  </n-card>
</template>

<script setup>
import { NCard } from 'naive-ui'
</script>

Слоты можно комбинировать с props и events, создавая полностью кастомизированные компоненты, которые остаются совместимыми с Naive UI.


4. Создание оберток с дополнительной логикой

Иногда требуется добавить глобальную логику к стандартным компонентам, например, обработку ошибок, анимацию или асинхронные действия. Для этого создаются обертки:

<template>
  <n-button :loading="loading" @click="handleAsyncClick">
    <slot />
  </n-button>
</template>

<script setup>
import { ref } from 'vue'
import { NButton } from 'naive-ui'

const loading = ref(false)

const handleAsyncClick = async () => {
  loading.value = true
  await new Promise(resolve => setTimeout(resolve, 2000))
  loading.value = false
  console.log('Асинхронная операция завершена')
}
</script>

В этом примере расширенный NButton автоматически обрабатывает асинхронные действия, не затрагивая исходный API кнопки.


5. Расширение через Composition API

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

// useCustomButton.js
import { ref } from 'vue'

export function useCustomButton() {
  const clicked = ref(false)
  
  const handleClick = () => {
    clicked.value = !clicked.value
    console.log('Состояние кнопки:', clicked.value)
  }

  return { clicked, handleClick }
}
<template>
  <n-button @click="handleClick">
    {{ clicked ? 'Активна' : 'Не активна' }}
  </n-button>
</template>

<script setup>
import { useCustomButton } from './useCustomButton'
import { NButton } from 'naive-ui'

const { clicked, handleClick } = useCustomButton()
</script>

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


6. Взаимодействие с TypeScript

Naive UI полностью поддерживает TypeScript, и расширение компонентов с типами повышает надежность кода:

import { NButton, ButtonProps } from 'naive-ui'
import { defineComponent } from 'vue'

export default defineComponent({
  extends: NButton,
  props: {
    ...NButton.props,
    customProp: {
      type: Boolean,
      default: false
    }
  },
  setup(props: ButtonProps & { customProp: boolean }) {
    return () => <NButton {...props}>Расширенная кнопка</NButton>
  }
})

Использование TypeScript позволяет проверять совместимость пропсов и предотвращать ошибки при расширении базовых компонентов.


Расширение базовых компонентов Naive UI объединяет композицию, слоты, кастомные темы и логические обертки, создавая мощные, повторно используемые и гибкие элементы интерфейса. Это позволяет строить сложные интерфейсы с единым стилем и поведением, не теряя преимуществ готовых решений библиотеки.