В предыдущей главе мы узнали, чтоnew Vue(), внутреннее исполнениеthis._init()метод, этот метод находится вinitMixin(Vue)Определяется в пределах:
export function initMixin(Vue) {
Vue.prototype._init = function(options) {
...
}
}
при исполненииnew Vue()После выполнения запускается серия инициализаций._initметод, его реализация заключается в следующем:
let uid = 0
Vue.prototype._init = function(options) {
const vm = this
vm._uid = uid++ // 唯一标识
vm.$options = mergeOptions( // 合并options
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
...
initLifecycle(vm) // 开始一系列的初始化
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm)
initState(vm)
initProvide(vm)
callHook(vm, 'created')
...
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
Сначала нужно объяснить, каждый компонент представляет собойVueПодклассы конструкторов, этот объяснит, почему это так, позже. Давайте рассмотрим его пошагово сверху вниз, сначала определим_uidСвойство, которое является уникальным частным идентификатором свойства для каждого компонента при каждой его инициализации и иногда имеет некоторые эффекты.
Вот небольшой пример его использования для поиска всех братьев и сестер компонента и отбраковки самого себя:
<div>
...
<child-components />
<child-components /> // 找到它的兄弟组件
... 其他组件
<child-components />
</div>
Первый компонент для поиска должен быть определенnameсвойства, определения курсаnameАтрибуты также являются хорошей письменной привычкой. Сначала через собственный родительский компонент($parent)все подкомпоненты($children)отфильтровать то же самоеnameКомпоненты коллекции, на этот раз это один и тот же компонент, хотя иnameто же самое, но_uidразличных, и, наконец, в пределах набора в соответствии с_uidПросто избавься от себя.
Конфигурация параметров слияния
Вернитесь к основному квесту, а затем слейтеoptionsи смонтировать один на экземпляре$optionsАтрибуты. Слил что? Здесь есть два случая:
- Инициализировать новый Vue
в исполненииnew VueПри построении функции параметр является объектом, то есть пользовательской конфигурацией пользователя, он будет сочетаться сvueМетод прототипа, определенный ранее, глобальныйAPIсвойства; также глобальныеVue.mixinпараметры внутри, объедините их в новыйoptionsи, наконец, присвоить его новому свойству$options.
- Инициализация дочернего компонента
Если это инициализация дочернего компонента, в дополнение к объединению вышеперечисленного, параметры родительского компонента также будут объединены, если родительский компонент определен в дочернем компоненте.event,propsи т.п.
После объединения вы можетеthis.$options.dataдоступ к пользовательскимdataфункция,this.$options.nameДоступ к определяемому пользователем имени компонента, это комбинированное свойство важно и будет часто использоваться.
Далее будет последовательно выполняться куча методов инициализации, в первую очередь эти три:
1. initLifecycle(vm)
2. initEvents(vm)
3. initRender(vm)
1. initLifecycle(vm): Основная функция заключается в подтверждении отношения родитель-потомок компонента и инициализации некоторых свойств экземпляра.
export function initLifecycle(vm) {
const options = vm.$options // 之前合并的属性
let parent = options.parent;
if (parent && !options.abstract) { // 找到第一个非抽象父组件
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$parent = parent // 找到后赋值
vm.$root = parent ? parent.$root : vm // 让每一个子组件的$root属性都是根组件
vm.$children = []
vm.$refs = {}
vm._watcher = null
...
vm._isDestroyed = false
vm._isBeingDestroyed = false
}
vueЭто разработка на основе компонентов, поэтому текущий экземпляр может одновременно подкомпоненты других компонентов также могут быть другими компонентами родительского компонента.
Во-первых, будет найден первый родительский компонент неабстрактного типа текущего компонента, поэтому, если текущий компонент имеет родителя, а текущий компонент не является абстрактным компонентом, он будет продолжать поиск до тех пор, пока не будет найден, и найденный родительский компонент будет присвоено свойству экземпляраvm.$parent, то текущий экземплярpushк найденному родителю$childrenСвойства экземпляра, тем самым устанавливая отношения родитель-потомок компонента. некоторые из следующих_Начало — это частный атрибут экземпляра, мы помним, что он определен здесь, а конкретное значение будет объяснено позже при его использовании.
2. initEvents(vm): Основная функция заключается в использовании родительского компонентаv-onили@Зарегистрированные пользовательские события добавляются в концентратор событий дочернего компонента.
Сначала посмотрите на место, где определен этот метод:
export function initEvents (vm) {
vm._events = Object.create(null) // 事件中心
...
const listeners = vm.$options._parentListeners // 经过合并options得到的
if (listeners) {
updateComponentListeners(vm, listeners)
}
}
Мы должны сначала знать, что вvueЕсть два типа событий, и они обрабатываются по-разному:
2.1родное событие
в исполненииinitEventsНа предыдущем этапе компиляции шаблона будет определено, что встречающеесяhtmlМетка или название компонента, если естьhtmlЯрлыки превратятся в настоящиеdomиспользовать послеaddEventListenerЗарегистрируйтесь для участия в собственных событиях браузера. Событие привязки mountdomПоследний этап, это просто этап инициализации, здесь в основном имеют дело с пользовательскими событиями, которые являются другим видом, вот утверждение, не обращайте внимания на неправильный порядок выполнения.
2.2пользовательское событие
происходит слияниеoptionsПосле этапа дочерние компоненты могутvm.$options._parentListenersПрочитайте пользовательское событие, переданное из родительского компонента:
<child-components @select='handleSelect' />
Формат передаваемых данных о событии:{select:function(){}}вот так, вinitEventsопределение методаvm._eventsИспользуется для хранения коллекции переданных событий.
Внутренне исполняемый методupdateComponentListeners(vm, listeners)в основном выполнятьupdateListenersметод. Этот метод имеет два тайминга выполнения, первый — это текущая фаза инициализации, а другой — финальная.patchТакже используются родные события того времени. Его роль заключается в сравнении списка новых и старых событий для определения добавления и удаления событий и обработки модификаторов событий. Теперь он в основном смотрит на добавление пользовательских событий. Его роль заключается в использовании ранее определенных$on,$emitметод для завершения передачи событий компонента родитель-потомок (подробный принцип будет объяснен в глобальномAPIунифицированное описание главы). первое использование$onпрошлоеvm.eventsСоздайте элемент коллекции массива с именем пользовательского события в центре событий. Каждый элемент массива представляет собой функцию обратного вызова, соответствующую имени события, например:
vm._events.select = [function handleSelect(){}, ...] // 可以有多个
После завершения регистрации используйте$emitСобытие выполнения метода:
this.$emit('select')
Во-первых, он будет прочитан в центре событий$emitпервый параметр методаselectМассив коллекции объектов, а затем последовательно выполнять каждую функцию обратного вызова в массиве для завершения$emitСписок задач.
Я не знаю, заметили ли выthis.$emitЭтот метод срабатывает в текущем экземпляре компонента, поэтому принцип события может отличаться от того, что понимает большинство людей: дело не в том, что родительский компонент слушает, а дочерний компонент отправляет событие родительскому компоненту.
Вместо этого дочерний компонент отправляет события своему собственному экземпляру только потому, что функция обратного вызова определена в области действия родительского компонента, поэтому выполняется метод, определенный в родительском компоненте, что создает иллюзию связи событий между родителем и ребенок. Зная эту принципиальную характеристику, мы можем сделать еще несколькоcoolтакие вещи, как:
<div>
<parent-component> // $on添加事件
<child-component-1>
<child-component-2>
<child-component-3 /> // $emit触发事件
</child-component-2>
</child-components-1>
</parent-component>
</div>
мы можем вparent-componentвнутреннее использование$onДобавить событие в концентратор событий текущего экземпляра, находясь вchild-components-3найдено внутриparent-componentЭкземпляр компонента и вызовите соответствующее событие в его центре событий для обеспечения связи между компонентами, ответ — да! Этот принцип оказался полезным при разработке библиотек компонентов.
3. initRender(vm): Основная функция – монтироватьrenderфункционировать, чтобыvnodeМетоды.
export function initRender(vm) {
vm._vnode = null
...
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false) //转化编译器的
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true) // 转化手写的
...
}
Основная функция – креплениеvm._cа такжеvm.$createElementДва метода, они отличаются только последним параметром, оба метода могутrenderфункционировать, чтобыvnode, вы должны увидеть разницу в названии,vm._cпреобразуется компиляторомtemplateпреобразованныйrenderфункция; иvm.$createElementПреобразование определяется пользователемrenderфункции, такие как:
new Vue({
data: {
msg: 'hello Vue!'
},
render(h) { // 这里的 h 就是vm.$createElement
return h('span', this.msg);
}
}).$mount('#app');
renderаргументы функцииhто естьvm.$createElementметод для преобразования внутренне определенных данных древовидной структуры вVnodeпример.
4. callHook(vm, 'beforeCreate')
Наконец, мы хотим выполнить первый хук жизненного цикла экземпляра.beforeCreate,здесьcallHookВ чем принцип? Мы объясним в главе о жизненном цикле позже. Сейчас нам нужно только знать, что он будет выполнять определенный пользователем метод жизненного цикла.mixinСмешанные в также выполнены.
Итак, инициализация первой фазы хука жизненного цикла экземпляра завершена, в одном предложении это в основном объясняет, что они сделали:
- initLifecycle(vm): Подтвердить компонент (также
vueпример) отношения родитель-потомок - initEvents(vm): передавать пользовательские события из родительских компонентов в дочерние.
- initRender(vm): предоставляет
renderфункционировать, чтобыvnodeМетоды - beforeCreate: выполнить компонент
beforeCreateфункция ловушки
Наконец, сvueЗавершают эту главу простые вопросы для интервью:
Интервьюер улыбнулся и вежливо спросил:
- Не могли бы вы, пожалуйста
beforeCreateКрюкthisдоступ кdataпеременная определена в , почему и что может делать этот хук?
Дайте отпор:
- недоступен, потому что
vueФаза инициализации, на этот разdataПеременные в не были смонтированы вthis, на этот раз значение доступа будетundefined.beforeCreateЭтот хук редко используется в развитии бизнеса в обычное время, и он похож на внутренний плагинinstanllметод переданVue.useКогда метод установлен, он обычно выбирается вbeforeCreateВыполнить внутри этого хука,vue-routerа такжеvuexВот что он делает.
Следующий:Анализ принципа Vue (3): что вы делали перед созданием во время инициализации?
Просто нажмите «Нравится» или подпишитесь, это легко найти~
Ссылаться на:
Всесторонний углубленный анализ исходного кода Vue.js
Vue.js объясняется простым способом
Поделитесь библиотекой компонентов со всеми, вы можете ею пользоваться ~ ↓