предисловие
Вычисляемое в Vue — очень мощная функция.После изменения значения, доступ к которому осуществляется в вычисляемой функции, вычисляемое значение также изменится автоматически.
Реализация в Vue2 заключается в использованииWatcherВложенная коллекция ,渲染watcherсобралcomputed watcherкак зависимость,computed watcherсобрал снова响应式数据某个属性как зависимость, так в响应式数据某个属性Когда произойдет изменение, оно будет响应式属性 -> computed值更新 -> 视图渲染Такая цепочка триггеров срабатывает в прошлом.Если вам интересен принцип во Vue2, вы можете прочитать мой анализ этой статьи:
Предварительное знание
Чтобы прочитать эту статью, вам нужно сначала изучить основные принципы отзывчивости Vue3.Сначала вы можете прочитать мою статью.Принцип такой же, как и у Vue3:Помогите вам полностью понять принцип ответа прокси Vue3! TypeScript реализует реактивную библиотеку на основе прокси с нуля.
если дляeffect,reactiveЕсли понятия недостаточно знакомы, эта статья временно недоступна.Вы можете сначала прочитать статью выше.
После того, как у вас есть необходимые знания, вы должны знать по умолчанию:
-
effectПо сути, это функция сбора зависимостей, доступ к реагирующим данным осуществляется внутри нее, а реагирующие данные будутeffectФункция собирается как зависимость, и при следующем изменении реактивных данных она будет запущена для повторного выполнения. -
reactiveТо, что возвращается, является ответными данными, которые можно комбинировать сeffectДля использования с.
Возьмите простой каштан:
// 响应式数据
const data = reactive({ count: 0 });
// 依赖收集
effect(() => console.log(data.count));
// 触发上面的effect重新执行
data.count++;
В этом примере данные являются реактивными данными.
Функция, переданная эффектом, обращается к его свойствам внутри.countсейчас,
Итак,count -> effectзависимость.
В следующий раз, когда счетчик изменится, эффект будет выполнен повторно, это так просто.
computed
Затем введите основные понятия в этой статье,computedПосле перезаписи этого примера:
// 1. 响应式数据
const data = reactive({ count: 0 });
// 2. 计算属性
const plusOne = computed(() => data.count + 1);
// 3. 依赖收集
effect(() => console.log(plusOne.value));
// 4. 触发上面的effect重新执行
data.count++;
Такой пример тоже может сработать, почему?data.countизменение энергиинепрямой триггерКак насчет повторного выполнения эффектов, обращающихся к вычисляемым свойствам?
Давайте проанализируем шаг за шагом с одноточечной отладкой.
Исходный код упрощенной версии
Первый взгляд на упрощенную версиюcomputedкод:
export function computed(getter) {
let dirty = true;
let value: T;
// 这里还是利用了effect做依赖收集
const runner = effect(getter, {
// 这里保证初始化的时候不去执行getter
lazy: true,
computed: true,
scheduler: () => {
// 在触发更新时 只是把dirty置为true
// 而不去立刻计算值 所以计算属性有lazy的特性
dirty = true;
},
});
return {
get value() {
if (dirty) {
// 在真正的去获取计算属性的value的时候
// 依据dirty的值决定去不去重新执行getter 获取最新值
value = runner();
dirty = false;
}
// 这里是关键 后续讲解
trackChildRun(runner);
return value;
},
set value(newValue: T) {
setter(newValue);
},
};
}
Как видите, вычисление на самом делеeffect. Здесь ловко используется замыкание, и несколько ключевых моментов в аннотации определяют, что вычисляемое свойство имеет懒加载Когда вы не читаете значение, оно фактически не будет оцениваться.
предварительная подготовка
Первое, что нужно знать, это то, что функция эффекта начнет выполняться немедленно, и перед ее выполнением поставьтеeffect自身стать глобальнымactiveEffect, для реактивных зависимостей сбора данных.
а такжеactiveEffectЗапись имеет форму стека, который помещается в стек, когда функция начинает выполняться, и извлекается из стека, когда выполнение функции заканчивается, чтобы можно было поддерживать взаимосвязь вложенных эффектов.
Давайте начнем с нескольких псевдонимов для простоты объяснения
// 计算effect
computed(() => data.count + 1);
// 日志effect
effect(() => console.log(plusOne.value));
С точки зрения зависимостей,
日志effectчитать计算effect
计算effectчитать реактивные свойстваcount
Так что порядок обновлений тоже должен быть:
count改变 -> 计算effect更新 -> 日志effect更新
Так как же формируется эта цепочка отношений?
пошаговая интерпретация
Когда эффект ведения журнала начнет выполняться,
⭐⭐
На данный момент activeEffect — это эффект журнала.
В настоящее время стек эффектов [эффект журнала]
⭐⭐
Чтение триггеров plusOne.value
get value() {
if (dirty) {
// 在真正的去获取计算属性的value的时候
// 依据dirty的值决定去不去重新执行getter 获取最新值
value = runner()
dirty = false
}
// 这里是关键 后续讲解
trackChildRun(runner)
return value
},
Сначала введите процесс оценки:value = runner(), бегун на самом деле计算effect, который является оболочкой для функции-получателя, переданной пользователем,
После входа в бегун
⭐⭐
На данный момент activeEffect — это расчетный эффект.
В настоящее время стек эффектов [эффект журнала, эффект расчета]
⭐⭐
бегун завернутый() => data.count + 1то есть计算effectбуду читатьcount, так как это функция, обернутая эффектом, она запускает ответные данныеgetПерехват:
В настоящее времяcountбудет собирать计算effectкак собственная зависимость.
а также计算effectбудет собиратьcountКоллекция зависимостей, хранимая на себе. (пройти черезeffect.depsАтрибуты)
dep.add(activeEffect);
activeEffect.deps.push(dep);
то есть формированиедвусторонняя коллекцияОтношение,
计算effectсохраненcountвсе зависимости отcountтакже сохранен计算effectзависимость.
Затем, когда бегун закончит бег,计算effectиз стека, в это времяactiveEffectстал вершиной стека日志effect
⭐⭐
На данный момент activeEffect — это эффект журнала.
В настоящее время стек эффектов [эффект журнала]
⭐⭐
Введите следующийважные шаги:trackChildRun
trackChildRun(runner);
function trackChildRun(childRunner: ReactiveEffect) {
for (let i = 0; i < childRunner.deps.length; i++) {
const dep = childRunner.deps[i];
dep.add(activeEffect);
}
}
этоrunnerто есть计算effect,этоdepsвисит наcountнабор зависимостей,
существуетtrackChildRun, он принимает текущий активный эффект, который日志effectтакже присоединилсяcountв наборе зависимостей.
В настоящее времяcountКоллекция зависимостей выглядит так:[ 计算effect, 日志effect ]
так что в следующий разcountПри обновлении оба эффекта будут срабатывать повторно, а потому порядок срабатывания срабатывает первымcomputed effectпост-триггер普通effect, тем самым завершая
- Грязь рассчитанного эффекта устанавливается в значение true, что указывает на то, что следующее чтение необходимо переоценить.
- Эффект журнала считывает значение вычисленного эффекта, получает последнее значение и распечатывает его.
Суммировать
Я должен признать, что реализация мощной функции вычислений действительно требует очень сложной внутренней реализации, и я верю, что эта двусторонняя процедура сбора зависимостей также принесет большое вдохновение всем вам. Учась с You Da, у вас действительно есть что поесть!
Кроме того, из-за@vue/reactivityНезависимость от фреймворка, я интегрировал его в React и сделал библиотеку управления состоянием, которая может полностью использовать вышеперечисленное.computedи другие мощные возможности Vue3.
Заинтересованные друзья также могут посмотреть и поставить звезду!