Vue.js — это **прогрессивный JavaScript-фреймворк** для создания пользовательских интерфейсов. Он называется прогрессивным, потому что его можно внедрять постепенно: начать с подключения через CDN для одной страницы и дорасти до полноценного SPA (Single Page Application).
Vue 3 вышел в 2020 году и принёс Composition API, значительно улучшенную TypeScript-поддержку и более производительный рантайм.
Главная идея Vue — **реактивность**: данные связаны с DOM, и при изменении данных DOM обновляется автоматически.
В ванильном JS вам нужно делать это вручную:
// Vanilla JS — всё вручную
let count = 0
const button = document.getElementById('btn')
const display = document.getElementById('count')
button.addEventListener('click', () => {
count++
display.textContent = count // нужно вручную обновить DOM
})В Vue это происходит автоматически:
<!-- Vue — данные и DOM синхронизированы автоматически -->
<template>
<button @click="count++">Нажми меня</button>
<p>Счётчик: {{ count }}</p>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>Vue использует **виртуальный DOM (VDOM)** — легковесное JavaScript-представление настоящего DOM-дерева. Когда данные меняются, Vue:
1. Создаёт новый виртуальный DOM
2. Сравнивает его со старым (алгоритм **diffing**)
3. Применяет только необходимые изменения к реальному DOM (процесс **patching**)
Это значительно эффективнее, чем пересоздавать весь DOM при каждом изменении.
Vue-приложения состоят из компонентов. Каждый компонент — это .vue файл с тремя секциями:
<!-- MyComponent.vue -->
<template>
<!-- HTML-разметка компонента -->
<div class="card">
<h2>{{ title }}</h2>
<p v-if="isVisible">Видимый текст</p>
<button @click="handleClick">Кнопка</button>
</div>
</template>
<script setup>
// JavaScript логика (Composition API)
import { ref } from 'vue'
const title = ref('Мой компонент')
const isVisible = ref(true)
function handleClick() {
isVisible.value = !isVisible.value
}
</script>
<style scoped>
/* CSS стили (scoped — только для этого компонента) */
.card {
padding: 16px;
border: 1px solid #ccc;
}
</style>Начать работу с Vue можно без сборщиков — просто подключив CDN:
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
<button @click="message = 'Привет, Vue!'">Изменить</button>
</div>
<script>
const { createApp, ref } = Vue
createApp({
setup() {
const message = ref('Привет, мир!')
return { message }
}
}).mount('#app')
</script>
</body>
</html>В основе реактивности Vue лежит JavaScript **Proxy** — объект-обёртка, который перехватывает операции чтения и записи свойств. Когда вы читаете свойство реактивного объекта, Vue запоминает зависимость. Когда пишете — Vue знает, что нужно обновить все зависящие части интерфейса.
Эмуляция реактивности через Proxy — как Vue отслеживает изменения "под капотом"
// Vue использует Proxy для отслеживания изменений в данных.
// Разберём как это работает на упрощённом примере.
// Хранилище текущего эффекта (функции, которая "следит" за данными)
let activeEffect = null
// Хранилище зависимостей: { объект -> { свойство -> Set эффектов } }
const deps = new WeakMap()
// Получить набор зависимостей для конкретного объекта и свойства
function getDep(target, key) {
if (!deps.has(target)) {
deps.set(target, new Map())
}
const targetDeps = deps.get(target)
if (!targetDeps.has(key)) {
targetDeps.set(key, new Set())
}
return targetDeps.get(key)
}
// Создать реактивный объект через Proxy
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
// Если есть активный эффект — запоминаем зависимость
if (activeEffect) {
const dep = getDep(target, key)
dep.add(activeEffect)
console.log(` [get] ${String(key)} — подписан эффект`)
}
return target[key]
},
set(target, key, value) {
const oldValue = target[key]
target[key] = value
console.log(`[set] ${String(key)}: ${oldValue} -> ${value}`)
// Уведомляем все зависящие эффекты
const dep = getDep(target, key)
dep.forEach(effect => effect())
return true
}
})
}
// Запустить эффект и отследить зависимости
function watchEffect(fn) {
activeEffect = fn
fn() // первый запуск — собираем зависимости
activeEffect = null
}
// --- Демонстрация ---
const state = reactive({ name: 'Алексей', age: 25 })
console.log('=== Регистрируем эффект ===')
watchEffect(() => {
// Эта функция зависит от state.name и state.age
console.log(`Пользователь: ${state.name}, возраст: ${state.age}`)
})
console.log('\n=== Меняем name ===')
state.name = 'Борис'
// Эффект автоматически перезапустится
console.log('\n=== Меняем age ===')
state.age = 30
// Эффект снова перезапустится
console.log('\nИтого: при каждом изменении данных Vue знает')
console.log('какие части интерфейса нужно перерендерить.')Vue.js — это **прогрессивный JavaScript-фреймворк** для создания пользовательских интерфейсов. Он называется прогрессивным, потому что его можно внедрять постепенно: начать с подключения через CDN для одной страницы и дорасти до полноценного SPA (Single Page Application).
Vue 3 вышел в 2020 году и принёс Composition API, значительно улучшенную TypeScript-поддержку и более производительный рантайм.
Главная идея Vue — **реактивность**: данные связаны с DOM, и при изменении данных DOM обновляется автоматически.
В ванильном JS вам нужно делать это вручную:
// Vanilla JS — всё вручную
let count = 0
const button = document.getElementById('btn')
const display = document.getElementById('count')
button.addEventListener('click', () => {
count++
display.textContent = count // нужно вручную обновить DOM
})В Vue это происходит автоматически:
<!-- Vue — данные и DOM синхронизированы автоматически -->
<template>
<button @click="count++">Нажми меня</button>
<p>Счётчик: {{ count }}</p>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>Vue использует **виртуальный DOM (VDOM)** — легковесное JavaScript-представление настоящего DOM-дерева. Когда данные меняются, Vue:
1. Создаёт новый виртуальный DOM
2. Сравнивает его со старым (алгоритм **diffing**)
3. Применяет только необходимые изменения к реальному DOM (процесс **patching**)
Это значительно эффективнее, чем пересоздавать весь DOM при каждом изменении.
Vue-приложения состоят из компонентов. Каждый компонент — это .vue файл с тремя секциями:
<!-- MyComponent.vue -->
<template>
<!-- HTML-разметка компонента -->
<div class="card">
<h2>{{ title }}</h2>
<p v-if="isVisible">Видимый текст</p>
<button @click="handleClick">Кнопка</button>
</div>
</template>
<script setup>
// JavaScript логика (Composition API)
import { ref } from 'vue'
const title = ref('Мой компонент')
const isVisible = ref(true)
function handleClick() {
isVisible.value = !isVisible.value
}
</script>
<style scoped>
/* CSS стили (scoped — только для этого компонента) */
.card {
padding: 16px;
border: 1px solid #ccc;
}
</style>Начать работу с Vue можно без сборщиков — просто подключив CDN:
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
<button @click="message = 'Привет, Vue!'">Изменить</button>
</div>
<script>
const { createApp, ref } = Vue
createApp({
setup() {
const message = ref('Привет, мир!')
return { message }
}
}).mount('#app')
</script>
</body>
</html>В основе реактивности Vue лежит JavaScript **Proxy** — объект-обёртка, который перехватывает операции чтения и записи свойств. Когда вы читаете свойство реактивного объекта, Vue запоминает зависимость. Когда пишете — Vue знает, что нужно обновить все зависящие части интерфейса.
Эмуляция реактивности через Proxy — как Vue отслеживает изменения "под капотом"
// Vue использует Proxy для отслеживания изменений в данных.
// Разберём как это работает на упрощённом примере.
// Хранилище текущего эффекта (функции, которая "следит" за данными)
let activeEffect = null
// Хранилище зависимостей: { объект -> { свойство -> Set эффектов } }
const deps = new WeakMap()
// Получить набор зависимостей для конкретного объекта и свойства
function getDep(target, key) {
if (!deps.has(target)) {
deps.set(target, new Map())
}
const targetDeps = deps.get(target)
if (!targetDeps.has(key)) {
targetDeps.set(key, new Set())
}
return targetDeps.get(key)
}
// Создать реактивный объект через Proxy
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
// Если есть активный эффект — запоминаем зависимость
if (activeEffect) {
const dep = getDep(target, key)
dep.add(activeEffect)
console.log(` [get] ${String(key)} — подписан эффект`)
}
return target[key]
},
set(target, key, value) {
const oldValue = target[key]
target[key] = value
console.log(`[set] ${String(key)}: ${oldValue} -> ${value}`)
// Уведомляем все зависящие эффекты
const dep = getDep(target, key)
dep.forEach(effect => effect())
return true
}
})
}
// Запустить эффект и отследить зависимости
function watchEffect(fn) {
activeEffect = fn
fn() // первый запуск — собираем зависимости
activeEffect = null
}
// --- Демонстрация ---
const state = reactive({ name: 'Алексей', age: 25 })
console.log('=== Регистрируем эффект ===')
watchEffect(() => {
// Эта функция зависит от state.name и state.age
console.log(`Пользователь: ${state.name}, возраст: ${state.age}`)
})
console.log('\n=== Меняем name ===')
state.name = 'Борис'
// Эффект автоматически перезапустится
console.log('\n=== Меняем age ===')
state.age = 30
// Эффект снова перезапустится
console.log('\nИтого: при каждом изменении данных Vue знает')
console.log('какие части интерфейса нужно перерендерить.')Реализуй функцию `reactive(obj)`, которая возвращает Proxy-обёртку над объектом. При изменении любого свойства должен вызываться переданный callback с аргументами (key, newValue, oldValue). Функция должна принимать два аргумента: объект и функцию-callback.
Используй new Proxy(obj, handler) где handler — объект с методом set(target, key, value). Внутри set сохрани старое значение через const oldVal = target[key], затем обнови target[key] = value, вызови callback(key, value, oldVal) и верни true.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке