Недавно я связался с несколькими интервьюерами, и когда я спросил, «как Vue реализует двустороннюю привязку данных», я выпалил «перехват данных», и что дальше? Тогда нет ╮(╯_╰)╭. Верно, что «перехват данных» — это основа, но это далеко не тот ответ, который хочет услышать интервьюер, лучше потратить десять минут на чтение этой статьи и ответить на него в следующий раз.
Сама "двусторонняя привязка"
Чтобы ответить на вопрос, сначала поймите вопрос:двусторонняя привязка данныхЭто режим В веб-контексте он обычно относится к автоматической синхронизации данных из dom с объектами JS. DOM и JS изолированы в двух разных средах выполнения, и им необходимо передавать друг другу императивные команды.DOM-интерфейсКоммуникация: DOM должен правильно запускать события и передавать информацию JS-программе, а JS также должен сознательно вызывать соответствующий интерфейс для изменения содержимого DOM после изменения состояния. Такой подход вызывает две проблемы:
- Управление состоянием и представление — это два отдельных набора логики, которые необходимо синхронизировать, что требует больших затрат на разработку.
- Спецификация DOM определяет множество интерфейсов, и есть проблемы с совместимостью, что требует больших затрат на изучение.
Двусторонняя привязка инкапсулирует процесс синхронизации данных из DOM в JS или из JS в DOM с помощью различных конструкций и инкапсулирует его в самом фреймворке.Код верхнего уровня отделен от зависимости от нижележащего интерфейса, и ему нужно только сосредоточиться на логике управления состоянием.
Object.defineProperty
Первый вопрос, который мы хотим обсудить, — как обнаружить изменения в свойствах объекта JS? Самый простой и грубый метод — это «грязная проверка», которая используется в более старых версиях Angular для запуска «грязной проверки» после различных событий, которые могут вызвать изменения состояния. Этот метод интуитивно понятен, но при его реализации необходимо учитывать множество моментов:
- В процессе грязной проверки могут инициироваться новые изменения данных, и возникает бесконечный цикл.
- Реализация грязной проверки должна сохранять копию старых и новых данных.
- Грязные проверки должны предсказывать все возможные тайминги, вызывающие изменения состояния, а это означает, что некоторые нативные интерфейсы должны быть обернуты (в том числе
setTimeout
,requestNextAnimationFrame
Ждать) - ...
Vue использует интерфейс метапрограммированияObject.defineProperty
осуществленный. Когда компонент инициализируется, интерфейс будет вызываться для обертывания свойств объекта какget
,set
функция, которая «встраивает» код в поведение «Получить», «Изменить». Взгляните на простой пример и почувствуйте это интуитивно:
const person = {};
// 嘿嘿,谁都改不了我的名字
Object.defineProperty(person, 'name', {
get() {
return 'van';
},
set(v) {
console.log('they want change my name');
}
});
console.log(person.name);
// van
person.name = 'tec';
// they want change my name
console.log(person.name);
// van
План управления зависимостью
Object.defineProperty
Это просто решает проблему, как инициировать уведомление после изменения состояния.Кого следует уведомлять? Кого волнует, что эти свойства изменились? В Vue используйтеDepРазделяет процесс определения отношения между зависимым и зависимым. Проще говоря:
-
Первый шаг, черезObserverПредоставленный интерфейс проходит через объект состояния и привязывает выделенный объект к каждому свойству и подсвойству объекта.
Dep
объект. Объект состояния здесь в основном относится к компонентам в компоненте.data
Атрибуты. -
Второй шаг — создать три типа наблюдателей:
- передачаinitComputedБуду
computed
атрибуты преобразуются вwatcher
пример - передачаinitWatchметод, будет
watch
конфиг переводится какwatcher
пример - передачаmountComponentметод, для
render
связывание функцийwatcher
пример
- передачаinitComputedБуду
-
Третий шаг, после изменения состояния, триггер
dep.notify()
функция, которая запускает дальнейшиеWatcherобъектupdate
Функция, выполняющая пересчет наблюдателя.
Соответствует следующему рисунку:
Обратите внимание, что в компоненте Vuerender
функцию, мы можем просто думать о ней как о специальнойcomputed
функция в соответствующемWatcher
Когда объект изменяется, запускается выполнение рендеринга для создания новой структуры виртуального дома, которая затем передается Vue для сравнения и обновления представления.
Эпилог
На этом статья заканчивается. Для получения дополнительной информации вы можете посмотреть исходный код. Шаблоны проектирования в коде очень достойны изучения.
Vue использует перехват данных в качестве базовой поддержки и разрабатывает сложное решение для управления зависимостями, чтобы отделить зависимости. Однако схема захвата данных также имеет свои болевые точки, которые трудно решить:
- Может применяться только к простым объектам
- Недействительно для массивов, поэтому вам нужно обернуть метод массива
- Отправной точкой захвата атрибута является «изменение», поэтому vue не может легко получить доступ к «неизменяемому» режиму.