Перейти к содержимому

9. Vue в Astro

Astro поддерживает Vue 3 из коробки через официальную интеграцию. Вы можете использовать Vue Single File Components (.vue) прямо в проекте, смешивая их с Astro и другими фреймворками на одной странице.

Окно терминала
npx astro add vue

Команда автоматически:

  • Устанавливает @astrojs/vue и vue
  • Обновляет astro.config.mjs
  • Настраивает компилятор Vue SFC
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
integrations: [vue()],
});

Vue SFCs работают точно так же, как в обычном Vue-проекте:

src/components/VueCounter.vue
<script setup>
import { ref } from 'vue';
const props = defineProps({
initialCount: { type: Number, default: 0 }
});
const count = ref(props.initialCount);
const increment = () => count.value++;
const decrement = () => count.value--;
</script>
<template>
<div class="counter">
<button @click="decrement">−</button>
<span>{{ count }}</span>
<button @click="increment">+</button>
</div>
</template>
<style scoped>
.counter {
display: flex;
align-items: center;
gap: 12px;
}
</style>
---
import VueCounter from '../components/VueCounter.vue';
import VueProductCard from '../components/VueProductCard.vue';
---
<html>
<body>
<h1>Магазин</h1>
<!-- Vue Islands — с директивами гидратации -->
<VueCounter initialCount={5} client:load />
<VueProductCard product={product} client:visible />
</body>
</html>
src/components/VueSearchBar.vue
<script setup>
import { ref, computed, watch } from 'vue';
const props = defineProps({
placeholder: String,
items: Array,
});
const query = ref('');
const results = computed(() =>
(props.items || []).filter(item =>
item.toLowerCase().includes(query.value.toLowerCase())
)
);
watch(query, (val) => {
if (val.length > 2) {
// Отправить аналитику
console.log('Поиск:', val);
}
});
</script>
<template>
<div>
<input v-model="query" :placeholder="placeholder" />
<ul>
<li v-for="item in results" :key="item">{{ item }}</li>
</ul>
</div>
</template>
---
import VueProductList from '../components/VueProductList.vue';
const products = await fetch('https://api.example.com/products').then(r => r.json());
---
<!-- Props сериализуются и передаются в Vue как обычные props -->
<VueProductList
client:visible
:products={products}
title="Популярные товары"
maxItems={6}
/>
src/components/VueShoppingCart.vue
<script setup>
import { ref, computed } from 'vue';
const items = ref([]);
const total = computed(() =>
items.value.reduce((sum, item) => sum + item.price * item.qty, 0)
);
const addItem = (product) => {
const existing = items.value.find(i => i.id === product.id);
if (existing) {
existing.qty++;
} else {
items.value.push({ ...product, qty: 1 });
}
};
</script>
<template>
<div class="cart">
<h3>Корзина ({{ items.length }})</h3>
<div v-for="item in items" :key="item.id">
{{ item.name }} × {{ item.qty }}
</div>
<strong>Итого: {{ total }}₽</strong>
</div>
</template>

Astro позволяет использовать несколько фреймворков одновременно. Каждый Island изолирован и управляет своим состоянием независимо:

---
import ReactSearchBar from '../components/ReactSearchBar.jsx';
import VueProductCard from '../components/VueProductCard.vue';
import SvelteRatingWidget from '../components/SvelteRating.svelte';
---
<!-- Все три фреймворка на одной странице! -->
<ReactSearchBar client:load />
<VueProductCard client:visible product={product} />
<SvelteRatingWidget client:idle productId={product.id} />