предисловие
Использование Vue часто затрагивает и использует жизненный цикл в ежедневной разработке.Официальный документ объясняет жизненный цикл следующим образом:
Каждый экземпляр Vue при создании проходит ряд процессов инициализации — например, ему необходимо настроить прослушиватели данных, скомпилировать шаблоны, смонтировать экземпляр в DOM и обновить DOM при изменении данных и т. д. В то же время во время этого процесса также запускаются некоторые функции, называемые хуками жизненного цикла, что дает пользователям возможность добавлять свой собственный код на разных этапах.
Точно так же, как процесс жизни, старости, болезни и смерти, Vue также имеет ряд процессов от инициализации до монтирования компонентов, обновления компонентов и уничтожения компонентов, а хук жизненного цикла — это функция, которая позволяет разработчикам, когда Vue достигает определенного период времени делать что-то
Наиболее распространенным является отправка ajax-запроса в смонтированный хук для получения данных, требуемых компонентом текущей страницы.
Но для продвинутого Vue.js определенно недостаточно знать только написание жизненного цикла и соответствующее время запуска.Почему функция ловушки не может быть стрелочной функцией?Почему нельзя получить определенные данные в данных? Мы передаем это. Действительно ли данные, полученные в данных, хранятся непосредственно под этим, и как Vue достигает неперцептивного мониторинга/отвязки событий?
В этой статье я углублюсь в исходный код Vue и проанализирую жизненный цикл Vue из исходного кода.
Скриншоты исходного кода в этой статье сохраняют только основную логику.Полный исходный адрес
версия vue: 2.5.21
Обзор исходного кода
Когда мы создаем экземпляр Vue в main.js, мы пройдем некоторую логику, а затем введем функцию _init, чтобы начать жизненный цикл Vue. Фактически, мы можем примерно увидеть, как работает Vue, по именам этих функций. Далее мы проанализируйте, что делает каждая функция одна за другой.
Объединить элементы конфигурации
Как видно из рисунка выше, первым делом в жизненном цикле является слияние элементов конфигурации, а для корневых экземпляров и экземпляров компонентов Vue обрабатывает это по-разному (в main.js new Vue генерирует корневой экземпляр, остальные все экземпляры компонентов), в параметре options, переданном корневым экземпляром, не будет атрибута _isComponent, иначе он будет истинным (время создания экземпляра другое, и переданные параметры также разные, заинтересованные друзья могут просмотреть соответствующие примерная статья)
В целях ненужных помех, нет введения vue-router, vuex
Элементы конфигурации слияния корневого экземпляра
Для логики, что корневой экземпляр примет значение false, введитеmergeOptionsФункция, объединяйте различные параметры конфигурации Vue, такие как миксины, свойства, методы, часы, вычисляемые, хуки жизненного цикла и т. д. Это первая конфигурация слияния во всем проекте. Vue сохранит все стратегии слияния в объекте strats, затем по очереди пройдёт одно и то же свойство текущего экземпляра и родителя, а затем перейдёт к началу, чтобы найти стратегию слияния, соответствующую этому свойству.
Через точки останова вы можете видеть, что стратегии сохраняют множество объединенных стратегий.
Нам не нужно читать каждую стратегию слияния, старайтесь сосредоточиться на всем процессе, не собирайте семена кунжута и не теряйте арбуз. При первом слиянии Vue пройдетresolveConstructorOptions(vm.constructor)Получить конструктор Vueстатические свойстваПараметры в качестве родителя, эти параметры содержат некоторые предустановленные элементы конфигурации, а дочерний элемент — это некоторые параметры, которые мы передали при создании экземпляра корневого экземпляра, и соответствующий пример выше.render函数
Предустановленные элементы конфигурации Vue используются в качестве первого родителя:
Корневой экземпляр инстанцирует входящие параметры:
Стратегия слияния корневого экземпляра на самом деле очень проста.Главное — просто объединить некоторые встроенные элементы конфигурации фреймворка Vue и параметры, переданные разработчиком, создающим экземпляр конструктора Vue в main.js, как Атрибут $options корневого экземпляра.
Элементы конфигурации слияния экземпляра компонента
Элемент конфигурации слияния экземпляров компонентов отсутствует_initВ функции, поскольку экземпляр компонента отличается от корневого экземпляра, экземпляр компонента создается конструктором компонента, в то время как корневой экземпляр создается конструктором Vue, а конструктор компонента наследуется от Vue. .extend Метод наследования конструктора Vue, я нарисовал картинку для простоты понимания
Vue делает это в соответствии с шаблоном объектно-ориентированного проектирования.Компонент по сути является функцией-конструктором (далее его можно рассматривать как класс), поэтому для добавления нескольких идентичных компонентов на страницу нужно всего лишь многократно создавать экземпляр конструктора компонента. ., и экземпляры могут быть независимыми друг от друга
Еще одним преимуществом объектной ориентации является то, что она может обеспечить наследование.В среде Vue конструктор компонента наследует конструктор Vue, так что конструктор компонента может получить некоторые элементы конфигурации, созданные в конструкторе Vue.
Элемент конфигурации слияния экземпляров компонентов находится вsrc/core/global-api/extend.js, тоже позвонюmergeOptionsЭлемент конфигурации слияния экземпляра компонента объединит встроенный элемент конфигурации платформы Vue с текущим элементом конфигурации компонента и назначит его параметрам статического свойства конструктора компонента.
Возвращаясь снова к mergeOptions, вот лишь пример стратегии слияния жизненного цикла, вставьте исходный код напрямую и прикрепите блок-схему для облегчения понимания.
Здесь я использую родитель вместо родительского компонента, потому что компоненты Vue обычно наследуются от конструктора Vue, а не от родительского компонента., из блок-схемы видно, что Vue гарантирует, что функции жизненного цикла всегда представляют собой массив и расположены в порядке родительский => дочерний, Vue будет проходить по этому массиву при выполнении определенного жизненного цикла и выполнять функции по очереди , поэтому, когда мы находимся в функции жизненного цикла, она определена в одном и том же жизненном цикле в конструкторе Vue и конструкторе компонентов, и функция в конструкторе Vue будет выполняться первой.
После наследования конструктора Vue будет создан экземпляр подкомпонента для создания экземпляра компонента, а затем введена функция _init, когда _isComponent имеет значение true, она будет выполнена.initInternalComponent, он создаст свойство $options для экземпляра компонента, указывающее на параметры статического свойства конструктора дочернего компонента,Таким образом, элементы конфигурации текущего компонента и встроенные элементы конфигурации платформы Vue (включая глобальные компоненты, глобальные примеси) могут быть доступны через атрибут $options экземпляра компонента.
резюме
- Первым делом в жизненном цикле является слияние элементов конфигурации, а сроки слияния корневых экземпляров и экземпляров компонентов различны.
- Корневой экземпляр объединяется во время создания нового Vue, а встроенные элементы конфигурации Vue объединяются с элементами конфигурации, передаваемыми новым Vue.
- Для экземпляра компонента сначала будет создан конструктор подкомпонента, а для наследования конструктора Vue будет вызываться Vue.extend.При наследовании встроенные элементы конфигурации Vue и элементы конфигурации компонента будут объединены, и результат будет сохранен в опциях конструктора.свойств, а затем вход в метод initInternalComponent при создании экземпляра компонента укажет $options экземпляра компонента на свойство options конструктора компонента
- Фреймворк Vue будет выполнять разные стратегии слияния в соответствии с разными конфигурациями.
Ошибка в среде разработки прокси
Войдет в непроизводственную средуinitProxyФункция через прокси-сервер ES6 для выполнения уровня перехвата экземпляра vm, основная функция заключается в создании некоторых пользовательских предупреждений для некоторых необоснованных конфигураций в среде разработки.
Многие разработчики столкнулись с вышеуказанной ошибкой.На самом деле, через перехватчик has Proxy в настоящее время, когда свойство не находится в экземпляре vm, но на него ссылается шаблон, Vue выдает несколько дружественных подсказок.
Инициализировать пользовательское событие
затем введитеinitLifecycle, в этой части не о чем говорить, инициализируйте некоторые состояния жизненного цикла и некоторые дополнительные свойства экземпляра, а затем введите пользовательское событие, которое инициализирует компонент
initEventsМонтируются только пользовательские события, то есть ненативные события, отслеживаемые v-on в компоненте (нативные DOM-события отсутствуют вinitEventsсмонтирован в). Vue сохранит пользовательские события, объявленные в этих родительских компонентах, в свойстве _parentListeners дочернего компонента (vm — это экземпляр дочернего компонента, _parentListeners находится вinitInternalComponentопределено в)
Введите updateComponentListeners и обнаружите, что Vue вызовет функцию добавления для регистрации всех пользовательских событий, а для компонентов функция добавления будетпозвони наДля достижения эффекта прослушивания пользовательских событий
//https://github.com/vuejs/vue/blob/dev/src/core/instance/events.js#L24
function add (event, fn) {
target.$on(event, fn)
}
//https://github.com/vuejs/vue/blob/dev/src/core/vdom/helpers/update-listeners.js#L83
//调用add注册自定义事件(后面3个参数可忽略)
add(event.name, cur, event.capture, event.passive, event.params)
beforeCreate
После добавления пользовательских событий введитеinitRender, определить слот и параметр createElement для функции рендеринга, а также превратить $attrs и $listeners Vue в адаптивные свойства.
затем выполнитcallHook(vm, 'beforeCreate'), вы можете буквально догадаться, что Vue вызовет функцию жизненного цикла beforeCreate в это время.При слиянии элементов конфигурации ранее упоминалось, что функция жизненного цикла в конечном итоге будет обернута в массив, поэтому на самом деле Vue также поддерживает этот Write
Функция callHook получит массив соответствующих функций жизненного цикла в свойстве $options в соответствии с переданными параметрами.Здесь передается beforeCreate, поэтому она получает все функции жизненного цикла, определенные в beforeCreate, затем последовательно проходит и использует метод вызова чтобы дать каждому Эта функция жизненного цикла привязана к этому контексту, поэтому функция жизненного цикла не может быть написана с помощью функции вырезания головы
Данные инициализации
Затем выполнитеinitInjections, эта часть используется для инициализации inject api, что подробно не поясняется из-за нечастого использования ежедневной разработки (на самом деле мне лень изучать -.-)
Затем он войдет в другую ключевую функциюinitState, он будет инициализировать реквизиты, методы, данные, вычислять, смотреть по очереди, мы объясним их один за другим
props
При обмене данными между компонентами родительский компонент передает параметры дочернему компоненту, а дочерний компонент должен определить реквизиты, чтобы принимать свойства, переданные родительским компонентом, а Vue предусматривает, что дочерний компонент не может изменять реквизиты, переданные от родительского компонента, потому что это нарушает единый элемент. Поток данных может затруднить управление между компонентами.Если реквизиты изменены в дочерних компонентах, Vue выдаст предупреждение.
И как Vue узнает, что разработчик изменил свойства реквизита? Причина в том, чтобы использовать установщик дескриптора доступа
Друзья, которые поняли принцип отзывчивости, должны быть знакомы с этим. Vue превратит объект реквизита в объект отклика, а четвертый параметр — настраиваемый сеттер. При изменении реквизита сработает сеттер. Об этом предупреждении сообщается. при нарушении одного потока данных
methods
Предупреждение для нестандартных методов, Vue определит процесс разработки, а затем все будет связывающим методом VM экземпляров, чтобы мы могли получить текущий экземпляр непосредственно через эту виртуальную машину
data
Когда дело доходит до наиболее важных данных, данные обычно хранят данные, которые должен использовать текущий компонент.За исключением корневого экземпляра, данные экземпляра компонента обычно являются функцией из-за характеристик ссылочного типа JS, если вы используете объект, когда есть несколько одинаковых компонентов, один из которых изменяет данные данных, будет отражаться на всех компонентах. Когда данные возвращают объект как функцию, при каждом выполнении генерируется новый объект, что может эффективно решить эту проблему.
При инициализации данных будет выполняться функция initData, которая будет выполнять заданную функцию данных внутри, использовать текущий экземпляр в качестве значения this и назначать его внутреннему свойству _data,Стоит отметить, что в процессе выполнения функции данных данные в вычисляемом не могут быть получены, потому что данные в вычисляемом в это время не были инициализированы.
Затем выполните прокси-функцию, ее функция состоит в том, чтобы сопоставить атрибуты vm._data с атрибутами vm и играть роль «прокси». Это напрямую написать форму этого [ключа] в процессе разработки. все еще использует геттер/сеттер, когда мы получаем доступ к этому [ключу], геттер будет запущен, указывая непосредственно на this._data[ключ], то же самое верно для установщика
Некоторые люди спросят, почему бы не написать это прямо на экземпляре виртуальной машины? Поскольку нам нужно управлять данными в унифицированном объекте, следующим шагом будет превращение _data в реагирующий объект с помощью наблюдения. Чтобы писать более лаконично во время разработки, Vue использует этот метод, который очень удобен.
computed
Когда вычисляемое значение инициализируется, Vue создает вычисляемый наблюдатель для каждого вычисляемого свойства.Только когда зависимость вычисляемого свойства изменяется, он уведомляет вычисляемый наблюдатель об обновлении вычисляемого свойства, чтобы данные можно было обновлять в режиме реального времени, не теряя времени. производительность., что также является отличной особенностью Vue.
watch
Когда часы инициализируются, в конечном итоге будет вызван метод $watch для создания пользовательского наблюдателя.При изменении отслеживаемого свойства пользовательский наблюдатель будет немедленно уведомлен о выполнении обратного вызова.
created
позвони сноваinitProvideОн будет выполнен после инициализации предоставленияcallHook(vm, 'beforeCreate'), Как и в случае с beforeCreate, поочередно пройтись по созданному массиву, определенному в $options, и выполнить функцию жизненного цикла.
На данный момент весь компонент создан.Фактически в это время вы можете взаимодействовать с бэкендом для получения данных.Однако реальные узлы DOM не отрисовывались, и некоторые интерактивные операции, которые должны взаимодействовать с DOM не может быть выполнено в созданном хуке, то естьНевозможно манипулировать DOM сгенерированного представления в созданном хуке
процесс монтирования
назад_initФункция, дошедшая до последней строки, будет судить, имеет ли $options атрибут el.В Vue-cli2 cli автоматически передает параметр el при использовании нового Vue.В Vue-cli3 этого не происходит. , но создает корень После экземпляра активно вызывается $mount и передается смонтированный узел.На самом деле, оба одинаковы.Вы также можете использовать $mount для ручного монтирования компонентов.
vue-cli2:
Vue-cli3:
$mount в конечном итоге выполнитсяmountComponentэта функция
только что из_initубежал от тирадыmountComponentэта яма
Я не буду подробно описывать монтаж компонентов здесь, я стараюсь сосредоточиться на жизненном цикле, заинтересованные друзья могут узнать об этом сами, или увидеть следующееСвязь
beforeMount
Когда компонент выполняет $mount и имеет точку монтирования и функцию рендеринга, срабатывает хук beforeMount для подготовки компонента к монтированию.
Функция updateComponent, которая рендерит представление
Затем Vue определит функцию updateComponent, которая является ядром всего монтирования и состоит из двух частей: функции _render и функции _update.
- Функция рендеринга в конечном итоге будет выполняться до
initRenderОпределенная функция createElement используется для создания vnode. - Функция обновления визуализирует vnode, сгенерированный вышеприведенной функцией рендеринга, в реальное дерево DOM и монтирует его в точке монтирования.
Первое выполнение updateComponent отобразит все дерево DOM, и страница будет полностью отображена в это время.
визуализировать наблюдатель
Затем будет создан экземпляр «наблюдателя за отрисовкой», updateComponent будет передан как функция обратного вызова, а функция updateComponet будет немедленно выполнена внутри.
Как следует из названия, наблюдатель используется для наблюдения.Короче говоря, наблюдатель рендеринга должен наблюдать, изменяются ли зависимые переменные в шаблоне, чтобы решить, следует ли обновлять страницу, а updateComponent — это функция, используемая для обновления страницы, поэтому передайте эту функцию в качестве обратного вызова. введите. Для адаптивных переменных в шаблоне (переменная a на рисунке ниже) наблюдатель за отрисовкой будет сохранен внутри (поскольку эти переменные могут изменить представление), после изменения переменной будет запущен установщик и, наконец, функция updateComponent. будет выполнен снова, чтобы обновить вид
mounted
Наблюдатель за рендерингом экземпляра вынесет решение после рендеринга страницы. Здесь следует отметить, чтоТолько корневой экземпляр будет истинным и вызовет установленный хук, когда экземпляр компонента запускает установленный хук?
Ответ дан сначала здесь, вsrc/core/vdom/create-component.jsЛовушка вставки (хук vnode для конкретного компонента) и Vue объявят массив InsertVnodeQueue для сохранения всех компонентных vnode. Всякий раз, когда vnode компонента визуализируется в DOM-узле, в этот массив добавляется элемент vnode. Когда все компоненты визуализируются После завершения смонтированный хук будет запущен в порядке дочерний => родитель (смонтированный хук самого внутреннего компонента срабатывает первым). затем обратно в_initметод и, наконец, активировать установленный хук корневого экземпляра. Заинтересованные студенты могут изучить его дальше.
На данный момент все данные инициализированы, DOM-узел отрендерен, далее будет представлен процесс обновления и уничтожения компонента.
обновление компонента
Возвращаясь к изображению mountComponent, при создании экземпляра наблюдателя за рендерингом Vue передает объект наблюдателю за рендерингом. Объект содержит метод before. Когда метод before выполняется, будет выполнен хук beforeUpdate. Когда этот метод быть казненным?
Как только зависимые переменные шаблона изменятся, это означает, что представление вот-вот изменится, сработает сеттер, а затем будет выполнен обратный вызов рендеринга наблюдателя, то есть updateComponent обновит представление. Перед выполнением этого обратного вызова Vue проверит, есть ли метод before, и если да, то он будет иметь приоритет Выполнить перед, а затем выполнить updateComponent для обновления представления
Vue поставит всех наблюдателей в очередь, flushSchedulerQueue будет проходить по этим наблюдателям по очереди, а у наблюдателей рендеринга будет метод before, который вызовет хук beforeUpdate.
Затем, когда все наблюдатели были пройдены, репрезентативные данные были обновлены, а представление обновлено.В это время он вызоветcallUpdatedHooks, выполнить обновленный хук
разрушение компонента
Предпосылка уничтожения компонента заключается в том, что происходит обновление представления.Vue определит разницу между vnode, который генерирует новое представление, и vnode, соответствующий старому представлению, а затем удалит те узлы, которые не нужно отображать в представлении. Этот процесс в конечном итоге вызовет метод экземпляра $destroy, соответствующий исходному коду.src/core/instance/lifecycle.js
Выполнять по порядку:
- Во-первых, хук beforeDestory будет выполнен напрямую, указывая, что узел готов к уничтожению, Это последний раз, когда нужно взаимодействовать с текущим экземпляром компонента.
- Затем будет найден родительский узел текущего компонента, а текущий узел будет удален из дочернего свойства родительского узла.
- Выйдите из наблюдателя за отрисовкой (vm._watcher хранит уникальный наблюдатель за отрисовкой для каждого компонента)
- Выйдите из других наблюдателей (наблюдатель за пользователем, наблюдатель с вычислением)
- Очищает узлы DOM, отображаемые этим экземпляром.
- выполнить уничтоженный хук
- Отменить регистрацию всех событий прослушивания ($off удалит все события прослушивания без передачи параметров)
Суммировать
На этом весь жизненный цикл Vue завершен.Наконец, давайте подытожим, что в основном делает каждый жизненный цикл, и перечислим это в строгом соответствии с внутренним порядком выполнения Vue.
-
beforeCreate: объединить элементы конфигурации, определенные разработчиком, с элементами конфигурации внутри Vue, инициализировать пользовательские события компонента, определить функцию createElement/инициализировать слот. -
created: Инициализировать инъекцию, инициализировать все данные (реквизиты -> методы -> данные -> вычисляемые -> часы), инициализировать предоставление -
beforeMount: Найдите, есть ли смонтированный узел, подготовьтесь к запуску рендеринга страницы/создайте экземпляр наблюдателя рендеринга в соответствии с функцией рендеринга. -
mounted: рендеринг страницы завершен -
beforeUpdate: переменные, от которых зависит наблюдатель рендеринга, изменились, готовы обновить представление. -
updated: Представления и данные обновлены. -
beforeDestroy: Выйти из наблюдателя, удалить узел DOM -
destroyed: отменить регистрацию всех событий прослушивания
На самом деле, если вы хотите полностью понять жизненный цикл Vue, вам также необходимо понять другие точки знаний, такие как монтирование компонентов, принципы адаптивности, и вам также может понадобиться понять принципы компиляции Vue. расширен более чем в дюжину раз.Небольшие очки знаний, но когда вы действительно поймете основные принципы Vue.js, я считаю, что это будет большим преимуществом для личного роста (после того, как, наконец, написал это, болит шея _:(´° ω°`” ∠) :_)
Двигайтесь вперед, будущее можно ожидать