В больших компонентах с Options API код одной фичи разбросан по разным секциям (data, methods, computed, watch). Это затрудняет чтение и переиспользование логики.
// Options API — логика фичи «поиск» разбросана по всему компоненту
export default {
data() {
return {
// данные поиска
searchQuery: '',
searchResults: [],
searchLoading: false,
// данные пагинации
currentPage: 1,
totalPages: 0,
}
},
methods: {
// методы поиска
async doSearch() { /* ... */ },
clearSearch() { /* ... */ },
// методы пагинации
nextPage() { /* ... */ },
prevPage() { /* ... */ },
},
watch: {
searchQuery: 'doSearch', // наблюдение за поиском
currentPage: 'doSearch', // наблюдение за пагинацией
}
}setup() запускается до монтирования компонента. Всё, что возвращается из setup, доступно в шаблоне:
import { ref, computed, watch, onMounted } from 'vue'
export default {
setup() {
// Состояние (аналог data)
const count = ref(0)
const name = ref('Vue')
// Вычисляемые значения (аналог computed)
const doubled = computed(() => count.value * 2)
const greeting = computed(() => `Hello, ${name.value}!`)
// Методы (аналог methods)
function increment() {
count.value++
}
// Lifecycle (аналог mounted)
onMounted(() => {
console.log('Компонент смонтирован')
})
// Наблюдатели (аналог watch)
watch(count, (newVal, oldVal) => {
console.log(`count изменился: ${oldVal} → ${newVal}`)
})
// Возвращаем что нужно в шаблоне
return { count, name, doubled, greeting, increment }
}
}// OPTIONS API
export default {
data() { return { count: 0 } },
computed: {
doubled() { return this.count * 2 }
},
methods: {
increment() { this.count++ }
},
mounted() {
console.log('ready')
}
}
// COMPOSITION API (эквивалент)
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
const count = ref(0)
const doubled = computed(() => count.value * 2)
const increment = () => count.value++
onMounted(() => console.log('ready'))
return { count, doubled, increment }
}
}<!-- Не нужен явный return — всё автоматически доступно в шаблоне -->
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
const increment = () => count.value++
</script>Главное преимущество Composition API — возможность вынести логику в **composables** (функции use*):
// useSearch.js — вся логика поиска в одном месте
export function useSearch() {
const query = ref('')
const results = ref([])
const loading = ref(false)
async function search() {
if (!query.value) return
loading.value = true
results.value = await fetchSearch(query.value)
loading.value = false
}
return { query, results, loading, search }
}
// Компонент — чистый и читаемый
export default {
setup() {
const { query, results, loading, search } = useSearch()
const { currentPage, nextPage } = usePagination(results)
return { query, results, loading, search, currentPage, nextPage }
}
}Composable useCounter через замыкание — аналог Composition API в чистом JS
// Composable через замыкание — аналог ref + методов в Vue 3
function useCounter(initialValue = 0, { min = -Infinity, max = Infinity } = {}) {
// Закрытое состояние (аналог ref внутри composable)
let count = initialValue
function clamp(value) {
return Math.min(max, Math.max(min, value))
}
return {
// Геттер (аналог count.value)
get value() { return count },
// Методы (аналог функций, возвращаемых из setup)
increment(step = 1) {
count = clamp(count + step)
return this
},
decrement(step = 1) {
count = clamp(count - step)
return this
},
reset() {
count = initialValue
return this
},
set(value) {
count = clamp(value)
return this
},
}
}
// Аналог использования в компоненте:
// const { count, increment, decrement } = useCounter(0, { min: 0, max: 10 })
const counter = useCounter(0, { min: 0, max: 10 })
console.log(counter.value) // 0
counter.increment()
counter.increment()
counter.increment(3)
console.log(counter.value) // 5
counter.increment(100) // зажато на max=10
console.log(counter.value) // 10
counter.decrement(3)
console.log(counter.value) // 7
counter.reset()
console.log(counter.value) // 0
// Можно комбинировать composables — как в Vue setup()
function useToggle(initialValue = false) {
let state = initialValue
return {
get value() { return state },
toggle() { state = !state; return this },
setTrue() { state = true; return this },
setFalse() { state = false; return this },
}
}
// "Компонент" — комбинирует composables как в setup()
function createComponent() {
const counter = useCounter(0, { min: 0, max: 5 })
const isVisible = useToggle(true)
return {
get count() { return counter.value },
get visible() { return isVisible.value },
increment: () => counter.increment(),
toggleVisibility: () => isVisible.toggle(),
render() {
return isVisible.value
? `Счётчик: ${counter.value}`
: '(скрыто)'
}
}
}
const comp = createComponent()
console.log(comp.render()) // Счётчик: 0
comp.increment()
comp.increment()
console.log(comp.render()) // Счётчик: 2
comp.toggleVisibility()
console.log(comp.render()) // (скрыто)В больших компонентах с Options API код одной фичи разбросан по разным секциям (data, methods, computed, watch). Это затрудняет чтение и переиспользование логики.
// Options API — логика фичи «поиск» разбросана по всему компоненту
export default {
data() {
return {
// данные поиска
searchQuery: '',
searchResults: [],
searchLoading: false,
// данные пагинации
currentPage: 1,
totalPages: 0,
}
},
methods: {
// методы поиска
async doSearch() { /* ... */ },
clearSearch() { /* ... */ },
// методы пагинации
nextPage() { /* ... */ },
prevPage() { /* ... */ },
},
watch: {
searchQuery: 'doSearch', // наблюдение за поиском
currentPage: 'doSearch', // наблюдение за пагинацией
}
}setup() запускается до монтирования компонента. Всё, что возвращается из setup, доступно в шаблоне:
import { ref, computed, watch, onMounted } from 'vue'
export default {
setup() {
// Состояние (аналог data)
const count = ref(0)
const name = ref('Vue')
// Вычисляемые значения (аналог computed)
const doubled = computed(() => count.value * 2)
const greeting = computed(() => `Hello, ${name.value}!`)
// Методы (аналог methods)
function increment() {
count.value++
}
// Lifecycle (аналог mounted)
onMounted(() => {
console.log('Компонент смонтирован')
})
// Наблюдатели (аналог watch)
watch(count, (newVal, oldVal) => {
console.log(`count изменился: ${oldVal} → ${newVal}`)
})
// Возвращаем что нужно в шаблоне
return { count, name, doubled, greeting, increment }
}
}// OPTIONS API
export default {
data() { return { count: 0 } },
computed: {
doubled() { return this.count * 2 }
},
methods: {
increment() { this.count++ }
},
mounted() {
console.log('ready')
}
}
// COMPOSITION API (эквивалент)
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
const count = ref(0)
const doubled = computed(() => count.value * 2)
const increment = () => count.value++
onMounted(() => console.log('ready'))
return { count, doubled, increment }
}
}<!-- Не нужен явный return — всё автоматически доступно в шаблоне -->
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
const increment = () => count.value++
</script>Главное преимущество Composition API — возможность вынести логику в **composables** (функции use*):
// useSearch.js — вся логика поиска в одном месте
export function useSearch() {
const query = ref('')
const results = ref([])
const loading = ref(false)
async function search() {
if (!query.value) return
loading.value = true
results.value = await fetchSearch(query.value)
loading.value = false
}
return { query, results, loading, search }
}
// Компонент — чистый и читаемый
export default {
setup() {
const { query, results, loading, search } = useSearch()
const { currentPage, nextPage } = usePagination(results)
return { query, results, loading, search, currentPage, nextPage }
}
}Composable useCounter через замыкание — аналог Composition API в чистом JS
// Composable через замыкание — аналог ref + методов в Vue 3
function useCounter(initialValue = 0, { min = -Infinity, max = Infinity } = {}) {
// Закрытое состояние (аналог ref внутри composable)
let count = initialValue
function clamp(value) {
return Math.min(max, Math.max(min, value))
}
return {
// Геттер (аналог count.value)
get value() { return count },
// Методы (аналог функций, возвращаемых из setup)
increment(step = 1) {
count = clamp(count + step)
return this
},
decrement(step = 1) {
count = clamp(count - step)
return this
},
reset() {
count = initialValue
return this
},
set(value) {
count = clamp(value)
return this
},
}
}
// Аналог использования в компоненте:
// const { count, increment, decrement } = useCounter(0, { min: 0, max: 10 })
const counter = useCounter(0, { min: 0, max: 10 })
console.log(counter.value) // 0
counter.increment()
counter.increment()
counter.increment(3)
console.log(counter.value) // 5
counter.increment(100) // зажато на max=10
console.log(counter.value) // 10
counter.decrement(3)
console.log(counter.value) // 7
counter.reset()
console.log(counter.value) // 0
// Можно комбинировать composables — как в Vue setup()
function useToggle(initialValue = false) {
let state = initialValue
return {
get value() { return state },
toggle() { state = !state; return this },
setTrue() { state = true; return this },
setFalse() { state = false; return this },
}
}
// "Компонент" — комбинирует composables как в setup()
function createComponent() {
const counter = useCounter(0, { min: 0, max: 5 })
const isVisible = useToggle(true)
return {
get count() { return counter.value },
get visible() { return isVisible.value },
increment: () => counter.increment(),
toggleVisibility: () => isVisible.toggle(),
render() {
return isVisible.value
? `Счётчик: ${counter.value}`
: '(скрыто)'
}
}
}
const comp = createComponent()
console.log(comp.render()) // Счётчик: 0
comp.increment()
comp.increment()
console.log(comp.render()) // Счётчик: 2
comp.toggleVisibility()
console.log(comp.render()) // (скрыто)Реализуй composable `useLocalStorage(key, defaultValue)` — функцию, которая возвращает объект для работы с «localStorage», используя Map как хранилище (вместо реального localStorage). Возвращаемый объект должен содержать: - геттер `value` — читает текущее значение из хранилища (или defaultValue если ключ отсутствует) - `setValue(v)` — записывает v в хранилище по key - `removeValue()` — удаляет ключ из хранилища Хранилище — один общий Map на все экземпляры (вне функции). ``` const storage = useLocalStorage('theme', 'light') console.log(storage.value) // 'light' (defaultValue) storage.setValue('dark') console.log(storage.value) // 'dark' storage.removeValue() console.log(storage.value) // 'light' (снова defaultValue) ```
store — это Map вне функции, общая для всех вызовов. В геттере value: return store.has(key) ? store.get(key) : defaultValue. В setValue: store.set(key, v). В removeValue: store.delete(key).
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке