Прежде чем использовать компонент в шаблоне, Vue должен о нём знать. Есть два способа: **глобальная** и **локальная** регистрация.
Глобально зарегистрированные компоненты доступны **в шаблонах любого компонента** без явного импорта:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import BaseButton from './components/BaseButton.vue'
import BaseInput from './components/BaseInput.vue'
import BaseModal from './components/BaseModal.vue'
const app = createApp(App)
// Регистрация глобальных компонентов
app.component('BaseButton', BaseButton)
app.component('BaseInput', BaseInput)
app.component('BaseModal', BaseModal)
app.mount('#app')<!-- В любом компоненте — без импорта -->
<template>
<BaseInput v-model="value" />
<BaseButton @click="submit">Отправить</BaseButton>
</template>Недостатки глобальной регистрации:
Компонент доступен только там, где он явно импортирован:
<!-- Options API -->
<script>
import MyComponent from './MyComponent.vue'
export default {
components: {
MyComponent
}
}
</script><!-- Composition API с <script setup> — достаточно импортировать -->
<script setup>
import MyComponent from './MyComponent.vue'
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<!-- MyComponent доступен автоматически -->
<MyComponent :count="count" />
</template>В <script setup> **импортированный компонент автоматически становится доступным** в шаблоне — регистрировать в components: {} не нужно.
В реальных проектах используют плагин unplugin-vue-components, который автоматически импортирует компоненты по имени из папки:
// vite.config.js
import Components from 'unplugin-vue-components/vite'
export default {
plugins: [
Components({
dirs: ['src/components'],
// Будет искать: BaseButton -> src/components/BaseButton.vue
})
]
}После этого в шаблонах не нужны никакие импорты — компоненты разрешаются автоматически.
Vue поддерживает несколько форматов имён компонентов в шаблоне:
<!-- PascalCase — рекомендуется для SFC, визуально отличает от HTML -->
<MyComponent />
<BaseButton />
<!-- kebab-case — работает всегда, обязателен в DOM-шаблонах -->
<my-component />
<base-button />Рекомендация: используйте PascalCase для имён компонентов везде в JavaScript/Vue-файлах.
Часто глобальные компоненты оформляют в виде плагина:
// plugins/ui.js
import BaseButton from '../components/BaseButton.vue'
import BaseInput from '../components/BaseInput.vue'
export default {
install(app) {
app.component('BaseButton', BaseButton)
app.component('BaseInput', BaseInput)
app.provide('uiConfig', { theme: 'default' })
}
}
// main.js
import UIPlugin from './plugins/ui.js'
app.use(UIPlugin)Эмуляция реестра компонентов Vue: глобальная/локальная регистрация и разрешение имён
// Эмулируем реестр компонентов Vue: регистрация и поиск по имени
// Глобальный реестр (аналог app.component())
class ComponentRegistry {
constructor() {
this._global = new Map()
}
// Глобальная регистрация
registerGlobal(name, definition) {
// Нормализуем имя: поддерживаем PascalCase и kebab-case
const normalized = this._normalize(name)
this._global.set(normalized, definition)
console.log(` [global] Зарегистрирован: "${name}" (ключ: "${normalized}")`)
}
// Создать контекст компонента с локальными регистрациями
createContext(localComponents = {}) {
const registry = this
const local = new Map(
Object.entries(localComponents).map(([k, v]) => [registry._normalize(k), v])
)
return {
resolve(name) {
const key = registry._normalize(name)
// Сначала ищем локально
if (local.has(key)) {
const comp = local.get(key)
console.log(` [resolve] "${name}" найден ЛОКАЛЬНО`)
return comp
}
// Затем глобально
if (registry._global.has(key)) {
const comp = registry._global.get(key)
console.log(` [resolve] "${name}" найден ГЛОБАЛЬНО`)
return comp
}
console.warn(` [resolve] "${name}" НЕ НАЙДЕН!`)
return null
},
listLocal() {
return [...local.keys()]
}
}
}
// PascalCase -> kebab-case для унификации
_normalize(name) {
return name
.replace(/([A-Z])/g, (_, c, i) => (i === 0 ? c.toLowerCase() : `-${c.toLowerCase()}`))
}
listGlobal() {
return [...this._global.keys()]
}
}
// Базовые компоненты (псевдо-определения)
const BaseButton = { name: 'BaseButton', template: '<button>...</button>' }
const BaseInput = { name: 'BaseInput', template: '<input>...' }
const BaseModal = { name: 'BaseModal', template: '<dialog>...</dialog>' }
const UserCard = { name: 'UserCard', template: '<div class="user-card">...</div>' }
const PaymentForm = { name: 'PaymentForm', template: '<form>...</form>' }
// === Настройка глобального реестра ===
console.log('=== Глобальная регистрация (app.component) ===')
const registry = new ComponentRegistry()
registry.registerGlobal('BaseButton', BaseButton)
registry.registerGlobal('BaseInput', BaseInput)
registry.registerGlobal('BaseModal', BaseModal)
console.log('Глобальные компоненты:', registry.listGlobal())
// === Локальная регистрация в компоненте ===
console.log('\n=== Локальная регистрация (<script setup> import) ===')
// Компонент UserProfile: импортирует UserCard локально
const userProfileContext = registry.createContext({
UserCard, // только здесь доступен UserCard
})
console.log('Локальные компоненты UserProfile:', userProfileContext.listLocal())
// === Разрешение компонентов ===
console.log('\n=== Разрешение компонентов ===')
// В UserProfile доступны: локальные + глобальные
userProfileContext.resolve('UserCard') // локальный
userProfileContext.resolve('BaseButton') // глобальный
userProfileContext.resolve('base-input') // kebab-case тоже работает
userProfileContext.resolve('PaymentForm') // не зарегистрирован -> предупреждение
// Компонент без локальных регистраций — только глобальные
console.log('\n=== Контекст без локальных компонентов ===')
const simpleContext = registry.createContext()
simpleContext.resolve('BaseModal') // глобальный
simpleContext.resolve('UserCard') // не найден
// === Паттерн: плагин ===
console.log('\n=== Паттерн: регистрация через плагин ===')
function UIPlugin(appRegistry) {
appRegistry.registerGlobal('BaseButton', BaseButton)
appRegistry.registerGlobal('BaseInput', BaseInput)
console.log(' UIPlugin установлен')
}
const newRegistry = new ComponentRegistry()
UIPlugin(newRegistry)
console.log('После установки UIPlugin:', newRegistry.listGlobal())Прежде чем использовать компонент в шаблоне, Vue должен о нём знать. Есть два способа: **глобальная** и **локальная** регистрация.
Глобально зарегистрированные компоненты доступны **в шаблонах любого компонента** без явного импорта:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import BaseButton from './components/BaseButton.vue'
import BaseInput from './components/BaseInput.vue'
import BaseModal from './components/BaseModal.vue'
const app = createApp(App)
// Регистрация глобальных компонентов
app.component('BaseButton', BaseButton)
app.component('BaseInput', BaseInput)
app.component('BaseModal', BaseModal)
app.mount('#app')<!-- В любом компоненте — без импорта -->
<template>
<BaseInput v-model="value" />
<BaseButton @click="submit">Отправить</BaseButton>
</template>Недостатки глобальной регистрации:
Компонент доступен только там, где он явно импортирован:
<!-- Options API -->
<script>
import MyComponent from './MyComponent.vue'
export default {
components: {
MyComponent
}
}
</script><!-- Composition API с <script setup> — достаточно импортировать -->
<script setup>
import MyComponent from './MyComponent.vue'
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<!-- MyComponent доступен автоматически -->
<MyComponent :count="count" />
</template>В <script setup> **импортированный компонент автоматически становится доступным** в шаблоне — регистрировать в components: {} не нужно.
В реальных проектах используют плагин unplugin-vue-components, который автоматически импортирует компоненты по имени из папки:
// vite.config.js
import Components from 'unplugin-vue-components/vite'
export default {
plugins: [
Components({
dirs: ['src/components'],
// Будет искать: BaseButton -> src/components/BaseButton.vue
})
]
}После этого в шаблонах не нужны никакие импорты — компоненты разрешаются автоматически.
Vue поддерживает несколько форматов имён компонентов в шаблоне:
<!-- PascalCase — рекомендуется для SFC, визуально отличает от HTML -->
<MyComponent />
<BaseButton />
<!-- kebab-case — работает всегда, обязателен в DOM-шаблонах -->
<my-component />
<base-button />Рекомендация: используйте PascalCase для имён компонентов везде в JavaScript/Vue-файлах.
Часто глобальные компоненты оформляют в виде плагина:
// plugins/ui.js
import BaseButton from '../components/BaseButton.vue'
import BaseInput from '../components/BaseInput.vue'
export default {
install(app) {
app.component('BaseButton', BaseButton)
app.component('BaseInput', BaseInput)
app.provide('uiConfig', { theme: 'default' })
}
}
// main.js
import UIPlugin from './plugins/ui.js'
app.use(UIPlugin)Эмуляция реестра компонентов Vue: глобальная/локальная регистрация и разрешение имён
// Эмулируем реестр компонентов Vue: регистрация и поиск по имени
// Глобальный реестр (аналог app.component())
class ComponentRegistry {
constructor() {
this._global = new Map()
}
// Глобальная регистрация
registerGlobal(name, definition) {
// Нормализуем имя: поддерживаем PascalCase и kebab-case
const normalized = this._normalize(name)
this._global.set(normalized, definition)
console.log(` [global] Зарегистрирован: "${name}" (ключ: "${normalized}")`)
}
// Создать контекст компонента с локальными регистрациями
createContext(localComponents = {}) {
const registry = this
const local = new Map(
Object.entries(localComponents).map(([k, v]) => [registry._normalize(k), v])
)
return {
resolve(name) {
const key = registry._normalize(name)
// Сначала ищем локально
if (local.has(key)) {
const comp = local.get(key)
console.log(` [resolve] "${name}" найден ЛОКАЛЬНО`)
return comp
}
// Затем глобально
if (registry._global.has(key)) {
const comp = registry._global.get(key)
console.log(` [resolve] "${name}" найден ГЛОБАЛЬНО`)
return comp
}
console.warn(` [resolve] "${name}" НЕ НАЙДЕН!`)
return null
},
listLocal() {
return [...local.keys()]
}
}
}
// PascalCase -> kebab-case для унификации
_normalize(name) {
return name
.replace(/([A-Z])/g, (_, c, i) => (i === 0 ? c.toLowerCase() : `-${c.toLowerCase()}`))
}
listGlobal() {
return [...this._global.keys()]
}
}
// Базовые компоненты (псевдо-определения)
const BaseButton = { name: 'BaseButton', template: '<button>...</button>' }
const BaseInput = { name: 'BaseInput', template: '<input>...' }
const BaseModal = { name: 'BaseModal', template: '<dialog>...</dialog>' }
const UserCard = { name: 'UserCard', template: '<div class="user-card">...</div>' }
const PaymentForm = { name: 'PaymentForm', template: '<form>...</form>' }
// === Настройка глобального реестра ===
console.log('=== Глобальная регистрация (app.component) ===')
const registry = new ComponentRegistry()
registry.registerGlobal('BaseButton', BaseButton)
registry.registerGlobal('BaseInput', BaseInput)
registry.registerGlobal('BaseModal', BaseModal)
console.log('Глобальные компоненты:', registry.listGlobal())
// === Локальная регистрация в компоненте ===
console.log('\n=== Локальная регистрация (<script setup> import) ===')
// Компонент UserProfile: импортирует UserCard локально
const userProfileContext = registry.createContext({
UserCard, // только здесь доступен UserCard
})
console.log('Локальные компоненты UserProfile:', userProfileContext.listLocal())
// === Разрешение компонентов ===
console.log('\n=== Разрешение компонентов ===')
// В UserProfile доступны: локальные + глобальные
userProfileContext.resolve('UserCard') // локальный
userProfileContext.resolve('BaseButton') // глобальный
userProfileContext.resolve('base-input') // kebab-case тоже работает
userProfileContext.resolve('PaymentForm') // не зарегистрирован -> предупреждение
// Компонент без локальных регистраций — только глобальные
console.log('\n=== Контекст без локальных компонентов ===')
const simpleContext = registry.createContext()
simpleContext.resolve('BaseModal') // глобальный
simpleContext.resolve('UserCard') // не найден
// === Паттерн: плагин ===
console.log('\n=== Паттерн: регистрация через плагин ===')
function UIPlugin(appRegistry) {
appRegistry.registerGlobal('BaseButton', BaseButton)
appRegistry.registerGlobal('BaseInput', BaseInput)
console.log(' UIPlugin установлен')
}
const newRegistry = new ComponentRegistry()
UIPlugin(newRegistry)
console.log('После установки UIPlugin:', newRegistry.listGlobal())Напиши функцию `createComponentResolver(globalComponents, localComponents)`, которая принимает два объекта с компонентами и возвращает функцию `resolve(name)`. Функция должна сначала искать по имени в `localComponents`, затем в `globalComponents`. Поиск должен поддерживать оба формата имён: PascalCase и kebab-case (например, `"BaseButton"` и `"base-button"` — это одно и то же).
В `toKebabCase` используй `.replace(/([A-Z])/g, (c, i) => i === 0 ? c.toLowerCase() : "-" + c.toLowerCase())`. В `resolve` попробуй оба варианта имени: оригинальный и kebab. Проверяй `obj[name] || obj[kebab]`.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке