предисловие
В этом году OKR установила KR, чтобы каждый квартал делиться технологиями, связанными с фронтендом, до 2020 года еще больше десяти дней. нет другого выбора, кроме как говорить об этом.Я обычно использую Vue.js с высокой частотой, но я не очень понимаю его внутренние принципы.
Поскольку эта статья представляет собой речь для обмена интерфейсными технологиями, я изо всех сил стараюсь не вставлять исходный код Vue.js, потому что вставка кода скучна и неэффективна при реальном обмене, и она больше выражается в форме картинки и текст.
Поделитесь целями:
- Понимать механизм компонентизации Vue.js
- Понимать принципы реактивных систем в Vue.js.
- Понять принцип Virtual DOM и Diff в Vue.js.
Поделиться основным докладом:Принцип анализа Vue.js framework.key
Обзор Vue.js
Vue — это прогрессивная среда MVVM для создания пользовательских интерфейсов. как понять渐进式
Шерстяная ткань? Дополнительные последствия: наименьшее количество обязательных утверждений.
Vue.js включает в себя декларативный рендеринг, компонентную систему, маршрутизацию на стороне клиента, крупномасштабное управление состоянием, инструменты сборки, сохранение данных, кросс-платформенную поддержку и т. д., но в реальной разработке от разработчиков не требуется обязательного соблюдения конкретной функции, но постепенно расширяться в соответствии со спросом.
Основная библиотека Vue.js заботится только о рендеринге представлений, и благодаря своей прогрессивной природе Vue.js легко интегрируется со сторонними библиотеками или существующими проектами.
компонентный механизм
Определение: Компонент — это независимая инкапсуляция функции и стиля, позволяющая расширять элементы HTML, чтобы можно было повторно использовать код, что делает разработку более гибкой и эффективной.
Как и HTML-элементы, компоненты Vue.js имеют свойства (prop) и события, передаваемые извне. Кроме того, компоненты также имеют свое собственное состояние (данные) и вычисляемые свойства (вычисляемые), вычисляемые на основе данных и состояния. внешний вид компонента и логика взаимодействия.
Передача данных
Область между каждым компонентом изолирована, что означает, что данные между компонентами не должны иметь отношения ссылки.Даже если отношение ссылки есть, компонент не может оперировать другими данными вне компонента. В Vue разрешено передавать компоненты внутриprop
данные, которые должны быть явно объявлены внутри компонентаprop
поле, объявите дочерний компонент следующим образом:
<!-- child.vue -->
<template>
<div>{{msg}}</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: 'hello world' // 当default为引用类型时,需要使用 function 形式返回
}
}
}
</script>
Родительский компонент передает данные этому компоненту:
<!-- parent.vue -->
<template>
<child :msg="parentMsg"></child>
</template>
<script>
import child from './child';
export default {
components: {
child
},
data () {
return {
parentMsg: 'some words'
}
}
}
</script>
доставка события
Vue внутренне реализует систему шины событий, а именноEventBus
. В Vue EventBus можно использовать как концепцию коммуникационного моста, и каждый экземпляр компонента Vue наследуетEventBus
, может принимать события$on
и отправить событие$emit
.
Как и в приведенном выше примере, компонент child.vue хочет изменить данные parentMsg компонента parent.vue Что мне делать? Чтобы обеспечить отслеживаемость потока данных, не рекомендуется напрямую изменять поле msg свойства в компоненте, и примером является нессылочный тип String, который не может быть изменен напрямую. событие изменения parentMsg необходимо передать в child.vue , пусть child.vue инициирует событие изменения parentMsg. Такие как:
<!-- child.vue -->
<template>
<div>{{msg}}</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: 'hello world'
}
},
methods: {
changeMsg(newMsg) {
this.$emit('updateMsg', newMsg);
}
}
}
</script>
Родительский компонент:
<!-- parent.vue -->
<template>
<child :msg="parentMsg" @updateMsg="changeParentMsg"></child>
</template>
<script>
import child from './child';
export default {
components: {
child
},
data () {
return {
parentMsg: 'some words'
}
},
methods: {
changeParentMsg: function (newMsg) {
this.parentMsg = newMsg
}
}
}
</script>
Родительский компонент parent.vue передает событие updateMsg дочернему компоненту child.vue.Когда создается экземпляр дочернего компонента, дочерний компонент использует событие updateMsg$on
Функция регистрируется внутри компонента, и когда нужно инициировать событие, функция вызываетсяthis.$emit
для запуска события.
В дополнение к передаче событий между родительскими и дочерними компонентами вы также можете использовать экземпляр Vue для создания моста передачи данных для многоуровневых родительских и дочерних компонентов, таких как:
const eventBus = new Vue();
// 父组件中使用$on监听事件
eventBus.$on('eventName', val => {
// ...do something
})
// 子组件使用$emit触发事件
eventBus.$emit('eventName', 'this is a message.');
Кроме$on
а также$emit
Кроме того, система шины событий также предоставляет два других метода:$once
а также$off
, все события следующие:
- $on: отслеживать и регистрировать события.
- $emit: вызвать событие.
- $once: Зарегистрируйте событие, разрешите запуск события только один раз и удалите событие сразу после окончания действия триггера.
- $off: удалить событие.
Распространение контента
Vue реализует наборWeb Components 规范草案
система распространения контента, скоро<slot>
Элементы служат выходами, которые несут распределенный контент.
Слот слот также является HTML-шаблоном компонента, отображать этот шаблон или нет, и как его отображать, определяет родительский компонент. Фактически, здесь указаны две основные проблемы слота: отображать ли и как отображать.
Слоты делятся на слоты по умолчанию и именованные слоты.
слот по умолчанию
Также известный как одиночный слот, анонимный слот, в отличие от именованного слота, этот тип слота не имеет определенного имени, и компонент может иметь только один слот этого типа.
Такие как:
<template>
<!-- 父组件 parent.vue -->
<div class="parent">
<h1>父容器</h1>
<child>
<div class="tmpl">
<span>菜单1</span>
</div>
</child>
</div>
</template>
<template>
<!-- 子组件 child.vue -->
<div class="child">
<h1>子组件</h1>
<slot></slot>
</div>
</template>
Как и выше, дочерний компонентslot
Метка будет передана родительским компонентомdiv.tmpl
заменять.
именованный слот
Анонимные слоты не имеют атрибута имени, поэтому их называют анонимными слотами. Затем, если слот добавляется с атрибутом имени, он становится именованным слотом. Именованные слоты могут появляться в компоненте N раз и появляться в разных позициях, просто используйте разные атрибуты имени, чтобы различать их.
Такие как:
<template>
<!-- 父组件 parent.vue -->
<div class="parent">
<h1>父容器</h1>
<child>
<div class="tmpl" slot="up">
<span>菜单up-1</span>
</div>
<div class="tmpl" slot="down">
<span>菜单down-1</span>
</div>
<div class="tmpl">
<span>菜单->1</span>
</div>
</child>
</div>
</template>
<template>
<div class="child">
<!-- 具名插槽 -->
<slot name="up"></slot>
<h3>这里是子组件</h3>
<!-- 具名插槽 -->
<slot name="down"></slot>
<!-- 匿名插槽 -->
<slot></slot>
</div>
</template>
Как и выше, тег слота заменит соответствующий контент в соответствии со значением атрибута слота контента, переданного родительским контейнером в дочерний тег.
На самом деле слот по умолчанию также имеет значение атрибута имени, т.е.default
, значение имени указанного слота также является значением по умолчанию, что также может отображать содержимое, переданное в родительский компонент без указанного слота.
слот с прицелом
Слот с областью действия может быть слотом по умолчанию или именованным слотом.Разница в том, что слот с областью действия может привязывать данные к тегу слота, чтобы его родительский компонент мог получать данные дочернего компонента.
Такие как:
<template>
<!-- parent.vue -->
<div class="parent">
<h1>这是父组件</h1>
<current-user>
<template slot="default" slot-scope="slotProps">
{{ slotProps.user.name }}
</template>
</current-user>
</div>
</template>
<template>
<!-- child.vue -->
<div class="child">
<h1>这是子组件</h1>
<slot :user="user"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: '小赵'
}
}
}
}
</script>
В приведенном выше примере, когда дочерний компонент выполняет рендеринг слота по умолчанию, он передает пользователю данных тег слота.В процессе рендеринга родительский компонент может передатьslot-scope
Свойство получает пользовательские данные и отображает представление.
Принцип реализации слота: когда дочерние компонентыvm
При создании экземпляра содержимое тега слота, переданного родительским компонентом, получается и сохраняется вvm.$slot
, слот по умолчаниюvm.$slot.default
, именованный слотvm.$slot.xxx
, xxx — имя слота, когда компонент выполняет функцию рендеринга, он встречает<slot>
этикетка, использование$slot
Замените содержимое в слоте. В это время вы можете передавать данные для слота. Если есть данные, слот можно использовать как слот с заданной областью.
На данный момент отношения между родительским и дочерним компонентами выглядят следующим образом:
Рендеринг шаблона
Core Vue.js является декларативным рендерингом, а также оказать императив, декларативное оказание необходимостью сказать программу, что мы хотим, что мы хотим, что другие вещи позволяют программе сделать самим собой. Команда рендеринга, команда должна выполнить шаг программы по шагу в соответствии с командой рендеринга. Примеры отличаются следующим образом:
var arr = [1, 2, 3, 4, 5];
// 命令式渲染,关心每一步、关心流程。用命令去实现
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(arr[i] * 2);
}
// 声明式渲染,不用关心中间流程,只需要关心结果和实现的条件
var newArr1 = arr.map(function (item) {
return item * 2;
});
Vue.js реализует такие инструкции, как «если», «для», «события», «привязка данных» и т. д., что позволяет декларативно отображать данные вне представлений, используя краткий синтаксис шаблона.
Компиляция шаблона
Зачем компилировать шаблон? На самом деле синтаксис шаблона в нашем компоненте не может быть разобран браузером, потому что это не правильный синтаксис HTML, а компиляция шаблона означает компиляцию шаблона компонента в исполняемый код JavaScript, то есть преобразование шаблона в реальный рендеринг функция .
Сборка шаблона делится на три этапа,parse
,optimize
,generate
, что в итоге порождаетrender
функция.
parse
Этап: Использование регулярного выраженияtemplate
Выполняйте синтаксический анализ строк для получения таких данных, как инструкции, классы и стили, и генерируйте абстрактное синтаксическое дерево AST.
optimize
Стадия: найдите статические узлы в AST для маркировки и оптимизации для сравнения в процессе исправления следующих VNodes. Узлы, помеченные как статические, будут игнорироваться в следующем алгоритме сравнения без детального сравнения.
generate
Этап: Генерация строки функции рендеринга путем объединения в соответствии со структурой AST.
предварительно скомпилировано
Для компонентов Vue компиляция шаблона компилируется только один раз при создании экземпляра компонента и не будет компилироваться после создания функции рендеринга. Таким образом, компиляция снижает производительность среды выполнения компонента. Целью компиляции шаблона является толькоtemplate
превратиться вrender function
, и этот процесс может быть завершен в процессе построения проекта.
Напримерwebpack
изvue-loader
зависел отvue-template-compiler
модули в процессе сборки webpack будутtemplate
Он предварительно компилируется в функцию рендеринга, и процесс компиляции шаблона можно пропустить непосредственно во время выполнения.
Оглядываясь назад, среда выполнения должна быть просто функцией рендеринга, и после того, как мы выполнили предварительную компиляцию, нам просто нужно убедиться, что функция рендеринга генерируется в процессе сборки. Подобно React, добавляяJSX
синтаксический сахар для компилятораbabel-plugin-transform-vue-jsx
После этого мы можем использовать в компоненте VueJSX
Синтаксис напрямую пишет функцию рендеринга.
<script>
export default {
data() {
return {
msg: 'Hello JSX.'
}
},
render() {
const msg = this.msg;
return <div>
{msg}
</div>;
}
}
</script>
Для вышеуказанного компонента после использования JSX можно напрямую использовать html-теги в JS-коде, а после объявления функции рендеринга нам больше не нужно объявлять шаблон. Конечно, если мы объявим тег шаблона и функцию рендеринга одновременно, в процессе построения результат компиляции шаблона перезапишет исходную функцию рендера, то есть шаблон имеет более высокий приоритет, чем прямо написанная функция рендера. .
По сравнению с шаблоном, JSX обладает большей гибкостью.Столкнувшись с некоторыми сложными компонентами, JSX имеет естественные преимущества.Хотя шаблон немного скучен, структура кода больше соответствует привычке разделять представление и логику.Проще, интуитивно понятнее и лучше поддерживается .
Следует отметить, что окончательная сгенерированная функция рендеринга обернута вwith
беги по грамматике.
резюме
Компоненты Vue передают данные через свойства и реализуют систему шины данных.EventBus
, компонент интегрируетсяEventBus
Для мониторинга регистрации событий, запуска событий используйтеslot
Распространение контента.
Кроме того, реализована декларативная система шаблонов, вruntime
Или предварительная компиляция заключается в компиляции шаблона для создания функции рендеринга для компонента для рендеринга представления.
Отзывчивая система
Vue.js — это фреймворк MVVM JS, когда модель данныхdata
При внесении модификации представление будет автоматически обновляться, то есть фреймворк поможет нам завершить операцию обновления DOM, не требуя от нас манипулирования DOM вручную. Можно понять, что когда мы назначаем данные, Vue сообщает всем компонентам, которые зависят от модели данных, что данные, от которых вы зависите, были обновлены, и вам необходимо повторно отобразить.В это время компонент будет повторно отрисован, а просмотр завершен.
Модель данных && Вычисляемые свойства && Слушатели
В компонентах вы можете определить модель данных для каждого компонентаdata
, вычисляемые свойстваcomputed
, слушательwatch
.
Модель данных: в процессе создания экземпляра Vue модель данныхdata
Каждое из свойств добавляется в реактивную систему, и при изменении данных представление будет реагировать и обновляться синхронно.data
Возвращать надо в виде функции.Завернутые без возврата данные будут глобально видны в проекте, что вызовет загрязнение переменных, после заворачивания с возвратом переменные в данных будут действовать только в текущем компоненте и будут не влияет на другие компоненты.
Вычисляемые свойства:computed
Расчеты на основе реактивных зависимостей компонентов получают результаты и кэшируют их. Они переоцениваются только тогда, когда меняются соответствующие реактивные зависимости, то есть только тогда, когда меняются реактивные данные, от которых он зависит (данные, реквизит, сам вычисляемый). Итак, когда следует использовать вычисляемые свойства? Выражения внутри шаблонов очень удобны, но они рассчитаны на простые операции. Слишком много логики в шаблоне может сделать его тяжелым и трудным в обслуживании. Для любой сложной логики следует использовать вычисляемые свойства.
слушатель: слушательwatch
Как следует из названия, он может отслеживать изменения в ответных данных. Отзывчивые данные включают в себя данные, реквизиты и вычисляемые данные. Когда ответные данные изменяются, их можно соответствующим образом обработать. Это наиболее полезно, когда вам нужно выполнять асинхронные или дорогостоящие операции при изменении данных.
Отзывчивые принципы
В Vue все свойства модели данных будут использоваться Vue.Object.defineProperty
(Vue3.0 использует прокси) для прокси-сервера для захвата данных. Основным механизмом реагирования является режим наблюдателя. Данные являются наблюдаемой стороной. Когда происходит изменение, все наблюдатели уведомляются, чтобы наблюдатели могли ответить. Например, когда наблюдатель является представлением, представление может обновить представление.
Поскольку реактивная система Vue.js имеет три важных концепции:Observer
,Dep
,Watcher
.
Издатель — Наблюдатель
Роль Observe — издатель, а его основная роль — в компонентеvm
При инициализации вызовdefineReactive
функция, использованиеObject.defineProperty
Метод выполняет захват/прослушивание данных для каждого подсвойства объекта, то есть добавляетgetter
а такжеsetter
, что делает соответствующее значение свойства адаптивным.
Когда компонент инициализируется, вызовитеinitState
функция, которая выполняется внутриinitState
,initProps
,initComputed
метод, соответственноdata
,prop
,computed
Инициализировать, чтобы сделать его реактивным.
инициализацияprops
, для всехprops
пройти, позвонитьdefineReactive
функция, чтобы сделать каждое значение свойства свойства реактивным, а затем смонтировать его в_props
, а потом через прокси поставитьvm.xxx
прокси дляvm._props.xxx
середина.
Точно так же инициализируйтеdata
когда, сprop
одинаково для всехdata
пройти, позвонитьdefineReactive
функцию, чтобы сделать каждое значение свойства данных реактивным, а затем смонтировать его в_data
, а потом через прокси поставитьvm.xxx
прокси дляvm._data.xxx
середина.
инициализацияcomputed
, сначала создайте объект-наблюдательcomputed-watcher
, затем повторяетсяcomputed
Для каждого свойства вызова для каждого значения свойстваdefineComputed
метод, используяObject.defineProperty
Делая его реактивным, прокси его к экземпляру компонента, вы можете передать егоvm.xxx
доступ кxxx
Вычисляемые свойства.
Диспетчерский центр/Абонентская служба
Роль Dep — диспетчерский центр/абонент, при звонкеdefineReactive
В процессе создания реактивного значения свойства также создается экземпляр значения свойства для каждогоDep
, основная функция заключается в управлении наблюдателем (Watcher), сборе наблюдателя и уведомлении об обновлении цели наблюдателя, то есть при изменении данных значения атрибута он будет проходить по списку наблюдателей (dep.subs), уведомлять всех наблюдателей и позволить подписке Пользователь выполняет собственную логику обновления.
Этоdep
Задача состоит в том, что в собственностиgetter
метод, вызовdep.depend()
метод, который сохраняет наблюдателя (то есть наблюдателя, который может быть функцией рендеринга компонента, который может быть вычисляемым, или который может быть наблюдателем монитора свойств) для завершения его коллекции зависимостей. в атрибутеsetter
метод, вызовdep.notify()
метод, чтобы уведомить всех наблюдателей о необходимости выполнить обновление и завершить отправку обновления.
Наблюдатель - Наблюдатель
Роль Watcher — подписчик/наблюдатель, его основная роль — предоставлять callback-функции для наблюдаемых свойств и собирать зависимости, при изменении наблюдаемого значения он будет получать данные из диспетчерского центра.Dep
уведомление, которое запускает функцию обратного вызова.
а такжеWatcher
на три категории,normal-watcher
,computed-watcher
,render-watcher
.
-
normal-watcher: в функции хука компонента
watch
Определено в , то есть при изменении отслеживаемого свойства будет запущена определенная функция обратного вызова. -
вычисленный-наблюдатель: функция ловушки в компоненте
computed
определяется в каждомcomputed
атрибут и, наконец, соответствующийWatcher
объект, но такойWatcher
Есть особенность: когда вычисляемое свойство зависит от других данных, то свойство не будет пересчитываться сразу, а будет вычисляться только тогда, когда свойство нужно будет прочитать в других местах позже, т.lazy
(ленивые вычисления). -
render-watcher: каждый компонент будет иметь один
render-watcher
, когдаdata/computed
Когда свойство в будет изменено, это будет вызыватьсяWatcher
для обновления представления компонента.
эти троеWatcher
Также существует фиксированный порядок выполнения, а именно: вычисленный-рендер -> нормальный-наблюдатель -> рендер-наблюдатель. Таким образом, можно максимально гарантировать, что при обновлении представления компонента вычисляемое свойство уже является последним значением. быть старыми данными при обновлении страницы.
резюме
Observer отвечает за перехват данных, Watcher отвечает за подписку и наблюдение за изменениями данных, Dep отвечает за получение подписок и уведомление Observer и получение публикаций и уведомление всех Watchers.
Virtual DOM
Во Вью,template
компилируется в исполняемый файл браузераrender function
, а затем с помощью адаптивной системыrender function
Прикреплено кrender-watcher
В, при изменении данных диспетчерский центрDep
уведомитьrender-watcher
воплощать в жизньrender function
, чтобы завершить визуализацию и обновление представления.
Весь процесс кажется гладким, но когда исполнениеrender function
Когда DOM удаляется и перестраивается каждый раз, это, несомненно, будет огромной потерей производительности выполнения, потому что мы знаем, что DOM браузера очень «дорогой».Когда мы часто обновляем DOM, это вызовет определенные проблемы с производительностью.
Чтобы решить эту проблему, Vue использует объекты JS для абстрагирования DOM браузера, который называется Virtual DOM. Каждый узел Virtual DOM определяется какVNode
, когда каждое выполнениеrender function
, Vue будетVNode
провестиDiff
Напротив, найдите как можно меньше реальных узлов DOM, которые нам нужно обновить, а затем обновите только те узлы, которые необходимо обновить, чтобы решить проблему производительности, вызванную частым обновлением DOM.
VNode
VNode, полное имяvirtual node
, то есть виртуальный узел, виртуальное описание реального узла DOM.В каждом экземпляре компонента Vue$createElement
функция, всеVNode
все создаются этой функцией.
Например, чтобы создать div:
// 声明 render function
render: function (createElement) {
// 也可以使用 this.$createElement 创建 VNode
return createElement('div', 'hellow world');
}
// 以上 render 方法返回html片段 <div>hellow world</div>
После выполнения функции рендеринга она будетVNode Tree
Сопоставьте VNode, чтобы сгенерировать настоящий DOM для завершения рендеринга представления.
Diff
Дифференциал сравнивает старый и новый узлы VNode, а затем изменяет представление в наименьшей единице в соответствии с результатами сравнения двух вместо перерисовки всего представления в соответствии с новым VNode, чтобы достичь цели повышения производительности.
patch
diff внутри Vue.js называетсяpatch
. Алгоритм diff сравнивает узлы дерева одного и того же слоя, а не выполняет поиск и обход дерева слой за слоем, поэтому временная сложность составляет всего O(n), что является довольно эффективным алгоритмом.
Сначала определите функцию, чтобы определить, совпадают ли новые и старые узлы.sameVnode
: удовлетворить ключевое значениеkey
и имя тегаtag
должны быть согласованы и другие условия, возвратtrue
,иначеfalse
.
в ходе выполненияpatch
До того, соответствуют ли новые и старые виртуальные узлы условиямsameVnode(oldVnode, newVnode)
, после выполнения условий входим в процессpatchVnode
, в противном случае считается, что это разные узлы, в это время старый узел будет удален, а новый узел будет создан.
patchVnode
Основная функция patchVnode — определить, как обновлять дочерние узлы.
-
Если старый и новый виртуальные узлы являются статическими, а их ключи одинаковыми (представляющими один и тот же узел), а новый виртуальный узел клонируется или помечается один раз (отмеченный атрибут v-once, визуализируется только один раз), то необходимо заменить только DOM. и VNode, а именно Can.
-
Если старые и новые узлы имеют детские узлы, то операция diff выполняется на дочерних узлах, а
updateChildren
, это обновление Children также является ядром diff. -
Если у старого узла нет дочерних узлов, а у нового узла есть дочерние узлы, сначала очистите текстовое содержимое DOM старого узла, а затем добавьте дочерние узлы к текущему узлу DOM.
-
Когда у нового узла нет дочерних элементов, а у старого узла есть дочерние элементы, все дочерние элементы узла DOM удаляются.
-
Когда старый и новый узлы не имеют дочерних узлов, это просто замена текста.
updateChildren
Ядром Diff является сравнение данных новых и старых дочерних узлов и определение того, как работать с дочерними узлами.В процессе сравнения, поскольку старый дочерний узел имеет ссылку на текущий реальный DOM, новый дочерний узел просто массив VNodes, поэтому выполняется обход.Во время процесса, если обнаруживается, что реальная DOM нуждается в обновлении, настоящая операция DOM будет выполняться непосредственно на старых дочерних узлах.Когда обход заканчивается, новые и старые дочерние узлы будут завершены синхронно.
updateChildren
Внутри определены четыре переменные, которыеoldStartIdx
,oldEndIdx
,newStartIdx
,newEndIdx
, которые соответственно представляют собой индекс левой и правой граничных точек старого и нового дочерних узлов, сравниваемых Diff.В массиве старых дочерних узлов индекс находится вoldStartIdx
а такжеoldEndIdx
Средний узел указывает, что старый дочерний узел является узлом, который нужно пройти, поэтому он меньше, чемoldStartIdx
или больше, чемoldEndIdx
представляет узлы, которые не были пройдены. Точно так же в новом массиве дочерних узлов индексnewStartIdx
а такжеnewEndIdx
Средний узел указывает, что старый дочерний узел является узлом, который нужно пройти, поэтому он меньше, чемnewStartIdx
или больше, чемnewEndIdx
представляет узлы, которые не были пройдены.
Каждый переход,oldStartIdx
а такжеoldEndIdx
а такжеnewStartIdx
а такжеnewEndIdx
Расстояние между ними будет ближе к середине. Завершите цикл, когда oldStartIdx > oldEndIdx или newStartIdx > newEndIdx.
В обходе выньте узел Vnode, соответствующий индексу 4:
- старыйстартидкс: старыйстартвноде
- старыйEndIdx: старыйEndVnode
- новыйStartIdx: новыйStartVnode
- новыйEndIdx: новыйEndVnode
Во время процесса Diff, если естьkey
, и удовлетворитьsameVnode
, узел DOM будет использоваться повторно, иначе будет создан новый узел DOM.
первый,oldStartVnode
,oldEndVnode
а такжеnewStartVnode
,newEndVnode
Всего существует 2*2=4 метода попарного сравнения.
Случай 1: когдаoldStartVnode
а такжеnewStartVnode
Удовлетворить тот же Vnode, тоoldStartVnode
а такжеnewStartVnode
для patchVnode, иoldStartIdx
а такжеnewStartIdx
Переместить вправо.
Случай 2: Аналогично случаю 1, когдаoldEndVnode
а такжеnewEndVnode
Удовлетворить тот же Vnode, тоoldEndVnode
а такжеnewEndVnode
для patchVnode, иoldEndIdx
а такжеnewEndIdx
Двигай влево.
Случай третий: когдаoldStartVnode
а такжеnewEndVnode
Удовлетворить тот же Vnode, значитoldStartVnode
уже побежалoldEndVnode
отстал, на этот разoldStartVnode
а такжеnewEndVnode
Одновременно с patchVnode необходимо такжеoldStartVnode
реальный узел DOM перемещается вoldEndVnode
позади, иoldStartIdx
переместить вправо,newEndIdx
Двигай влево.
Случай 4: Аналогично случаю 3, когдаoldEndVnode
а такжеnewStartVnode
Удовлетворить тот же Vnode, значитoldEndVnode
уже побежалoldStartVnode
пошел вперед, сейчасoldEndVnode
а такжеnewStartVnode
Одновременно с patchVnode необходимо такжеoldEndVnode
реальный узел DOM перемещается вoldStartVnode
перед, иoldStartIdx
переместить вправо,newEndIdx
Двигай влево.
Если эти четыре условия не выполняются, тоoldStartIdx
а такжеoldEndIdx
Найдите между сnewStartVnode
удовлетворитьsameVnode
, если он существует, переместите реальный DOM соответствующего узла вoldStartVnode
спереди.
Если нет, укажитеnewStartVnode
Для новых узлов создайте новые узлы вoldStartVnode
Только впереди.
Когда OldStartiDX> OldendidX или NewStartiDX> NewEdeDIDID, цикл кончики. В это время нам нужно обрабатывать эти VNODES, которые не пройдены.
Когда oldStartIdx > oldEndIdx, это означает, что старый узел был пройден, но новый узел не был пройден, в это время необходимо создать новый узел и разместить его наoldEndVnode
Позади.
На этом сопоставление дочерних узлов завершено. Вот пример схемы процесса исправления:
Суммировать
Чтобы позаимствовать официальное изображение:
Vue.js реализует декларативный движок рендеринга иruntime
Либо скомпилировать декларативный шаблон в функцию рендеринга во время прекомпиляции, смонтировать его в наблюдателе Watcher, а в функции рендеринга (touch) отзывчивая система использует отзывчивые данные.getter
Метод собирает как зависимость от наблюдателя, используя реагирующие данныеsetter
Метод уведомляет (уведомляет) всех наблюдателей об обновлении.В это время наблюдатель-наблюдатель активирует функцию рендеринга компонента (триггер повторного рендеринга), функцию рендеринга, выполняемую компонентом, и генерирует новое виртуальное дерево DOM.В это время , Vue обновит старое и новое виртуальное дерево DOM, выполнит Diff, найдет настоящий DOM, которым нужно управлять, и обновит его.