Плагины и миксины
Плагины Vue
Плагин — объект с методом install. Добавляет глобальные функции, компоненты, директивы.
Простой плагин
plugins/logger.js:
export default {
install(app, options = {}) {
const prefix = options.prefix || '[App]';
app.config.globalProperties.$log = (message) => {
console.log(`${prefix} ${message}`);
};
},
};
main.js:
import logger from './plugins/logger';
app.use(logger, { prefix: '[Skills]' });
Использование в компоненте:
<script setup>
import { getCurrentInstance } from 'vue';
const instance = getCurrentInstance();
instance.appContext.config.globalProperties.$log('Hello');
</script>
В Options API:
export default {
mounted() {
this.$log('Hello');
},
};
Плагин с компонентом
import Button from './components/Button.vue';
export default {
install(app) {
app.component('MyButton', Button);
},
};
Использование:
<MyButton>Клик</MyButton>
Плагин с provide
Лучшая практика:
export default {
install(app, options) {
const state = reactive({
theme: 'dark',
});
const toggleTheme = () => {
state.theme = state.theme === 'dark' ? 'light' : 'dark';
};
app.provide('theme', { state, toggleTheme });
},
};
Потомок:
<script setup>
const { state, toggleTheme } = inject('theme');
</script>
Плагин с директивой
export default {
install(app) {
app.directive('focus', {
mounted(el) {
el.focus();
},
});
},
};
TypeScript
plugins/logger.ts:
import type { App } from 'vue';
interface Options {
prefix?: string;
}
export default {
install(app: App, options: Options = {}) {
const prefix = options.prefix || '[App]';
app.config.globalProperties.$log = (msg: string) => {
console.log(`${prefix} ${msg}`);
};
},
};
Расширение типов:
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$log: (message: string) => void;
}
}
Популярные плагины
- vue-router — роутинг
- pinia — состояние
- @vueuse/core — composables (много)
- vue-i18n — локализация
- vee-validate — формы
- floating-vue — tooltip, dropdown
Миксины (Options API)
Миксин — переиспользуемая логика для Options API компонентов.
⚠️ Устарели. Для Vue 3 — composables. Но знать стоит.
mixins/logger.js:
export default {
data() {
return { logCount: 0 };
},
methods: {
log(msg) {
this.logCount++;
console.log(msg);
},
},
mounted() {
console.log('Mixin mounted');
},
};
Компонент:
<script>
import logger from '@/mixins/logger';
export default {
mixins: [logger],
mounted() {
this.log('Component mounted');
},
};
</script>
Проблемы миксинов
1. Коллизии имён. Если два миксина имеют data.logCount — конфликт.
2. Неявные зависимости. Не видно, откуда метод.
3. Сложно типизировать (TypeScript).
4. Каша при 5+ миксинах.
Composables вместо миксинов
composables/useLogger.js:
import { ref } from 'vue';
export function useLogger() {
const logCount = ref(0);
function log(msg) {
logCount.value++;
console.log(msg);
}
return { logCount, log };
}
Компонент:
<script setup>
import { useLogger } from '@/composables/useLogger';
import { onMounted } from 'vue';
const { logCount, log } = useLogger();
onMounted(() => log('Component mounted'));
</script>
Плюсы:
- Явные зависимости
- Нет коллизий
- Работает с TS
- Легко тестируется
Правило: вместо миксинов — composables.
Глобальный миксин
⚠️ Не делай так:
app.mixin({
mounted() {
console.log('Каждый компонент');
},
});
Применяется ко всем компонентам. Отладка — ад.
Плагин-пример: уведомления
plugins/notifications.js:
import { reactive } from 'vue';
import Notification from '@/components/Notification.vue';
const state = reactive({
items: [],
});
export function useNotifications() {
function notify(message, type = 'info', timeout = 3000) {
const id = Date.now();
state.items.push({ id, message, type });
setTimeout(() => {
state.items = state.items.filter(n => n.id !== id);
}, timeout);
}
return {
items: state.items,
notify,
success: (msg) => notify(msg, 'success'),
error: (msg) => notify(msg, 'error'),
};
}
export default {
install(app) {
app.component('NotificationContainer', Notification);
},
};
Использование:
<script setup>
import { useNotifications } from '@/plugins/notifications';
const { success, error } = useNotifications();
function save() {
try {
success('Сохранено');
} catch {
error('Ошибка');
}
}
</script>
Плагин i18n вручную
import { ref, computed } from 'vue';
const locales = {
ru: { hello: 'Привет' },
en: { hello: 'Hello' },
};
const current = ref('ru');
export default {
install(app) {
app.provide('i18n', {
locale: current,
t: (key) => computed(() => locales[current.value]?.[key] ?? key),
setLocale: (l) => current.value = l,
});
},
};
Порядок плагинов
app
.use(createPinia())
.use(router)
.use(i18nPlugin, { locale: 'ru' })
.use(notificationsPlugin);
Порядок важен — если плагин использует другой, тот должен быть подключён первым.
Итог
- Плагин —
{ install(app, options) } app.use(plugin, options)— подключение- Внутри install: component, directive, provide, globalProperties
- Миксины устарели — используй composables
- TypeScript — расширяй
ComponentCustomProperties
Дальше — рендер-функции и JSX.