Это первый раз, когда пишу статью, я надеюсь, вы можете указать неправильное место.
Кратко поговорим о принципе адаптивной реализации Vue.
Отзывчивый принцип Vue находится через модульную реализацию «наблюдателя / абонента».
Во-первых, Vue будет циклически вызывать данные, а также массивы и объекты под данными.Object.definePropertyметод, чтобы установить геттер и сеттер для перехвата назначения и значения данных. То есть, когда мы присваиваем значения (например:this.property='string'), он вызоветObject.definePropertyМетод set устанавливается методом, когда мы берем значение (например:<div v-if="title">{{title}}</div>), будет вызван метод set get.
Метод get будет определять текущийDep.target(Объекты Dep используются для поддержания зависимостей,Dep.targetиспользуется для сохранения текущего объекта Watcher) пусто, если нет, то будетDep.targetДобавить в массив subs объекта dep для записи зависимостей, то есть в этот массив subs записываются все объекты Watcher, которые будут принимать данные, чтобы при изменении данных мы могли использовать этот массив для уведомления всех объектов Watcher. на обновление, чтобы завершить отзывчивость.
Этот процесс уведомления выполняется с помощью метода set. Метод set вызывает метод dep.notify, чтобы уведомить все зависимые объекты Watcher о вызове их собственного метода обновления для обновления.
Далее поговорим о том, как создается этот Watcher.
Когда компонент Vue выполняет рендеринг, он сначала сгенерирует функцию рендеринга из шаблона компонента с помощью функции compileToFunctions (эта функция рендеринга используется для создания виртуального дерева DOM VNode), а затем создаст объект Watcher и передаст сгенерированный рендеринг. Функция к функции. Дайте этот объект Watcher для обновлений. При создании объекта Watcher будет вызвана переданная нами функция рендеринга.При вызове функции рендеринга будет получено значение данных, используемых в шаблоне.В этом случае будет затронут геттер и объект Watcher будет добавлен в зависимость. Это завершает всю нашу цепочку.
Когда данные изменяются, объект Watcher будет уведомлен об обновлении, и объект Watcher вызовет функцию рендеринга для повторного рендеринга. Поскольку Vue2.0 использует виртуальный DOM, при изменении данных будет повторно отображаться только измененная часть.Не нужно беспокоиться о проблемах с производительностью, вызванных повторным рендерингом всего компонента, поэтому для всего рендеринга требуется Объект Watcher, который следует поддерживать вместо объекта В Vue1.0 каждая директива соответствует объекту Watcher.
1. Почему мой ответ с данными иногда не работает?
Поскольку установление этой отзывчивости осуществляется при рендеринге компонента Vue, добавление атрибутов к данным в коде не может добиться отзывчивости, потому что эти атрибуты не добавляются сеттерами и геттерами, и объект Watcher не может быть уведомлен при его изменении. обновить.
Если нам нужно добавить адаптивное свойство после рендеринга компонента, нам нужно использоватьVue.$set(obj,'name',value)чтобы добавить свойства к объектам, уже находящимся в объекте данных. То есть корневой атрибут в данных должен быть определен с самого начала, иначе не может быть реализована отзывчивость.
Например:
мы можем пройтиVue.$setМетод добавляет в диалоговое окно атрибут обратного вызова, но не может добавить корневой атрибут реагирующих данных productId (при условии, что атрибут productId не определен в начале).
2. Почему вычисляемые свойства также реагируют?
В Vue 2.0 при изменении данных объект Watcher вызывает функцию рендеринга для повторного рендеринга, поэтому места, где используются вычисляемые свойства, также будут пересчитаны, таким образом реализуя отзывчивость.
3. Почему ответ иногда задерживается?
Например, когда мы перейдем к получению DOM сразу после изменения данных, мы обнаружим, что полученный DOM, похоже, не изменился, потому что, когда данные изменяются, Vue помещает изменения данных в очередь и ждет, пока следующий «тик». Выполните обновление DOM, чтобы избежать повторных обновлений DOM. Если у нас есть какие-то операции, которые зависят от обновленного DOM, мы можем поместить эти операции в качестве обратных вызовов вvm.$nextTick(callback), так что наша функция обратного вызова будет выполнена на следующем «тике».
Далее давайте взглянем на конкретную реализацию кода.
Init
Начнем с создания экземпляра Vue.
Как видите, при создании экземпляра Vue он будет вызыватьсяthis._initметод, см. далееthis._initключевой код в методе.
Здесь вызывается метод initState, давайте посмотрим, что делает метод initState.
Видно, что в методе initState вызываются initProps, initMethods, initData, initComputed, initWatch и другие методы. Они инициализируются на основе реквизитов, методов, данных, вычисляемых, наблюдаемых и т. д. компонента.
В основном мы фокусируемся на методе initData.
Во-первых, данные в параметрах компонента будут присвоеныvm._data, который затем выполнитobserve(data,true), а затем посмотрите, как определяется метод наблюдения.
Observer
если значение уже имеетobЕсли это объект, он вернет значение.ob, в противном случае после ряда суждений (например, является ли значение массивом или объектом, может ли значение быть расширено, является ли значение экземпляром Vue и т. д.), используйте значение для создания объекта Observer и возврата его . Далее посмотрите, как определяется класс Observer.
Здесь сначала объекту Observer будет присвоен новый объект Dep.Объект Dep используется для обработки зависимостей данных.У него есть идентификатор и подпрограммы (для сбора зависимостей). Затем метод def передаст defineProperty объекту Observer какobАтрибуты добавляются к стоимости. Затем оцените, является ли значение массивом, если да, вызовите методObserverArray, чтобы вызвать метод наблюдения для элементов в массиве; если это не массив, вызовите метод walk. Метод walk будет циклически вызывать метод definereactivity для свойств в значении. Давайте взглянем на ключевой код в definereactivity.
Во-первых, мы рассмотрим атрибут значения (let childOb = !shallow && observe(val)). В definereactivity вызывается метод defineProperty, чтобы установить геттер и сеттер для значения, чтобы мы могли перехватить получение и присвоение значения. То есть, когда мы используем значение, мы будем вызывать геттер, чтобы получить значение, а когда мы присваиваем значение значению, мы будем вызывать сеттер вместо того, чтобы назначать его напрямую, как раньше.
Когда геттер вызывается, еслиDep.targetне пуст, он вызоветdep.dependметод, который будет вызываться в методе зависимостиDep.target.addDepМетод (addDep — это метод класса Watcher) помещает объект dep в массив newDeps Dep.target и вызывает метод addSub класса Dep, чтобы поместить объект Watcher в массив subs, используемый для записи зависимостей в Dep.target. объект. тогда позвонюchildOb.dep.depend()Соберите объект Watcher в объект dep значения childOb.
Одна из проблем заключается в том, что объект dep для childOb является dep в классе Observer, и когда мы вызываем сеттер и вызываем dep.notify() для уведомления Watcher, который зависит от данных, используется dep, определенный в методе definereactivity. поэтому значение этого шага временно неясно и может иметь другое применение.
Когда объект вызывает геттер Watcher, приведенный выше код может зависеть от значения собранного Watcher.
Давайте снова посмотрим на сеттер. Сеттер сначала устанавливает новое значение, затем повторно наблюдает новое значение и, наконец, вызываетdep.notify()Уведомлять объекты Watcher, которые зависят от данных, для вызова метода обновления.
Render
Далее давайте взглянем на процесс рендеринга компонентов Vue.
В методе _init он будет вызыватьсяvm.$mountМетод компилирует шаблон или el в функцию рендеринга. Эта сгенерированная функция рендеринга будет вvm._renderМетод вызывается для создания объекта VNode. Затем найдите разницу с помощью алгоритма DOM Diff, чтобы сгенерировать настоящее дерево DOM для рендеринга.
Давайте взглянем на конкретный процесс реализации и посмотрим, что делает метод $mount.
В методе $mount вызывается метод compileToFunctions для создания рендеринга и staticRenderFns. Render — это функция рендеринга, а staticRenderFns — это массив, содержащий функции, сгенерированные узлами VNode, которые не изменяются.
Затем метод $mount вызывает метод mount, который, в свою очередь, вызывает метод mountComponent.
Вы можете видеть, что в методе mountComponent будет создан новый объект Watcher и передан в функцию updateComponent, которая вернетvm._update(vm._render(), hydrating).
мы знаем раньшеvm._renderМетод вызовет сгенерированную функцию рендеринга, чтобы вернуть объект VNode,vm._updateметод вызоветvm.__patch__Метод сравнивает этот объект VNode с предыдущим объектом VNode и отображает разницу в реальном дереве DOM.
Watcher
Наконец, взгляните на определение класса Watcher.
Во-первых, он установит геттер в expOrFn.Как видно из вышеизложенного, при рендеринге этот expOrFn является функцией updateComponent. Затем будет вызван метод get.
В методе get сначала вызывается функция pushTarget для установки объекта Watcher в качестве Dep.target, а затем вызывается функция getter для получения значения, то есть вызовvm._update(vm._render(), hydrating), который вызывает функцию рендеринга, сгенерированную функцией compileToFunctions. При вызове функции рендеринга данные, используемые в шаблоне, будут получены, тем самым сработав геттер наблюдателя данных.
Из-за настройкиDep.target, поэтому, когда срабатывает геттер, объект данных Dep будет собирать объект Watcher в качестве зависимости, тем самым завершая сбор зависимостей рендеринга. Всякий раз, когда мы идем изменять реактивные данные, сеттер будет проходитьdep.notifyдля вызова метода обновления Watcher. После вызова функции получения функция popTarget будетDep.targetЗаглушка.
Вы можете видеть, что в методе update будет вызываться метод run или метод queueWatcher, который добавит объект Watcher в очередь и вызовет его метод run в nextTick. Таким образом, эти два метода в конечном итоге вызовут метод run объекта Watcher. В методе run снова вызывается метод get объекта Watcher для повторной выборки значения и сбора зависимостей. Как вы можете видеть выше, метод get объекта Watcher вызовет функцию получения, которая вызовет функцию получения.vm._update(vm._render(), hydrating), тем самым перерисовывая.
Таким образом, когда мы изменяем данные, корректные изменения DOM завершаются.