Email: {{ userData.email }}
\nRole: {{ userData.role }}
\nОтвет сгенерирован нейросетью и может содержать ошибки
Да, безусловно смогу. За последние 6 лет я создавал десятки приложений на Vue — от простых виджетов до крупных корпоративных SPA. Vue мой основной фреймворк в стеке, и я прошел через все его основные версии (2.x, 3.x с Options и Composition API). Создание приложения с нуля — это стандартная задача, которую я выполняю регулярно.
Современный подход — использование Vite или create-vue (официальный шаблон). Предпочитаю Vite за скорость и гибкость:
npm create vue@latest
# или с Vite напрямую
npm create vite@latest my-vue-app -- --template vue
Для TypeScript добавляю флаг --typescript. Сразу настраиваю ESLint, Prettier и Pinia (замену Vuex).
Перед написанием кода определяю структуру:
<script setup>// Пример главного компонента App.vue
<script setup>
import { RouterView } from 'vue-router'
import AppHeader from '@/components/layout/AppHeader.vue'
import AppFooter from '@/components/layout/AppFooter.vue'
</script>
<template>
<div class="app">
<AppHeader />
<main class="main-content">
<RouterView />
</main>
<AppFooter />
</div>
</template>
<style scoped>
.app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
</style>
<!-- Пример компонента пользователя с Composition API -->
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useUserStore } from '@/stores/user'
import type { User } from '@/types/user'
interface Props {
userId: string
showDetails?: boolean
}
const props = withDefaults(defineProps<Props>(), {
showDetails: false
})
const emit = defineEmits<{
'user-updated': [user: User]
}>()
// Реактивные данные
const isLoading = ref(false)
const userData = ref<User | null>(null)
// Хранилище Pinia
const userStore = useUserStore()
// Вычисляемое свойство
const fullName = computed(() => {
if (!userData.value) return ''
return `${userData.value.firstName} ${userData.value.lastName}`
})
// Методы
const fetchUser = async () => {
isLoading.value = true
try {
userData.value = await userStore.fetchUser(props.userId)
emit('user-updated', userData.value)
} catch (error) {
console.error('Failed to fetch user:', error)
} finally {
isLoading.value = false
}
}
// Хуки жизненного цикла
onMounted(() => {
fetchUser()
})
// Экспорт методов при необходимости
defineExpose({
refreshUser: fetchUser
})
</script>
<template>
<div class="user-card" :class="{ loading: isLoading }">
<div v-if="isLoading" class="skeleton-loader">
<!-- Скелетон -->
</div>
<div v-else-if="userData" class="user-content">
<img
:src="userData.avatar"
:alt="fullName"
class="user-avatar"
/>
<h3>{{ fullName }}</h3>
<div v-if="showDetails" class="user-details">
<p>Email: {{ userData.email }}</p>
<p>Role: {{ userData.role }}</p>
</div>
<button @click="fetchUser" class="refresh-btn">
Обновить
</button>
</div>
<div v-else class="no-data">
Пользователь не найден
</div>
</div>
</template>
<style scoped>
.user-card {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
transition: all 0.3s ease;
}
.user-card.loading {
opacity: 0.7;
}
</style>
В реальном проекте я обязательно уделяю внимание:
TypeScript: Строгая типизация всех компонентов, хранилищ и утилит
Композиция функций: Разделение логики на переиспользуемые композабли
// useFetch.composable.ts
export function useFetch<T>(url: string) {
const data = ref<T | null>(null)
const error = ref<Error | null>(null)
const isLoading = ref(false)
const execute = async () => {
// Логика запроса
}
return { data, error, isLoading, execute }
}
Оптимизация производительности:
v-memo для тяжелых списковТестирование: Unit-тесты (Vitest + Vue Test Utils) и E2E (Cypress или Playwright)
CI/CD: Автоматические сборки, линтинг, тестирование и деплой
Для демонстрации реального опыта — реализация слоя API с интерсепторами, кешированием и отменой запросов:
// api/client.ts
import axios from 'axios'
import type { InternalAxiosRequestConfig, AxiosResponse } from 'axios'
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
// Интерсептор запросов
apiClient.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const token = localStorage.getItem('auth_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
// Интерсептор ответов
apiClient.interceptors.response.use(
(response: AxiosResponse) => response.data,
async (error) => {
if (error.response?.status === 401) {
// Обработка неавторизованного доступа
window.location.href = '/login'
}
return Promise.reject(error)
}
)
// Обертка с кешированием
export function createCachedApiCall<T>(key: string, fn: () => Promise<T>, ttl = 300000) {
const cache = ref<{ data: T | null; timestamp: number }>({
data: null,
timestamp: 0
})
return async (): Promise<T> => {
const now = Date.now()
if (cache.value.data && (now - cache.value.timestamp) < ttl) {
return cache.value.data
}
const data = await fn()
cache.value = { data, timestamp: now }
return data
}
}
export default apiClient
Я не просто "могу сделать приложение на Vue" — я могу построить профессиональное, масштабируемое, поддерживаемое и высокопроизводительное приложение, учитывая все лучшие практики и потенциальные требования бизнеса. Мой подход всегда начинается с архитектурных решений и заканчивается оптимизацией производительности и качественным деплоем.