Интервьюер: расскажите о своем понимании фреймворка Vue.js.

Vue.js
Интервьюер: расскажите о своем понимании фреймворка Vue.js.

предисловие

В этом году 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, чтобы завершить визуализацию и обновление представления.

DOM更新

Весь процесс кажется гладким, но когда исполнение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), что является довольно эффективным алгоритмом.

DIFF

Сначала определите функцию, чтобы определить, совпадают ли новые и старые узлы.sameVnode: удовлетворить ключевое значениеkeyи имя тегаtagдолжны быть согласованы и другие условия, возвратtrue,иначеfalse.

в ходе выполненияpatchДо того, соответствуют ли новые и старые виртуальные узлы условиямsameVnode(oldVnode, newVnode), после выполнения условий входим в процессpatchVnode, в противном случае считается, что это разные узлы, в это время старый узел будет удален, а новый узел будет создан.

patchVnode

Основная функция patchVnode — определить, как обновлять дочерние узлы.

  1. Если старый и новый виртуальные узлы являются статическими, а их ключи одинаковыми (представляющими один и тот же узел), а новый виртуальный узел клонируется или помечается один раз (отмеченный атрибут v-once, визуализируется только один раз), то необходимо заменить только DOM. и VNode, а именно Can.

  2. Если старые и новые узлы имеют детские узлы, то операция diff выполняется на дочерних узлах, аupdateChildren, это обновление Children также является ядром diff.

  3. Если у старого узла нет дочерних узлов, а у нового узла есть дочерние узлы, сначала очистите текстовое содержимое DOM старого узла, а затем добавьте дочерние узлы к текущему узлу DOM.

  4. Когда у нового узла нет дочерних элементов, а у старого узла есть дочерние элементы, все дочерние элементы узла DOM удаляются.

  5. Когда старый и новый узлы не имеют дочерних узлов, это просто замена текста.

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.

img

В обходе выньте узел 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Позади.

Когда newStartIdx > newEndIdx, это означает, что новый узел был пройден, но старый узел не был пройден, в это время все старые узлы, которые не были пройдены, должны быть удалены.

На этом сопоставление дочерних узлов завершено. Вот пример схемы процесса исправления:

patchChildren

Суммировать

Чтобы позаимствовать официальное изображение:

Vue.js реализует декларативный движок рендеринга иruntimeЛибо скомпилировать декларативный шаблон в функцию рендеринга во время прекомпиляции, смонтировать его в наблюдателе Watcher, а в функции рендеринга (touch) отзывчивая система использует отзывчивые данные.getterМетод собирает как зависимость от наблюдателя, используя реагирующие данныеsetterМетод уведомляет (уведомляет) всех наблюдателей об обновлении.В это время наблюдатель-наблюдатель активирует функцию рендеринга компонента (триггер повторного рендеринга), функцию рендеринга, выполняемую компонентом, и генерирует новое виртуальное дерево DOM.В это время , Vue обновит старое и новое виртуальное дерево DOM, выполнит Diff, найдет настоящий DOM, которым нужно управлять, и обновит его.

исходный адрес

Ссылаться на