28.12.2017

Использование Computed переменных — Knockout.js

Что если есть observable-переменная для firstName (имя), и другая для lastName(фамилия), и вы хотите вывести полное имя? Это именно тот момент, когда на сцене появляются computed observable (вычисляемые observable-переменные). Это функции, которые зависят от одной или более других observable-переменных, и их значение будет автоматически обновляться когда любая из из переменных, от которых зависит computed-переменная изменилась.

Например, есть следующий класс модели представления,

function AppViewModel() {
    this.firstName = ko.observable('Вася');
    this.lastName = ko.observable('Пупкин');
}

… вы можете добавить вычисляемую observable-переменную, которая будет возвращать полное имя:

function AppViewModel() {
    // ... оставит firstName и lastName без изменений ...
 
    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();
    }, this);
}

Теперь можно забиндить UI с этой переменной, например:

Ваше полное имя <span data-bind="text: fullName"></span>

… и она будет обновляться, когда firstName или lastName изменились (указанная вами функция будет выполняться единожды, каждый раз при изменении любой из зависимых переменных, и то значение, что вы возвращаете в return будет передано подписчикам вроде элементов UI или других computed-переменных).

Цепочки зависимостей работают без проблем

Разумеется, вы можете создавать целые цепочки из computed переменных на свое усмотрение. Например, у вас может быть:

  • observable называемая items, что представляет набор записей
  • другая observable с названием selectedIndexes, хранящая те индексы записей, что ‘выбрал’ пользователь
  • computed переменная с названием selectedItems, которая возвращает массив объектов записей, соответствующий выбранным индексам
  • другая computed, возвращающая true или false, в зависимости от того, есть ли у любого объекта из selectedItems некоторое свойство (например это только что добавленная запись, или с не сохраненными изменениями). Некоторый UI элемент, вроде кнопки, который может иметь включенное или отключенное состояние в зависимости от этого значения.

Изменения, сделанные в items или selectedIndexes будут пробегать по всей цепочке computed переменных, что в конечном итоге обновит любые UI элементы, связанные с этими переменными.

Управление контекстом ‘this’

Второй параметр в ko.computed (кусочек кода, где мы указали this в качестве аргумента в примере выше) определяет значение контекста для this при вычислении computed переменной. Без передачи этого параметра, не было бы возможности ссылаться на this.firstName() или this.lastName(). Опытные JavaScript разработчики посчитают это очевидным, но если вы все ещё изучаете JavaScript, это может выглядеть странным. (Языки вроде C# и Java никогда не ждут, что программист установит значение для this, но JavaScript ожидает, потому что, по-умолчанию  функции внутри него не являются частью какого-либо объекта.)

Распространенные соглашения, которые упрощают жизнь

Есть популярные соглашения, которые позволяют вообще избавиться от необходимости отслеживать this: если конструктор вашей модели представления (view model) копирует ссылку на this в другую переменную (по-традиции называемую self), то можно затем использовать self на протяжении всего кода модели представления и не беспокоиться о том, что она будет переопределена для ссылки на что-то другое. Например:

function AppViewModel() {
    var self = this;
 
    self.firstName = ko.observable('Вася');
    self.lastName = ko.observable('Пупкин');
    self.fullName = ko.computed(function() {
        return self.firstName() + " " + self.lastName();
    });
}

Поскольку переменная self попала в замыкание функции, она останется доступной и неизменной в любых вложенных функциях, таких, как тело computed переменных. Это соглашение даже более полезно, когда дело доходит до обработчиков событий, что можно увидеть во множестве реальных примеров.

Pure computed переменные

Если ваша computed переменная просто вычисляет и возвращает значение на основе каких observable переменных, то лучше определить такую переменную, как ko.pureComputed (чисто вычисляемая) вместо a ko.computed. Например:

this.fullName = ko.pureComputed(function() {
    return this.firstName() + " " + this.lastName();
}, this);

Как только computed переменная объявлена с префиксом pure (то есть, её функция напрямую не изменяет другие объекты или состояние), Knockout может более эффективно управлять её повторными запусками и использованием памяти. Knockout будет автоматически приостанавливать или высвобождать её из памяти, если в другом коде на нее нет зависимостей.

Pure computed переменные были введены в Knockout 3.2.0. Смотри так же: узнать больше о pure computed переменных.

Заставляем computed переменные всегда уведомлять подписчиков

Когда computed переменная возвращает простейшее значение (число, строку, булево значение, или null), зависимые от данной переменные обычно уведомляются если значение реально изменилось. Однако, можно воспользоваться встроенным notify экстендером для обеспечения подписчиков этой переменной уведомлениями о каждом изменении, даже если значение переменной такое же. Можно применить экстендер таким способом:

myViewModel.fullName = ko.pureComputed(function() {
    return myViewModel.firstName() + " " + myViewModel.lastName();
}).extend({ notify: 'always' });

Задержка и/или подавление уведомлений об изменении

Обычно, computed переменная обновляется и мгновенно уведомляет об этом подписчиков, как только её зависимые переменные изменились. Но если computed переменная содержит множество зависимостей или затрагивает дорогие (в плане производительности) обновления, то можно улучшить производительность, ограничивая частоту уведомлений об изменении computed-переменной или подавляя их. Этого можно достигнуть, используя экстендер rateLimit например так:

// Обеспечиваем создание уведомлений не чаще, чем раз в 50-миллисекундный период
myViewModel.fullName.extend({ rateLimit: 50 });

Определяем является ли свойство computed переменной

В некоторых задачах, бывает полезно программно определять, работаете ли вы с computed переменной или нет. Knockout предоставляет вспомогательную функцию, ko.isComputed для того, чтобы решить такую задачу. Например, вы хотите исключить computed переменные из данных, которые отправляете назад на сервер.

for (var prop in myObject) {
    if (myObject.hasOwnProperty(prop) && !ko.isComputed(myObject[prop])) {
        result[prop] = myObject[prop];
    }
}

В дополнение, Knockout предоставляет похожие функции, которые могут работать с observable переменными записываемыми computed переменными:

  • ko.isObservable — вернет true для observable переменных, observable массивов, и всех computed переменных.
  • ko.isWritableObservable — вернет true для observable переменных, observable массивов, и записываемых computed переменных (есть так же алиас ko.isWriteableObservable с чуть другим написанием).

Когда computed переменная используется только в UI

Если вам нужно выводить например только полное имя человека в UI то можно объявить его так:

function AppViewModel() {
    // ... оставит firstName и lastName без изменений ...
 
    this.fullName = function() {
        return this.firstName() + " " + this.lastName();
    };
}

Теперь, ваш биндинг с UI элементами превращается в вызов метода, например:

The name is <span data-bind="text: fullName()"></span>

Knockout создаст внутреннюю computed переменную для определения того, какой результат нужно вывести в результате выражения с зависимыми observable переменными, и автоматически сделает нужные операции с этой перменной, когда связанный элемент был позже удален.


Эксклюзивно для сайта skv1991.ru

Перепечатка и копирование строго запрещены.

Оригинал страницы

Опубликовано: 28/12/2017

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *