Как работает отслеживание зависимостей — Knockout.js
Новичкам не обязательно это знать, но более продвинутые разработчики захотят узнать, почему мы продолжаем делать все эти утверждения о том, что KO автоматически отслеживает зависимости и обновляет правильные участки UI…
На самом деле, все довольно просто и красиво. Алгоритм отслеживания работает наподобие такого:
- Когда вы объявляете computed переменную, KO мгновенно вызывает функцию, переданную в качестве параметра (далее функция А), для получения начального значения.
- В то время, как эта функция запущена, KO настраивает подписку на любые observable-переменные (включая другие computed переменные), которые читает данная функция. Callback-функция подписки настроена так, чтобы повторно запускать «функцию А», повторяя весь процесс с шага 1 (убирая все старые подписки, которые больше не применяются).
- KO уведомляет любых подписчиков о новом значении вашей computed переменной.
Итак, Knockout не просто отслеживает зависимости при первом запуске переданной в computed-переменную функции — он повторно смотрит на зависимости каждый раз. Например, это значит, что зависимости могут динамически изменяться: зависимость А может определять, будет ли computed переменная так же зависеть от Б или В. Затем, функция будет повторно запущена, когда либо А или текущий выбор из Б или В изменился. Вам не нужно специально объявлять зависимости: они определяются во время работы скрипта из выполнения кода.
Другой ловкий трюк заключается в том, что декларативные биндинги реализованы как computed переменные. Таким образом, если биндинг читает значение observable-переменной, этот биндинг становится зависимым от этой observable-переменной, что заставляет этот биндинг повторно выполняться, если observable-переменная изменилась.
Pure computed-переменные работают немного иначе. Для более подробной информации, смотри документацию для purecomputed переменных.
Контролируем зависимости используя peek
Обычно система автоматического отслеживания зависимостей в Knockout делает именно то, что вам нужно. Но иногда может быть так, что нужно контролировать, какие из observable-переменных будут обновлять вашу computed-переменную, особенно, если computed-переменная делает какого-то рода действия, вроде выполнения Ajax запроса. Функция peek позволяет получить доступ к observable или computed переменной без создания зависимости.
В примере ниже, computed-переменная используется для перезагрузки observable-переменной с именем currentPageData используя Ajax с данными из двух других observable-свойств. Computed переменная будет обновляться, когда изменилась переменная pageIndex, но будет игнорировать изменения в selectedItem потому что доступ к её значению производится через функцию peek. В этом случае, пользователь может захотеть использовать текущее значение selectedItem только для отслеживания назначения данного запроса, когда новый набор данных был загружен.
ko.computed(function() {
var params = {
page: this.pageIndex(),
selected: this.selectedItem.peek()
};
$.getJSON('/Some/Json/Service', params, this.currentPageData);
}, this);
Примечание: Если вам просто хочется избавиться от слишком частого обновления computed-переменной, смотри экстендер rateLimit.
Игнорирование зависимостей внутри computed
Функция ko.ignoreDependencies доступна для сценариев, где вам нужно выполнить код внутри computed-переменной, который не должен рассматриваться как зависимость для данной computed-переменной. Часто такое бывает полезно в произвольных биндингах, когда вы хотите вызвать какой-то код, который может получить доступ к observable-переменным, но при этом не хотите, чтобы биндинг повторно выполнялся, при изминениях в этих observable-переменных.
ko.ignoreDependencies( callback, callbackTarget, callbackArgs );
Пример:
ko.bindingHandlers.myBinding = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = ko.unwrap(valueAccessor());
var value = ko.unwrap(options.value);
var afterUpdateHandler = options.afterUpdate;
// разработчик передал функцию, которую нужно вызывать при обновленнии данного биндинга, но
// мы не очень то хотим отслеживать любые зависимости, которые могут повторно запустить этот биндинг
if (typeof afterUpdateHandler === "function") {
ko.ignoreDependencies(afterUpdateHandler, viewModel, [value, color]);
}
$(element).somePlugin("value", value);
}
}
Примечание: Почему циклические зависимости бессмысленны
Computed-переменные должны отображать набор входящих observable-переменных в виде одиночной observable-переменной на выходе. Таким образом, нет смысла включать циклы в ваши цепочки зависимостей. Циклы не будут аналогичны рекурсии; они будут аналогичны тому, что у вас есть скажем две ячейки электронной таблицы, значения которых вычиляются функцией, на основе значений друг друга. Это приводит к бесконечному циклу выполнения.
Итак, что же делает Knockout если у вас есть цикл внутри графа зависимостей? Он предотвращает бесконечные циклы путем применения следующего правила: Knockout не перезапустит выполнение расчета computed-переменной когда он уже выполняется. Это вряд ли повлияет на ваш код. Это актуально в двух случаях: когда две computed-переменные зависят друг от друга (возможно торлько, если одна или обе используют опцию deferEvaluation), или когда computed-переменная записывает значение в другую observable-переменную от которой у нее есть зависимость (либо напрямую, либо через цепочку зависимостей). Если вам нужно использовать один из этих шаблонов и вы хотите полностью предотвратить циклическую зависимость, то можете использовать функцию peek описанную выше.
Эксклюзивно для сайта skv1991.ru
Перепечатка и копирование строго запрещены.