Эта статья просто написана для моих знаний и понимания самой крутой части Vue : Система реактивности.
Задний план
Как мы знаем, команда Vue.js работает на 3,0 некоторое время. Недавно он выпустил первую бета-версию. Это означает, что основной технический дизайн достаточно стабилен. Теперь я думаю, что пришло время пройти через что-то внутри VUE 3.0. Это одна из моих самых любимых частей: система реактивности.
Что такое реактивность?
Для краткости означает реактивность, результат расчетов, который зависит от некоторых определенных данных, будет автоматически обновляться, когда данные изменяются.
В современном веб-разработке нам всегда нужно представить некоторые взгляды, связанные с данными или государством. Таким образом, очевидно, зарабатывание данных реагирует может дать нам много преимуществ. В VUE система реактивности всегда существует из своей очень ранней версии до сих пор. И я думаю, что это одна из самых популярных причин, почему Vue так популярен.
Давайте посмотрим на систему реактивности в ранней версии Vue.
Реактивность в Vue от 0x до 1.x
Впервые я коснулся Vue, – это около 2014 года, я думаю, это было Vue 0.10. В то время вы могли бы просто передать простой объект JavaScript в Vue компонента через данные
вариант. Затем вы можете использовать их в кусочке фрагмента документа в качестве своего шаблона с реактивностью. Однажды данные
Изменения, представление будет автоматически обновляться. Также вы могли бы использовать вычисляется
и Смотреть
Варианты, чтобы принести пользу из системы реактивности более гибкими способами. То же самое для позже Vue 1.x.
new Vue({ el: '#app', template: '{{x}} + {{y}} = {{z}}', data() { return { x: 1, y: 2 } }, computed: { z() { return this.x + this.y } }, watch: { x(newValue, oldValue) { console.log(`x is changed from ${oldValue} to ${newValue}`) } } })
Вы можете найти, что эти API настолько не изменились слишком сильно. Потому что они работают то же самое совершенно полностью.
Итак, как это работает? Как сделать простой объект JavaScript реагировать автоматически?
К счастью, в JavaScript у нас есть API Object.DefineProperty ()
которые могут перезаписать добыча/установку объекта свойства. Итак, чтобы сделать их реактивными, могут быть 3 шага:
- Использовать
Object.DefineProperty ()
Перезаписать Getters/Benters из всех свойств внутри объекта данных рекурсивно. Помимо того, что ведет себя как правило, это дополнительно вводит триггер внутри всех поселенцев и трекеру внутри всех получателей. Также это создаст маленькийДеп
Экземпляр внутри каждый раз, чтобы записать все расчеты, которые зависят от этого свойства. - Каждый раз, когда мы устанавливаем значение в свойство, он позвонит на сетте, который переоценит те связанные расчеты внутри
Деп
пример. Затем вы можете спросить, как мы могли записать все связанные расчеты. Дело в том, когда каждый раз мы определяем расчет, такой какСмотреть
Функция или функция обновления DOM, она будет работать один раз – иногда она работает как инициализация, иногда это просто сухой пробег. И во время этого бега он коснется каждого трекера внутри Getters. Каждый трекер будет нажать текущую функцию расчета в соответствующиеДеп
пример. - Поэтому в следующий раз, когда некоторые данные изменяются, он узнает все связанные расчеты внутри соответствующего
Деп
экземпляр, а затем запустить их снова. Таким образом, эффект этих расчетов будет обновляться автоматически.
Простая реализация для наблюдения за данными, использующими Объект.defineproperty.
как:
// data const data = { x: 1, y: 2 } // real data and deps behind let realX = data.x let realY = data.y const realDepsX = [] const realDepsY = [] // make it reactive Object.defineProperty(data, 'x', { get() { trackX() return realX }, set(v) { realX = v triggerX() } }) Object.defineProperty(data, 'y', { get() { trackY() return realY }, set(v) { realY = v triggerY() } }) // track and trigger a property const trackX = () => { if (isDryRun && currentDep) { realDepsX.push(currentDep) } } const trackY = () => { if (isDryRun && currentDep) { realDepsY.push(currentDep) } } const triggerX = () => { realDepsX.forEach(dep => dep()) } const triggerY = () => { realDepsY.forEach(dep => dep()) } // observe a function let isDryRun = false let currentDep = null const observe = fn => { isDryRun = true currentDep = fn fn() currentDep = null isDryRun = false } // define 3 functions const depA = () => console.log(`x = ${data.x}`) const depB = () => console.log(`y = ${data.y}`) const depC = () => console.log(`x + y = ${data.x + data.y}`) // dry-run all dependents observe(depA) observe(depB) observe(depC) // output: x = 1, y = 2, x + y = 3 // mutate data data.x = 3 // output: x = 3, x + y = 5 data.y = 4 // output: y = 4, x + y = 7
Внутри Vue 2.x и ранее механизм примерно подобно этому выше, но намного лучше абстрагировано, спроектирован и реализован.
Для поддержки более сложных случаев, таких как массивы, вложенные свойства, или мутируют более 2 свойств одновременно, в то время больше, чем информация о внедрении и оптимизации внутри Vue, но в основном, тот же механизм для мы упомянул ранее.
Реактивность в Vue 2.x
С 1.x до 2.x, это было полное переписать. И он ввел некоторые действительно крутые особенности, такие как виртуальный DOM, серверный рендеринг, низкоуровневые функции рендер и т. Д. Но интересная вещь – система реактивности не изменилась слишком сильно, однако, использование выше было совершенно другим:
- От 0x до 1.x логика рендеринга зависит от поддержания фрагмента документа. Внутри этого фрагмента документа есть некоторые функции обновления DOM для каждого динамического элемента, атрибута и контента текста. Таким образом, система реактивности в основном работает между объектом данных и этими функциями обновления DOM. Поскольку функции все реальные DOM функционируют так, что производительность не совсем хорошая. В Vue 2.x эта логика рендеринга компонента Vuue стала целым чистым функцией рендеринга JavaScript. Так что это сначала вернет виртуальные узлы вместо настоящих узлов DOM. Тогда он обновит реальный DOM на основе результата алгоритма быстрого мутации для виртуальных узлов DOM. Это было быстрее, чем раньше.
- В Vue 2.6 он представил автономный API
Vue.observalue (OBJ)
Для генерации реактивных объектов JavaScript. Так что вы могли бы использовать их внутриоказывать
функция иливычисляется
имущество. Это было более гибко для использования.
В то же время в сообществе Vue есть некоторые дискуссии о рассуждении системы реактивности в независимый пакет для более широкого использования. Однако это не произошло в то время.
Ограничение системы реактивности до 3,0
Пока что Vue не изменил механизм реактивности. Но это не значит, что текущее решение идеально идеально. Как я лично понимаю, есть некоторые оговорки:
Из-за ограничения
Объект.defineProperty
Мы не могли наблюдать за некоторыми изменениями данных, таких как:- Установка элементов массива, назначая значение определенному индексу. (например
Arr [0]
) - Установка длины массива. (например
arr.length
) - Добавление нового свойства на объект. (например,
obj.newkey
) Так что ему нужны дополнительные API, такие какVue. $ Set (obj, newkey, value)
Отказ
- Установка элементов массива, назначая значение определенному индексу. (например
- Из-за ограничения простой структуры данных JavaScript для каждого реактивного объекта будет ненормативные Недвижимость по имени
__ob__
, что может привести к конфликту в некоторых экстремальных случаях. - Это не поддерживает больше типов данных, таких как
карта
иУстановить
Отказ Ни другие нелые объекты JavaScript. - Производительность – это проблема. Когда данные большие, делая его реактивным, когда инициализация будет стоить видимое время. Есть несколько советов, чтобы сгладить начальную стоимость, но немного сложно.
Система реактивности в Vue 3.0
Для коротких, в Vue 3.0 система реактивности была полностью переписана новым механизмом и новой абстракцией, как независимый пакет. И он также поддерживает более современные типы данных JavaScript.
Вы можете быть знакомы с этим, может быть, нет. Не беспокойтесь. Давайте сначала посмотрим на это сначала, создав проект Vue 3.0.
Создать проект Vue 3.0
До сих пор нет устойчивого полнофункционального генератора проекта, поскольку он все еще находится в бета-версии. Мы могли бы попробовать Vue 3.0 через экспериментальный проект под названием «Vite»:
Vitejs/Vite.
Инструментаринг интерфейса следующего поколения. Это быстро!
Инструментарий следующего поколения
💡 Мгновенное начало сервера⚡️ Молния быстро HMR🛠️ Богатые особенности📦 Оптимизированная сборка🔩 Универсальный интерфейс плагина🔑 Полностью напечатанные API
Vite (французское слово для «быстро», произносится /vit/
) – это новая порода инструмента сборки Frontend, который значительно улучшает опыт разработки Frontend. Он состоит из двух основных частей:
SEV Server, который обслуживает ваши исходные файлы через родные модули ES , с богатые встроенные функции и удивительно быстро Замена горячего модуля (HMR) Отказ
А Создать команду что связывает ваш код с Рулон предварительно сконфигурирован для вывода высоко оптимизированных статических активов для производства.
Кроме того, Vite очень расширяется через его Плагин API и JavaScript API с полной наборной поддержкой.
Прочитайте документы, чтобы узнать больше Отказ
Мигрировать с 1.x.
Вите сейчас в бета-версии 2.0. Проверьте Миграция Руководство Если вы обновляете с 1.x.
Пакеты
https://github.com/vitejs/vite
Просто запустите эти команды ниже:
$ npx create-vite-app hello-world $ cd hello-world $ npm install $ npm run dev
Тогда вы можете получить доступ к приложению Vue 3.0 через http://localhost: 3000 Отказ
Вы могли видеть, что уже есть Vue компонент App.vue.vue.
:
Count is: {{ count }} is positive: {{ isPositive }}
Есть реактивное свойство считать
И это отображается в <Шаблон>
Отказ Когда пользователи нажимают кнопку «Приращение», свойство Считать
будет увеличиваться, вычисленная собственность Aspositive
будет также будет рассчитан, и UI будет обновляться автоматически.
Кажется, ничего не отличается от прежней версии до сих пор.
Теперь давайте попробуем что-то невозможное в ранних версиях Vue.
1. Добавление нового свойства
Как мы уже упоминали, в Vue 2.x и ранее мы не могли соблюдать недавно добавленное свойство автоматически. Например:
My name is {{ name.given }} {{ name.family }}
Обновление
Метод не мог работать правильно, потому что новая недвижимость Семья
не может быть замечено. Поэтому при добавлении этого нового свойства функция рендеринга не будет рассчитана. Если вы хотите эту работу, вы должны вручную использовать еще один дополнительный API как Vue. $ Set (this.name, "Семья", "Чжао")
Отказ
Но в Vue 3.0 он уже работает. Вам не нужно Vue. $ Set
больше.
2. Назначение элементов на массив по индексу
Теперь давайте попробуем установить значение в индекс массива:
- {{ item }}
В Vue 2.x и раньше, когда вы нажимаете одну из кнопок «Редактировать» в элементе списка и введите новый элемент текстовой строки, вид не будет изменен, потому что элемент настройки с помощью индекса, как Это .List [индекс]
не может быть отслеживается. Вы должны написать Vue. $ Set (this.list, index, newitem)
вместо. Но в Vue 3.0 он тоже работает.
3. Установка длины свойства массива
Также, если мы добавим другую кнопку на пример выше, чтобы очистить все элементы:
...
btw Vue 3.0 supports multi-root template like this
Он не будет работать в Vue 2.x и ранее, потому что установка длины массива, как this.list.length
не может быть отслеживается. Таким образом, вы должны использовать другие методы, такие как this.list = []
. Но в Vue 3.0 все пути выше работы.
4. Использование SET/MAP
Давайте посмотрим аналогичный пример с ES Set:
- {{ item }}
Теперь мы используем Установить
вместо массива. В Vue 2.x и раньше, к счастью, это может быть должным образом оказано правильно. Но когда вы удалите, добавьте или очистить, вид не будет обновлен, потому что они не отслеживаются. Так что обычно мы не используем Установить
или Карта
в Vue 2.x и раньше. В VUE 3.0 тот же код будет работать, как вам нравится, потому что он полностью поддерживает их.
5. Использование нереактивных свойств
Если у нас есть одни однократные потребляющие тяжелые данные в VUE-компонент, вероятно, не нужно реагировать, потому что после инициализации он не изменится. Но в Vue 2.x и раньше, что бы вы ни использовали их снова, все свойства внутри будут отслеживаться внутри. Так что иногда это стоит видно время. Практически у нас есть другие способы ходить Но это немного сложно.
В Vue 3.0 он обеспечивает выделенную API для этого – Markraw
:
Hello {{ test.name }}
В этом случае мы используем Markraw
Чтобы сказать систему реактивности, тест свойств и свойства его потомков не нужно отслеживать. Таким образом, процесс отслеживания будет пропущен. В то же время любое дальнейшее обновление на них не будет вызвать реранда.
Кроме того, есть еще один «двойной» API – readonly
Отказ Этот API может предотвратить мутировать данные. Например:
import { readonly } from 'vue' export default { data: () => ({ test: readonly({ name: 'Vue' }) }), methods: { update(){ this.test.name = 'Jinjiang' } } }
Тогда мутация на Это. Тест
будет неудачно.
До сих пор мы видим силу и магию системы реактивности в Vue 3.0. На самом деле есть более мощные способы его использования. Но мы не будем немедленно двигаться, потому что, прежде чем освоить их, также приятно знать, как он работает позади Vue 3.0.
Как это работает
Для коротких, система реактивности в Vue 3.0 подходит с ES2015!
Первая часть: простой наблюдатель данных
С ES2015 есть пара API – Прокси
и Отражать
Отказ Они рождены для реактивности систем! Vue 3.0 Система реактивности просто будет построена на основе этого.
С Прокси
Вы можете установить «ловушку», чтобы наблюдать любую работу на определенном объекте JavaScript.
const data = { x: 1, y: 2 } // all behaviors of a proxy by operation types const handlers = { get(data, propName, proxy) { console.log(`Get ${propName}: ${data[propName]}!`) return data[propName] }, has(data, propName) { ... }, set(data, propName, value, proxy) { ... }, deleteProperty(data, propName) { ... }, // ... } // create a proxy object for the data const proxy = new Proxy(data, handlers) // print: 'Get x: 1' and return `1` proxy.x
С Отражать
Вы можете вести себя так же, как оригинальный объект.
const data = { x: 1, y: 2 } // all behaviors of a proxy by operation types const handlers = { get(data, propName, proxy) { console.log(`Get ${propName}: ${data[propName]}!`) // same behavior as before return Reflect.get(data, propName, proxy) }, has(...args) { return Reflect.set(...args) }, set(...args) { return Reflect.set(...args) }, deleteProperty(...args) { return Reflect.set(...args) }, // ... } // create a proxy object for the data const proxy = new Proxy(data, handlers) // print: 'Get x: 1' and return `1` proxy.x
Так с Прокси
+ Отражать
Вместе мы могли бы легко сделать объект JavaScript, а затем реактивный.
const track = (...args) => console.log('track', ...args) const trigger = (...args) => console.log('trigger', ...args) // all behaviors of a proxy by operation types const handlers = { get(...args) { track('get', ...args); return Reflect.get(...args) }, has(...args) { track('has', ...args); return Reflect.set(...args) }, set(...args) { Reflect.set(...args); trigger('set', ...args) }, deleteProperty(...args) { Reflect.set(...args); trigger('delete', ...args) }, // ... } // create a proxy object for the data const data = { x: 1, y: 2 } const proxy = new Proxy(data, handlers) // will call `trigger()` in `set()` proxy.z = 3 // create a proxy object for an array const arr = [1,2,3] const arrProxy = new Proxy(arr, handlers) // will call `track()` & `trigger()` when get/set by index arrProxy[0] arrProxy[1] = 4 // will call `trigger()` when set `length` arrProxy.length = 0
Таким образом, этот наблюдатель лучше, чем объект. ИспользуетсяПроперство, потому что он мог наблюдать каждый прежний мертвый угол. Также наблюдатель просто должен создать «ловушку» на объект. Так дешевле во время инициализации.
И это не все реализация, потому что в Прокси
Это может справиться с всеми видами поведения с разными целями. Таким образом, завершенный код обработчиков в VUE 3.0 более сложный.
Например, если мы запустим arrproxy.push (10)
Прокси будет вызвать Установить
обработчик с 3
Как его пропитан
и 10
Как его ценность
Отказ Но мы не знаем, стоит ли ли это новый индекс. Так что, если мы хотели бы отследить arrproxy.length
мы должны сделать более точное определение о том, есть ли набор или DELETEPROPERTY
Операция изменит длину.
Также это Прокси
+ Отражать
Механизм поддерживает вас отслеживать и вызвать мутации в Установить
или Карта
Отказ Это означает, что операции, такие как:
const map = new Map() map.has('x') map.get('x') map.set('x', 1) map.delete('x')
также будет наблюдаемой.
Второй: больше реактивности API
В Vue 3.0 мы также предоставляем некоторые другие API, как readonly
а также Markraw
Отказ Для readonly
Что вам нужно, это просто изменить обработчики, как Установить
и DELETEPROPERTY
чтобы избежать мутаций. Вероятно, нравится:
const track = (...args) => console.log('track', ...args) const trigger = (...args) => console.log('trigger', ...args) // all behaviors of a proxy by operation types const handlers = { get(...args) { track('get', ...args); return Reflect.get(...args) }, has(...args) { track('has', ...args); return Reflect.set(...args) }, set(...args) { console.warn('This is a readonly proxy, you couldn\'t modify it.') }, deleteProperty(...args) { console.warn('This is a readonly proxy, you couldn\'t modify it.') }, // ... } // create a proxy object for the data const data = { x: 1, y: 2 } const readonly = new Proxy(data, handlers) // will warn that you couldn't modify it readonly.z = 3 // will warn that you couldn't modify it delete readonly.x
Для Markraw
в Vue 3.0 он установил бы ненормативные Флаг Недвижимость по имени __v_skip
Отказ Поэтому, когда мы создаем прокси для данных, если есть __v_skip
Флаг свойство, то это было бы пропущено. Вероятно, нравится:
// track, trigger, reactive handlers const track = (...args) => console.log('track', ...args) const trigger = (...args) => console.log('trigger', ...args) const reactiveHandlers = { ... } // set an invisible skip flag to raw data const markRaw = data => Object.defineProperty( data, '__v_skip', { value: true } ) // create a proxy only when there is no skip flag on the data const reactive = data => { if (data.__v_skip) { return data } return new Proxy(data, reactiveHandlers) } // create a proxy object for the data const data = { x: 1, y: 2 } const rawData = markRaw(data) const reactiveData = readonly(data) console.log(rawData === data) // true console.log(reactiveData === data) // true
Кроме того, испытание с использованием слабого управления для записи депожества и флаги
Хотя он не реализован в Vue 3.0, наконец. Но была еще одна попытка записать DEPS и флаги, используя новые структуры данных в ES2015.
С Установить
и Карта
Мы могли бы сохранить отношения из самого данных. Поэтому нам не нужно свойства флага, как __v_skip
Внутри данных больше – на самом деле есть некоторые другие свойства флага, такие как __v_isreacive
и __v_isreadonly
в Vue 3.0. Например:
// a Map to record dependets const dependentMap = new Map() // track and trigger a property const track = (type, data, propName) => { if (isDryRun && currentFn) { if (!dependentMap.has(data)) { dependentMap.set(data, new Map()) } if (!dependentMap.get(data).has(propName)) { dependentMap.get(data).set(propName, new Set()) } dependentMap.get(data).get(propName).add(currentFn) } } const trigger = (type, data, propName) => { dependentMap.get(data).get(propName).forEach(fn => fn()) } // observe let isDryRun = false let currentFn = null const observe = fn => { isDryRun = true currentFn = fn fn() currentFn = null isDryRun = false }
Тогда с Прокси
/ Отражать
Вместе мы могли бы отслеживать мутацию данных и зависимые функции спусковой обработки данных:
// … handlers // … observe // make data and arr reactive const data = { x: 1, y: 2 } const proxy = new Proxy(data, handlers) const arr = [1, 2, 3] const arrProxy = new Proxy(arr, handlers) // observe functions const depA = () => console.log(`x = ${proxy.x}`) const depB = () => console.log(`y = ${proxy.y}`) const depC = () => console.log(`x + y = ${proxy.x + proxy.y}`) const depD = () => { let sum = 0 for (let i = 0; i < arrProxy.length; i++) { sum += arrProxy[i] } console.log(`sum = ${sum}`) } // dry-run all dependents observe(depA) observe(depB) observe(depC) observe(depD) // output: x = 1, y = 2, x + y = 3, sum = 6 // mutate data proxy.x = 3 // output: x = 3, x + y = 5 arrProxy[1] = 4 // output: sum = 8
На самом деле в ранней бета-версии Vue 3.0 он использует Слабый пометки
вместо карта
Таким образом, не будет никакой утечки памяти, чтобы беспокоиться о. Но, к сожалению, производительность не хороша, когда данные идут на большие. Поэтому позже он переоделся на свойства флага.
Кстати, есть также испытание с использованием Символ
S как имена свойств флага. С Символ
S крайние случаи также могут быть облегчены. Но то же самое, производительность все еще не хороши, как обычные имена свойств String.
Хотя эти эксперименты не сохранились, наконец, я думаю, что это хороший выбор, если вы хотите сделать чистый (но, возможно, не совсем исполнительный) наблюдатель данных самостоятельно. Так что просто упомяните это немного здесь.
Краткое резюме
В любом случае мы сделаем данные реагирующими первыми, и соблюдайте функции для отслеживания всех данных, на которых они зависят. Затем, когда мы мутируем реактивные данные, соответствующие функции будут запущены для запуска.
Все функции и их дальнейшие проблемы выше, уже были завершены в Vue 3.0, при этом мощность функций ES2015.
Если вы хотите увидеть всю живую версию образца кода о объяснении основного механизма системы реактивности в Vue от 0x до 3,0. Вы можете проверить этот кодепен и увидеть его панель «Консоль»:
Теперь мы уже знали основное использование этого – это происходит что-то в данные
опция в Vue компонент, а затем, используя его в другие параметры, такие как вычисляется
, смотреть
или Шаблон
Отказ Но на этот раз в Vue 3.0 он предоставляет больше apis itil, как Markraw
Мы упоминали ранее. Итак, давайте посмотрим на эти UTIL API.
Инкапсуляция
1. Прокси для объектов
1.1 Основные: Реактивные (данные), readonly (данные), Markraw (данные)
Сначала позвольте мне представить Реактивные (данные)
. Так же, как имя, этот API создаст реактивный прокси для данных. Но здесь, может быть, вам не нужно использовать это напрямую, потому что объект данных, которые вы возвращаете из данные
Опция будет автоматически настроен с этим API.
потом Если вы просто хотите:
- Некоторые части данных неизменны, то вы можете использовать
readonly (данные)
Отказ - Некоторые части данных не реагируют, то вы можете использовать
Markraw (данные)
.
Например:
import { reactive, readonly, markRaw } from 'vue' const ComponentFoo = { data() { return { reactiveX: { x: 1 }, reactiveXInAnotherWay: reactive({ x: 1 }), immutableY: readonly({ y: 2 }), needntChangeReactivelyZ: markRaw({ z: 3 }) } }, // ... }
В этом случае:
- Если свойства в
реактивный
илиРеактивацияxinAnotherway.
Изменено, представление, использующее их в шаблоне, будет перенаправлено автоматически. - Если вы измените свойства в
неизменный
, будет брошена ошибка. В то же время представление не будет воспроизведено. - Если вы измените свойства в
Необязательность Вид не будет переназначен.
Также для маркировки в качестве необработанных данных вы можете пометить данные, а затем использовать его где-либо еще:
const { markRaw } from 'vue' const obj = { x: 1 } const result = markRaw(obj) console.log(obj === result) // true const ComponentFoo = { data() { return { obj, result } }, // ... }
Здесь свойства в Это .obj
и Это.Result
являются нереактивными.
1.2 UTILS: ISREACTIVE (DATA), ISREADONLY (DATA), ISPROXY (DATA), TORAV (DATA)
Тогда вам может понадобиться apist apit, чтобы помочь вам сделать работу лучше.
- Для прокси реактивных данных, то оба
Isproxy (данные)
иISREACTIVE (данные)
будетправда
Отказ - Для доверенного прокси-сервера данных, то оба
Isproxy (данные)
иIsreadonly (данные)
будетправда
Отказ - Для исходных данных, помечены ли он или нет, то все это
Isproxy (данные)
иIsreактивы (данные)
иIsreadonly (данные)
будетложь
Отказ - Для реактивной или реальной прокси-сервера вы можете использовать
Тора (данные)
чтобы вернуть необработанные данные.
1.3 Advanced: MOLLOWROWREACTIVE (DATA), HULLOWSREADONLY (данные)
С этими 2 API вы можете создать «неглубокие» прокси-сервер данных, что означает, что они не будут глубоко устанавливать ловушки. Только первые свойства в этих данных в этих данных будут реактивными или реально. Например:
import { shallowReactive, shallowReadonly } from 'vue' const ComponentFoo = { data() { return { x: shallowReactive({ a: { b: 1 } }), y: shallowReadonly({ a: { b: 1 } }) } } }
В этом случае this.x.a
реагирует, но this.x.a.b
не является; This.y.a
готовлю, но this.y.a.b
не является.
Если вы только потребляете реактивные данные внутри своего собственного компонента, я думаю, что эти API выше достаточно. Но когда все придет в реальный мир, иногда мы хотели бы поделиться состояниями между компонентами или просто абстрактным состоянием из компонента для лучшего обслуживания. Поэтому нам нужно больше API ниже.
2. Ref для примитивных ценностей
Реф может помочь вам выполнить ссылку на реактивное значение. В основном это используется для примитивной ценности. Например, как-то у нас есть номер числа с именем счетчик
В модуле ES, но код ниже не работает:
// store.js // This won't work. export const counter = 0; // This won't works neither. // import { reactive } from 'vue' // export const counter = reactive(0)
{{ counter }}
… Потому что примитивные значения неизменяются. При импорте и экспорте примитивных ценностей мы теряем трассу. Для этого мы могли бы использовать ref вместо.
2.1 Базовый: REF (данные)
Чтобы поддержать предыдущий пример, давайте представим Ref (данные)
:
// store.js import { ref } from 'vue' export const counter = ref(0)
Тогда это будет работать должным образом.
Есть одно внимание: если вы хотите получить доступ к значению ссылки из шаблона, вы должны получить доступ к его ценность
недвижимость вместо этого. Например, если бы мы хотели бы изменить Bar.Vue
Чтобы избежать данные
Опция, мы могли бы добавить увеличение
Способ сделать это, с Counter.value
:
Для более предупреждений мы могли бы сделать несколько быстрых тестов позже.
2.2 UTILS: ISREF (данные), UNREF (данные)
Я думаю, что эти 2 UTIL API легко понять:
ISREF (данные)
: Проверьте значение – это реф или нет.Unref (данные)
: Верните значение ref.
2.3 Прокси для Ref: Toref (данные, ключ), Torefs (данные)
Эти 2 UTIL API используются для получения Refs из прокси-данных:
import { reactive, toRef, toRefs } from 'vue' const proxy = reactive({ x: 1, y: 2 }) const refX = toRef(proxy, 'x') proxy.x = 3 console.log(refX.value) // 3 const refs = toRefs(proxy) proxy.y = 4 console.log(refs.x.value) // 3 console.log(refs.y.value) // 4
Как приведен выше пример, типичное использование этих API – это распространяет реактивный объект в несколько подменных переменных и одновременно сохраняет реакционную способность.
2.4 Расширенный: MOLLOWREF (данные)
Только запускаемое обновление, когда Ref.value
назначается еще одним значением. Например:
import { shallowRef } from 'vue' const data = { x: 1, y: 2 } const ref = shallowRef(data) // won't trigger update ref.value.x = 3 // will trigger update ref.value = { x: 3, y: 2 }
Случай: вычисляется (…)
Подобная идея для вычисляется
вариант внутри составляющей Vue. Но если вы хотите поделиться вычисленным состоянием из компонента, я предлагаю вам попробовать эту API:
// store.js import { ref, computed } from 'vue' export const firstName = ref('Jinjiang') export const lastName = ref('Zhao') // getter only version export const fullName = computed(() => `${firstName.value} ${lastName.value}`) // getter + setter version export const fullName2 = computed({ get: () => `${firstName.value} ${lastName.value}`, set: (v) => { const names = v.split(' ') if (names.length > 0) { firstName.value = names[0] } if (names.length > 1) { lastName.value = names[names.length - 1] } } })
// another-file.js import { firstName, lastName, fullName, fullName2 } from './store.js' console.log(fullName.value) // Jinjiang Zhao firstName.value = 'Evan' lastName.value = 'You' console.log(fullName.value) // Evan You fullName2.value = 'Jinjiang Zhao' console.log(firstName.value) // Jinjiang console.log(lastName.value) // Zhao
Случай: commanderf (…)
Эта API – моя лучшая любимая API в Vue 3.0. Потому что с этим API вы могли бы определить, как и когда отслеживать / триггер Ваши данные, во время получения или установки значения, это совершенно разумное!
Например:
Это делает реальный ввод пользователей намного проще справиться.
3. Следите за эффектами
Watcheffect (Функция), Часы (Депс, обратный вызов)
В Vue компонент мы могли смотреть мутации данных по Смотреть
вариант или VM. $ Смотреть ()
API экземпляра. Но Этот же вопрос: как насчет просмотра мутаций данных из составляющей VUE?
Похож на вычисляется
Реактивность API против вычисляется
Опция, у нас есть 2 API на реактивность: Watcheffect
и Смотреть
Отказ
// store.js import { ref, watch, watchEffect } from 'vue' export const counter = ref(0) // Will print the counter every time it's mutated. watchEffect(() => console.log(`The counter is ${counter.value}`)) // Do the similar thing with more options watch(counter, (newValue, oldValue) => console.log(`The counter: from ${oldValue} to ${newValue}`) )
4. Автономный пакет и использование
Также в Vue 3.0 у нас есть автономный пакет для них. То есть @ Vue/реактивность
. Вы также можете импортировать большинство API, которые мы упомянули выше, из этого пакета. Таким образом, код почти одинаково для выше:
import { reactive, computed, effect } from '@vue/reactivity' const data = { x: 1, y: 2 } const proxy = reactive(data) const z = computed(() => proxy.x + proxy.y) // print 'sum: 3' effect(() => console.log(`sum: ${z.value}`)) console.log(proxy.x, proxy.y, z.value) // 1, 2, 3 proxy.x = 11 // print 'sum: 13' console.log(proxy.x, proxy.y, z.value) // 11, 2, 13
Единственное различие нет смотреть
и наблюдение
. Вместо этого есть еще один низкоуровневый API с именем эффект
Отказ Его основное использование просто похоже на Watcheffect
Но более гибкий и мощный.
Для получения более подробной информации я предлагаю вам прочитать исходный код напрямую:
https://github.com/vuejs/vue-next/tree/master/packages/reactivity
Таким образом, вы даже можете использовать эти API в проектах, связанных с VUE, как вам нравится.
Отныне вы можете подумать об этом: с API на реактивность, какие еще удивительные вещи вы могли бы сделать? 😉
Выгода и предостережения
До сих пор мы знаем, как реактивность API работает в Vue 3.0. Сравнение с 2.x и более ранней версии, это:
- Полностью охватывает все виды мутаций данных, таких как добавление нового свойства на объект, установив значение для
Индекс
массива и т. Д. - Полностью поддерживать все новые структуры данных, например
карта
иУстановить
Отказ - Имеет лучшую производительность.
- Он может быть использован в качестве автономного пакета.
Так что, если вам действительно нужно или любить любое из вышеперечисленного, возможно, пришло время попробовать.
В то же время есть некоторые предостережения для вас:
- Это работает только на ES2015 +
- Используйте Refs для примитивных значений для сохранения реактивности.
- Реактивный прокси не равен исходным данным в JavaScript.
Для получения более подробной информации я подготовил чит-лист на цисте ниже:
https://gist.github.com/Jinjiang/f795b943d4315a42077b7261caf25187
Также есть еще 2 случайных проекта CodeSandbox, которые я проверяю для себя ранее. Может быть, это как-то немного полезно:
- для
реактивный
,readonly
, а такжеMarkraw
: https://codesandbox.io/s/vue-reactivity-tests-1-jm3d4 - для
Ref
ивычисляется
: https://codesandbox.io/s/vue-reactivity-tests-2-vyykh
Дальнейшее использование случаев
Пока что мы знаем много вещей о системе реактивности в Vue, от ранней версии до 3.0. Теперь пришло время показать некоторые случаи использования на основе этого.
Состав API.
Первое, что определенно является Vue состав API, который новый в 3.0. С API реактивности мы могли более гибко организовать нашу логику кода.
import { ref, reactive, readonly, markRaw, computed, toRefs } from 'vue' export default { setup(props) { const counter = ref(0) const increment = () => counter.value++ const proxy = reactive({ x: 1, y: 2 }) const frozen = readonly({ x: 1, y: 2 }) const oneTimeLargeData = markRaw({ ... }) const isZero = computed(() => counter.value === 0) const propRefs = toRefs(props) // could use a,b,c,d,e,f in template and `this` return { a: counter, b: increment, c: proxy, d: frozen, e: oneTimeLargeData, f: isZero, ...propRefs } } }
Я не хочу показать больше демонстрации об этом, потому что они уже везде. Но ИМО, для дальнейшей выгоды немногих людей, о которых говорят, это, ранее в Vue 2.x и ранее мы привыкли ставить все на Это
, когда мы:
- Создание реактивных данных для экземпляра компонента.
- Доступ к данным/функциям в шаблоне.
- Доступ к данным/функциям вне экземпляра компонента, в основном это происходит, когда мы устанавливаем шаблон Ref в Sub Vue.
Все 3 вещи всегда случаются вместе. Это означает, может быть, мы только что:
- Хотелось бы получить доступ к чему-то в шаблоне, но не нужна реактивность.
- Хотел бы создать реактивные данные, но не используйте это в шаблоне.
Vue Композиция API Элегантно осудит их на 2 шага:
- создавать реактивные данные;
- Решите, что потребности шаблона.
BTW, для государственных членов экземпляра, я думаю, что потенциальная проблема все еще там. Однако до сих пор не имеет большого значения.
Кроме того, есть некоторые другие преимущества, в том числе не ограничиваются:
- Поддерживать многоразовый код, не беспокоясь о конфликте имена.
- Собрание логически связанного кода вместе, а не сбора членов экземпляра вместе с тем же типом опции.
- Лучше и легкая поддержка TeampStry.
Также в составе API больше API, как Предоставить ()
/ ввод ()
, Крючки жизненного цикла, шаблоны Refs и т. Д. Для получения дополнительной информации о композиции API, пожалуйста, проверьте этот URL: https://composition-api.vuejs.org/ Отказ
Межкомпонентный государственный обмен
При совместном использовании данных между компонентами. API на реактивность также хороший выбор. Мы могли бы даже использовать их из любого VUE-компонента, и, наконец, использовать их в приложении Vue, например, с композицией API обеспечить
и вставлять
:
// store.js import { ref } from 'vue' // use Symbol to avoid naming conflict export const key = Symbol() // create the store export const createStore = () => { const counter = ref(0) const increment = () => counter.value++ return { counter, increment } }
// App.vue import { provide } from 'vue' import { key, createStore } from './store' export default { setup() { // provide data first provide(key, createStore()) } }
// Foo.vue import { inject } from 'vue' import { key } from './store' export default { setup() { // you could inject state with the key // and rename it before you pass it into the template const { counter } = inject(key) return { x: counter } } }
// Bar.vue import { inject } from 'vue' import { key } from './store' export default { setup() { // you could inject state with the key // and rename it before you pass it into the template const { increment } = inject(key) return { y: increment } } }
Итак, когда пользователь звонит Y () в bar.vue, x в foo.vue также будет обновляться. Вам даже не нужно больше государственной библиотеки управления. Это довольно просто в использовании.
Помните vue-крючки?
Это больше не активный проект. Но я помню, что после того, как отреагировал крючки впервые объявил, Эван, создатель Vue, просто дал POC под Vue в 1 день с Менее 100 строк кода Отказ
Вот живая демонстрация в кодовой коробке:
Почему это можно сделать так легко с Vue. Я думаю, в основном из-за системы реактивности в Vue. Это уже помогает вам сделать большую часть работы. Что нам нужно сделать, это просто инкапсулировать их в новом узоре или более дружественных API.
Написание реагировать с системой реактивности VUE
Итак, давайте попробуем еще один шаг POC. Как насчет использования API реактивности в реакции на создание компонентов реагирования?
import * as React from "react"; import { effect, reactive } from "@vue/reactivity"; const Vue = ({ setup, render }) => { const Comp = props => { const [renderResult, setRenderResult] = React.useState(null); const [reactiveProps] = React.useState(reactive({})); Object.assign(reactiveProps, props); React.useEffect(() => { const data = { ...setup(reactiveProps) }; effect(() => setRenderResult(render(data))); }, []); return renderResult; }; return Comp; }; const Foo = Vue({ setup: () => { const counter = ref(0); const increment = () => { counter.value++; }; return { x: counter, y: increment }; }, render: ({ x, y }) =>Hello World {x.value}
});
Я сделал небольшой тест, как выше, это не полная реализация. Но каким-то образом мы могли бы сохранить основной реагировать компонент с 2 частями:
- Чистая логика данных с реактивностью.
- Любое обновление данных будет наблюдаться и запускается и запускает компонент.
Те, кто соответствует Настройка
и оказывать
Функции как Vue компонент делает.
И нет способа беспокоиться о том, пишу ли я или нет, я пишу реактивный крюк за пределами реактивный компонент или внутри условного блока. Просто код, как вам нравится и сделать это, как вы себе представляете.
Окончательные заключительные выводы
Так вот вот и все о системе реактивности в Vue, от ранней версии до последней версии 3.0 бета. Я все еще учу много новых вещей, таких как языки программирования, парадигмы, рамки и идеи. Все они великолепны и сияют. Но система реактивности всегда является мощным и элегантным инструментом, чтобы помочь мне решить все виды проблем. А также Это все еще сохраняет развиваться.
С помощью ES2015 +, новой реакционной способности API и его независимый пакет, композиция API, Vue 3.0 и более удивительные вещи в экосистеме и сообществе. Надеюсь, вы сможете использовать их или вдохновлять от них, чтобы построить более великие вещи намного проще.
Надеюсь, вы сможете знать Vue и его реактивную способность лучше по этой статье.
Все образцы кода в этой статье: https://gist.github.com/Jinjiang/f9b6f968af980cfd21cfc713e59db91b
Оригинал: “https://dev.to/jinjiang/understanding-reactivity-in-vue-3-0-1jni”