Реактивность описывает ситуацию, при которой изменения в состоянии приложения автоматически отображаются в DOM.
Vue - это прогрессивный фреймворк для создания современных реактивных пользовательских интерфейсов и отдельных приложений. Vue обеспечивает расширяемый рендеринг HTML-разметки с помощью объявления шаблонов с привязанными данными.
Vue.js имеет адаптируемую реактивную архитектуру, которая фокусируется на декларативном рендеринге данных и набора компонентов.
Реактивность во Vue позволяет фреймворку взаимодействовать на каждом уровне приложения и обновляет модификацию данных представления.
Реактивное состояние во Vue 2
Прежде чем погрузиться в то, как работает реактивность во Vue 3, давайте кратко рассмотрим, как создавать реактивные данные в приложении на Vue 2. Если вы хотите, чтобы Vue отслеживал изменения, которые вы внесли в данные, вам необходимо объявить свойство внутри объекта, возвращаемого data-функцией.
<template>
<h1>Меня зовут {{ name }}</h1>
</template>
<script>
export default {
data() {
return {
name: "Иван"
};
}
};
</script>
Во второй версии Vue отслеживает все свойства и использует Object.defineProperty() для создания геттеров и сеттеров для каждой части данных и обеспечения отслеживания изменений данных.
Согласно веб-документации, object.defineProperty() определяет новое свойство непосредственно для объекта. Метод также может изменять существующее свойство объекта и возвращать объект. С помощью object.defineProperty мы можем легко установить геттеры и сеттеры объекта:
const data = {
count: 10
};
const newData = {};
Object.defineProperty(newData, 'count', {
get() { return data.count; },
set(newValue) { data.count = newValue; },
});
console.log(newData.count); // => 10
newData.count = 20;
console.log(newData.count); // => 20
Мы создали два объекта: data и newData (пустой). data - наш исходный объект, а newData будет служебным объектом. Устанавливаем свойство count с помощью object.defineProperty().
Чтобы отслеживать, когда к свойству обращаются или когда его изменяют, мы можем сделать следующее:
const data = {
count: 10
};
const newData = {};
function track(){
console.log('К свойству обратились')
};
function trigger(){
console.log('Свойство изменили')
};
Object.defineProperty(newData, 'count', {
get() { track(); return data.count; },
set(newValue) { data.count = newValue; trigger(); },
});
console.log(newData.count); // К свойству обратились 10
newData.count = 20; // Свойство изменили
console.log(newData.count); // К свойству обратились 20
С помощью функций trigger и track мы можем легко увидеть, когда происходят изменения, и выполнить операции отслеживания зависимостей и уведомления об изменениях.
Как разработчику на Vue, важно, чтобы у вас действительно было твердое представление о том, как работает реактивность во Vue 3, чтобы избежать различного рода ее ограничений - особенно во Vue 2. Вам также необходимо понимать реактивность, чтобы использовать такие новые функции, как API композиции.
По-умолчанию JavaScript не реактивен:
let speed = 5;
let time = 2;
let length = speed * time;
console.log(`Пройденное расстояние - ${total}`); // => Пройденное расстояние - 10
time = 3;
console.log(`Пройденное расстояние - ${total}`); // => Пройденное расстояние - 10
Как видим, значение length не было обновлено после того, как мы изменили time на 3.
Чтобы добавить немного реактивности нашему коду, мы можем превратить вычисление в функцию:
let speed = 5;
let time = 2;
let length = 0;
// объявим вычисление
const computeLength = () => length = speed * time;
computeLength();
console.log(length); // => 10
time = 3;
console.log(length); // => по-прежнему 10
computeLength(); // запускаем опять вычисление
console.log(length); // => 15
Теперь мы можем запустить функцию вычисления, чтобы обновить вычисленное значение.
В приведенном выше примере кода мы вручную выполнили вычисление после изменения значения time, чтобы получить обновленное значение length. Однако такой процесс ненадежен, и вычисления приходится выполнять вручную, что может быть проблемой - это может отрицательно сказаться на производительности.
Реактивность во Vue 3
Во Vue 3 отвечающая за реактивность API была переписана с целью устранения некоторых недостатков Vue 2. Реактивная система была переписана для использования прокси JavaScript. Прокси-сервер действует как оболочка вокруг объекта или функции, которая перехватывает операции получения значения и установки/изменения их.
С новой реактивной системой во Vue 3 теперь улучшена поддержка наблюдения за изменениями данных с помощью прокси-объектов.
Объекты Proxy позволяют вам создавать прокси для другого объекта, который может перехватывать и переопределять основные операции для него.
С помощью прокси мы можем перехватывать такие операции, как get и set, и сразу же видеть, когда данные доступны или изменены.
Чтобы создать прокси, вам нужно передать два параметра:
- target: объект данных
- handler: объект, который определяет операции, которые вы хотите перехватить.
Давайте рассмотрим базовый пример ниже:
const data = {
name: 'Иван'
};
const handler = {
get(target, prop, receiver){
console.log('Получение данных: ', target, prop);
return target[prop];
},
set(target, key, value, receiver) {
console.log('Установка данных: ', target, key, value);
return target[key] = value;
}
};
const proxy = new Proxy(data, handler);
console.log(proxy); // => { name: 'Иван' }
console.log(proxy.name);
// => Получение данных: { name: 'Иван' } name
// => Иван
console.log(proxy.name = 'Вася');
// => Установка данных: { name: 'Иван' } name Вася
// => Вася
В приведенном выше примере мы создали прокси-объект, который принял объект данных. У нас также есть обработчик handler, который мы используем для перехвата операций get и set в наших объектах. Каждый раз, когда мы пытаемся получить доступ к параметру name, консоль отображает Получение данных: { name: 'Иван' } name. Подобное происходит, когда мы пытаемся обновить значение свойства name в проксируемых объектах.
Чтобы поддерживать нормальное поведение геттеров и сеттеров объектов, нам нужно использовать объект Reflect для отражения нормального поведения объекта.
Взглянем на пример ниже:
const data = {
name: 'Иван'
};
const handler = {
get(target, prop, receiver){
console.log('Получение данных: ', target, prop);
return Reflect.get(target, prop, receiver);
},
set(target, key, value, receiver) {
console.log('Установка данных: ', target, key, value);
return Reflect.set(target, key, value, receiver);
}
};
const proxy = new Proxy(data, handler);
console.log(proxy); // => { name: 'Иван' }
console.log(proxy.name);
// => Получение данных: { name: 'Иван' } name
// => Иван
console.log(proxy.name = 'Вася');
// => Установка данных: { name: 'Иван' } name Вася
// => Вася
С Reflect нам не нужно обрабатывать мануально прописанные параметры, как мы это делали раньше.
Чтобы увидеть, когда данные поступили или изменились, мы можем добавить три функции:
- track: сообщает нам, когда получен доступ к данным
- watch: информирует нас, когда устанавливаются параметры объекта
- trigger: информирует нас об изменении данных в объекте
Теперь к примеру:
const data = {
name: 'Иван',
surname: 'Иванов',
};
const track = (target, prop, receiver) => console.log('Получение данных: ', target, prop);
const trigger = (target, key, value, receiver) => console.log('Изменение данных: ', target, key, target[key], value);
const watch = (target, key, value, receiver) => console.log('Установка данных: ', target, key, target[key], value);
const handler = {
get(target, prop, receiver){
track(target, prop, receiver);
return Reflect.get(target, prop, receiver);
},
set(target, key, value, receiver) {
watch(target, key, value, receiver);
if (target[key] != value) {
trigger(target, key, value, receiver);
};
return Reflect.set(target, key, value, receiver);
}
};
const proxy = new Proxy(data, handler);
console.log(proxy); // => { name: 'Иван', surname: 'Иванов' }
console.log(proxy.name);
// => Получение данных: { name: 'Иван', surname: 'Иванов' } name
// => Иван
console.log(proxy.name = 'Вася');
// => Установка данных: { name: 'Иван', surname: 'Иванов' } name Иван Вася
// => Изменение данных: { name: 'Иван', surname: 'Иванов' } name Иван Вася
// => Вася
console.log(proxy.name = 'Петя');
// => Установка данных: { name: 'Вася', surname: 'Иванов' } name Вася Петя
// => Изменение данных: { name: 'Вася', surname: 'Иванов' } name Вася Петя
// => Петя
console.log(proxy.name = 'Петя');
// => Установка данных: { name: 'Петя', surname: 'Иванов' } name Петя Петя
// => Петя
Теперь мы можем определить, что делать при изменении данных, на основе добавленных нами пользовательских функций.
Vue поставляется с реактивным методом, который используется для создания реактивного состояния в JavaScript или Vue. По сути, реактивный метод - это просто функция, которая создает прокси-сервер и обертывает его вокруг предоставленных объектов данных, в конечном итоге превращая его в прокси-объект.
Происходит преобразование данных в прокси-объект, которое позволяет Vue выполнять отслеживание зависимостей и уведомлять об изменении при доступе к свойствам или их изменении.
Объединив все, что мы знаем сейчас, мы сможем создать наш собственный реактивный метод:
const reactive = (data) => {
const track = (target, prop, receiver) => console.log('Получение данных: ', target, prop);
const trigger = (target, key, value, receiver) => console.log('Изменение данных: ', target, key, value, receiver);
const watch = (target, key, value, receiver) => console.log('Установка данных: ', target, key, value, receiver);
const handler = {
get(target, prop, receiver) {
track(target, prop, receiver);
return Reflect.get(target, prop, receiver);
},
set(target, key, value, receiver) {
watch(target, key, value, receiver);
if (target[key] != value ) {
trigger(target, key, value, receiver);
};
return Reflect.set(target, key, value, receiver);
}
};
const proxy = new Proxy(data, handler);
return proxy
};
const store = reactive({
count: 0
});
store.count;
// => Получение данных: { count: 0 } count
// => 0
console.log(++store.count);
// => Получение данных: { count: 0 } count
// => Установка данных: { count: 0 } count 1 { count: 0 }
// => 1
Мы только что создали реактивный метод, который превращает наш объект данных в прокси. Это позволяет уведомлять нас об изменениях при каждом изменении свойства.
Эти прокси-объекты не могут быть видны пользователю, но внутри они позволяют Vue выполнять отслеживание зависимостей и уведомлять об изменении при доступе к свойствам или их изменении. Команда Vue выпустила пакет реактивности как отдельный пакет во Vue 3.
Vue отслеживает все объекты, которые были сделаны реактивными, поэтому всегда возвращает один и тот же прокси для одного и того же объекта.
Таким образом, мы можем сказать, что у каждого экземпляра компонента есть соответствующий экземпляр наблюдателя, который записывает все свойства, "затронутые' во время рендеринга компонента, как зависимости. Если в будущем установщик зависимости запускается, он уведомляет наблюдателя, который, в свою очередь, вызывает повторную отрисовку компонента.
При первой отрисовке компонент будет отслеживать список зависимостей (или данных в объектах). Это свойства, к которым он получил доступ во время рендеринга. Это делает компонент подписчиком каждого из этих свойств. В результате, когда прокси-сервер перехватывает операцию, свойство уведомляет все свои подписанные компоненты о повторном рендеринге.
Комментарии (0)