Реактивные свойства¶
Lit-компоненты получают входные данные и хранят свое состояние в виде полей или свойств класса JavaScript. Реактивные свойства — это свойства, которые при изменении могут запускать цикл реактивного обновления, перерисовывая компонент, а также могут быть прочитаны или записаны в атрибуты.
1 2 3 4 |
|
1 2 3 4 5 |
|
Lit управляет вашими реактивными свойствами и соответствующими им атрибутами. В частности:
- Реактивные обновления. Lit генерирует пару геттер/сеттер для каждого реактивного свойства. Когда реактивное свойство изменяется, компонент планирует обновление.
- Обработка атрибутов. По умолчанию Lit устанавливает наблюдаемый атрибут, соответствующий свойству, и обновляет свойство при изменении атрибута. Значения свойств могут также, по желанию, отражаться обратно в атрибут.
- Свойства суперклассов. Lit автоматически применяет параметры свойств, объявленных суперклассом. Вам не нужно заново объявлять свойства, если вы не хотите изменить параметры.
- Обновление элементов. Если компонент Lit определен после того, как элемент уже находится в DOM, Lit обрабатывает логику обновления, гарантируя, что любые свойства, установленные для элемента до его обновления, вызовут правильные реактивные побочные эффекты при обновлении элемента.
Публичные свойства и внутреннее состояние¶
Публичные свойства являются частью публичного API компонента. В целом, публичные свойства — особенно публичные реактивные свойства — должны рассматриваться как ввод.
Компонент не должен изменять свои собственные публичные свойства, кроме как в ответ на ввод пользователя. Например, компонент меню может иметь публичное свойство selected
, которое может быть инициализировано в заданное значение владельцем элемента, но которое обновляется самим компонентом, когда пользователь выбирает элемент. В таких случаях компонент должен отправлять событие, чтобы сообщить владельцу компонента об изменении свойства selected
. Подробнее см. в разделе Диспетчеризация событий.
Lit также поддерживает внутреннее реактивное состояние. Внутреннее реактивное состояние относится к реактивным свойствам, которые не являются частью API компонента. У этих свойств нет соответствующего атрибута, и они обычно помечены как protected или private в TypeScript.
1 2 |
|
1 2 3 4 5 6 7 8 |
|
Компонент манипулирует своим собственным внутренним реактивным состоянием. В некоторых случаях внутреннее реактивное состояние может быть инициализировано из публичных свойств — например, если существует дорогостоящее преобразование между свойством, видимым пользователю, и внутренним состоянием.
Как и в случае с публичными реактивными свойствами, обновление внутреннего реактивного состояния запускает цикл обновления. Для получения дополнительной информации см. раздел Внутреннее реактивное состояние.
Публичные реактивные свойства¶
Объявите публичные реактивные свойства вашего элемента с помощью декораторов или статического поля properties
.
В любом случае вы можете передать объект options для настройки свойств.
Объявление свойств с помощью декораторов¶
Используйте декоратор @property
с объявлением поля класса, чтобы объявить реактивное свойство.
1 2 3 4 5 6 7 |
|
Аргументом декораторов @property
является объект options
. Отсутствие аргумента эквивалентно указанию значения по умолчанию для всех опций.
Использование декораторов
Декораторы — это предложенная функция JavaScript, поэтому для использования декораторов вам потребуется компилятор, например Babel или компилятор TypeScript. Подробности см. в разделе Включение декораторов.
Объявление свойств в статическом поле класса properties
¶
Чтобы объявить свойства в статическом поле класса properties
:
1 2 3 4 5 6 7 8 9 10 11 |
|
Пустой объект опции эквивалентен указанию значения по умолчанию для всех опций.
Избегание проблем с полями классов при объявлении свойств¶
Поля классов имеют проблематичное взаимодействие с реактивными свойствами. Поля классов определяются на экземпляре элемента, в то время как реактивные свойства определяются как аксессоры на прототипе элемента. Согласно правилам JavaScript, свойство экземпляра имеет приоритет над свойством прототипа и фактически скрывает его. Это означает, что аксессоры реактивных свойств не работают при использовании полей классов, так что установка свойства не вызовет обновления элемента.
1 2 3 4 |
|
В JavaScript при объявлении реактивных свойств нельзя использовать поля класса. Вместо этого свойства должны быть инициализированы в конструкторе элемента:
1 2 3 4 5 6 7 |
|
В качестве альтернативы вы можете использовать стандартные декораторы с Babel для объявления реактивных свойств.
1 2 3 4 |
|
Для TypeScript вы можете использовать поля классов для объявления реактивных свойств, если вы используете один из этих шаблонов:
-
Установите опцию компилятора
useDefineForClassFields
вfalse
. Это уже является рекомендацией при использовании декораторов с TypeScript.1 2 3 4 5 6 7
// tsconfig.json { "compilerOptions": { "experimentalDecorators": true, // If using decorators "useDefineForClassFields": false } }
1 2 3 4 5 6 7
class MyElement extends LitElement { static properties = { foo: { type: String } }; foo = 'Default'; @property() bar = 'Default'; }
-
Добавьте ключевое слово
declare
для поля и поместите инициализатор поля в конструктор.1 2 3 4 5 6 7 8
class MyElement extends LitElement { declare foo: string; static properties = { foo: { type: String } }; constructor() { super(); this.foo = 'Default'; } }
-
Добавьте ключевое слово
accessor
в поле, чтобы использовать auto-accessors.1 2 3 4 5 6 7
class MyElement extends LitElement { static properties = { foo: { type: String } }; accessor foo = 'Default'; @property() accessor bar = 'Default'; }
Свойства опций¶
Объект options может иметь следующие свойства:
attribute
-
Связано ли свойство с атрибутом, или пользовательское имя для связанного атрибута. По умолчанию: true. Если
attribute
равен false, опцииconverter
,reflect
иtype
игнорируются. Для получения дополнительной информации смотрите Установка имени атрибута. converter
-
пользовательский конвертер для преобразования между свойствами и атрибутами. Если не указано, используется конвертер атрибутов по умолчанию.
hasChanged
-
Функция, вызываемая каждый раз, когда свойство установлено, чтобы определить, изменилось ли свойство, и вызвать обновление. Если функция не указана,
LitElement
использует строгую проверку неравенства (newValue !== oldValue
), чтобы определить, изменилось ли значение свойства. Дополнительные сведения см. в разделе Настройка обнаружения изменений. noAccessor
-
Установите значение true, чтобы не генерировать аксессоры свойств по умолчанию. Эта опция редко бывает необходима. По умолчанию:
false
. Для получения дополнительной информации смотрите Предотвращение генерации Lit аксессоров свойств. reflect
-
Отражается ли значение свойства обратно в связанный с ним атрибут. По умолчанию:
false
. Подробнее см. в разделе Включение отражения атрибутов. state
-
Установите значение
true
, чтобы объявить свойство как внутреннее реактивное состояние. Внутреннее реактивное состояние запускает обновления, как и публичные реактивные свойства, но Lit не генерирует для него атрибут, и пользователи не должны обращаться к нему извне компонента. Эквивалентно использованию декоратора@state
. По умолчанию:false
. Для получения дополнительной информации смотрите Внутреннее реактивное состояние. type
-
При преобразовании строкового атрибута в свойство, конвертер атрибутов Lit по умолчанию будет разбирать строку в заданный тип, и наоборот, при отражении свойства в атрибут. Если задан
converter
, то это поле передается конвертеру. Еслиtype
не указан, конвертер по умолчанию принимает значениеtype: String
. См. раздел Использование конвертера по умолчанию. -
При использовании TypeScript это поле, как правило, должно соответствовать типу TypeScript, объявленному для данного поля. Однако опция
type
используется в runtime Lit'а для сериализации/десериализации строк, и ее не следует путать с механизмом проверки типов.
Отсутствие объекта options или указание пустого объекта options эквивалентно указанию значения по умолчанию для всех опций.
Внутреннее реактивное состояние¶
Внутреннее реактивное состояние относится к реактивным свойствам, которые не являются частью публичного API компонента. Эти свойства состояния не имеют соответствующих атрибутов и не предназначены для использования извне компонента. Внутреннее реактивное состояние должно задаваться самим компонентом.
Для объявления внутреннего реактивного состояния используйте декоратор @state
:
1 2 |
|
Используя статическое поле класса properties
, вы можете объявить внутреннее реактивное состояние с помощью опции state: true
.
1 2 3 4 5 6 7 |
|
Внутреннее реактивное состояние не должно вызываться извне компонента. В TypeScript эти свойства должны быть помечены как private
или protected
. Мы также рекомендуем использовать такое соглашение, как ведущее подчеркивание (_
) для идентификации приватных или защищенных свойств для пользователей JavaScript.
Внутреннее реактивное состояние работает так же, как и публичные реактивные свойства, за исключением того, что у свойства нет атрибута, связанного с ним. Единственный параметр, который вы можете указать для внутреннего реактивного состояния — это функция hasChanged
.
Декоратор @state
также может служить подсказкой для минификатора кода, что имя свойства может быть изменено во время минификации.
Что происходит при изменении свойств¶
Изменение свойства может запустить реактивный цикл обновления, который заставит компонент перерисовать свой шаблон.
При изменении свойства происходит следующая последовательность действий:
- Вызывается сеттер свойства.
- Сеттер вызывает метод компонента
requestUpdate
. - Сравниваются старое и новое значения свойства.
- По умолчанию Lit использует строгий тест на неравенство, чтобы определить, изменилось ли значение (то есть
newValue !== oldValue
). - Если у свойства есть функция
hasChanged
, то она вызывается со старым и новым значениями свойства.
- По умолчанию Lit использует строгий тест на неравенство, чтобы определить, изменилось ли значение (то есть
- Если изменение свойства обнаружено, обновление планируется асинхронно. Если обновление уже запланировано, выполняется только одно обновление.
- Вызывается метод
update
компонента, отражающий измененные свойства в атрибутах и перерисовывающий шаблоны компонента.
Обратите внимание, что если вы измените свойство объекта или массива, это не вызовет обновления, поскольку сам объект не изменился. Для получения дополнительной информации смотрите Мутирование свойств объектов и массивов.
Существует множество способов подключиться к реактивному циклу обновления и изменить его. Дополнительные сведения см. в разделе Реактивный цикл обновления.
Дополнительные сведения об обнаружении изменений свойств см. в разделе Настройка обнаружения изменений.
Мутирование свойств объектов и массивов¶
Мутирование объекта или массива не изменяет ссылку на объект, поэтому не вызывает обновления. Свойства объектов и массивов можно обрабатывать одним из двух способов:
-
Шаблон неизменяемых данных. Рассматривайте объекты и массивы как неизменяемые. Например, чтобы удалить элемент из
myArray
, создайте новый массив:1 2 3
this.myArray = this.myArray.filter( (_, i) => i !== indexToRemove, );
Хотя этот пример прост, для управления неизменяемыми данными часто полезно использовать библиотеку типа Immer. Это поможет избежать хитрого шаблонного кода при настройке глубоко вложенных объектов.
-
Вручную вызывая обновление. Мутируйте данные и вызовите
requestUpdate()
, чтобы вызвать обновление напрямую. Например:1 2
this.myArray.splice(indexToRemove, 1); this.requestUpdate();
При вызове без аргументов
requestUpdate()
планирует обновление без вызова функцииhasChanged()
. Но обратите внимание, чтоrequestUpdate()
вызывает обновление только текущего компонента. То есть, если в компоненте используется код, показанный выше, и компонент передаетthis.myArray
подкомпоненту, то подкомпонент обнаружит, что ссылка на массив не изменилась, и не будет обновляться.
В целом, использование нисходящего потока данных с неизменяемыми объектами лучше всего подходит для большинства приложений. Это гарантирует, что каждый компонент, которому нужно вывести новое значение, сделает это (и сделает это максимально эффективно, поскольку части дерева данных, которые не изменились, не заставят обновляться компоненты, которые полагаются на них).
Мутирование данных напрямую и вызов requestUpdate()
следует рассматривать как продвинутый вариант использования. В этом случае вам (или другой системе) необходимо определить все компоненты, использующие мутировавшие данные, и вызвать requestUpdate()
для каждого из них. Когда эти компоненты распределены по всему приложению, управлять этим становится сложно. Если не делать этого надежно, то можно изменить объект, который отображается в двух частях приложения, но обновляется только в одной.
В простых случаях, когда известно, что данный фрагмент данных используется только в одном компоненте, можно смело мутировать данные и вызывать requestUpdate()
, если вам так удобнее.
Атрибуты¶
В то время как свойства отлично подходят для получения данных JavaScript в качестве входных данных, атрибуты — это стандартный способ, с помощью которого HTML позволяет настраивать элементы из разметки, без необходимости использовать JavaScript для установки свойств. Предоставление интерфейса свойств и атрибутов для своих реактивных свойств — ключевой способ, с помощью которого компоненты Lit могут быть полезны в самых разных средах, включая те, которые отображаются без шаблонизатора на стороне клиента, например, статические HTML-страницы, обслуживаемые CMS.
По умолчанию Lit устанавливает наблюдаемый атрибут, соответствующий каждому публичному реактивному свойству, и обновляет свойство при изменении атрибута. Значения свойств могут также, по желанию, быть отражены (записаны обратно в атрибут).
В то время как свойства элементов могут быть любого типа, атрибуты всегда являются строками. Это влияет на наблюдаемые атрибуты и отраженные атрибуты нестроковых свойств:
-
Чтобы наблюдать атрибут (установить свойство из атрибута), значение атрибута должно быть преобразовано из строки в соответствии с типом свойства.
-
Чтобы отразить атрибут (установить атрибут из свойства), значение свойства должно быть преобразовано в строку.
Булевы свойства, которые отображают атрибут, должны по умолчанию иметь значение false. Для получения дополнительной информации см. раздел Булевы атрибуты.
Установка имени атрибута¶
По умолчанию Lit создает соответствующий наблюдаемый атрибут для всех публичных реактивных свойств. Имя наблюдаемого атрибута — это имя свойства, выделенное строчными буквами:
1 2 3 |
|
1 2 3 4 5 6 7 8 9 |
|
Чтобы создать наблюдаемый атрибут с другим именем, задайте attribute
в виде строки:
1 2 3 |
|
1 2 3 4 5 6 7 8 9 |
|
Чтобы предотвратить создание наблюдаемого атрибута для свойства, установите attribute
в false
. Свойство не будет инициализироваться из атрибутов в разметке, и изменения атрибутов не будут влиять на него.
1 2 3 |
|
1 2 3 4 5 6 7 8 9 |
|
Внутреннее реактивное состояние никогда не имеет связанного атрибута.
Наблюдаемый атрибут может быть использован для предоставления начального значения свойства из разметки. Например:
1 |
|
Использование конвертера по умолчанию¶
Lit имеет конвертер по умолчанию, который обрабатывает типы свойств String
, Number
, Boolean
, Array
и Object
.
Чтобы использовать конвертер по умолчанию, укажите параметр type
в объявлении свойства:
1 2 3 |
|
1 2 3 4 5 6 7 8 9 |
|
Если вы не укажете тип или пользовательский конвертер для свойства, оно будет вести себя так же, как если бы вы указали type: String
.
В таблицах ниже показано, как конвертер по умолчанию обрабатывает преобразование для каждого типа.
Из атрибута в свойство
Тип | Преобразование |
---|---|
String | Если элемент имеет соответствующий атрибут, установите свойство в значение атрибута. |
Number | Если элемент имеет соответствующий атрибут, установите свойство в Number(attributeValue) . |
Boolean | Если элемент имеет соответствующий атрибут, установите свойство в true. Если нет, установите свойство в false. |
Object , Array | Если элемент имеет соответствующий атрибут, установите значение свойства в JSON.parse(attributeValue) . |
Для любого случая, кроме Boolean
, если элемент не имеет соответствующего атрибута, свойство сохраняет значение по умолчанию, или undefined
, если значение по умолчанию не задано.
От свойства к атрибуту
Тип | Преобразование |
---|---|
String , Number | Если свойство определено и не является нулевым, установите атрибут в значение свойства. Если свойство равно null или не определено, удалите атрибут. |
Boolean | Если свойство истинно, создайте атрибут и установите его значение в пустую строку. Если свойство ложное, удалите атрибут |
Object , Array | Если свойство определено и не является нулевым, установите атрибут в JSON.stringify(propertyValue) . Если свойство равно null или не определено, удалите атрибут. |
Предоставление пользовательского конвертера¶
Вы можете указать пользовательский конвертер свойств в объявлении свойства с помощью опции converter
:
1 2 3 |
|
converter
может быть объектом или функцией. Если это объект, то он может иметь ключи fromAttribute
и toAttribute
:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Если converter
является функцией, то она используется вместо fromAttribute
:
1 2 3 4 5 6 |
|
Если для отраженного атрибута не указана функция toAttribute
, атрибут устанавливается в значение свойства с помощью конвертера по умолчанию.
Если toAttribute
возвращает null
или undefined
, атрибут удаляется.
Булевы атрибуты¶
Чтобы булево свойство можно было настраивать из атрибута, по умолчанию оно должно быть равно false
. Если по умолчанию оно равно true
, вы не сможете установить его в false
из разметки, поскольку наличие атрибута, со значением или без него, равносильно true
. Это стандартное поведение для атрибутов в веб-платформе.
Если такое поведение не подходит для вашего случая, есть несколько вариантов:
-
Изменить имя свойства, чтобы по умолчанию оно имело значение
false
. Например, в веб-платформе используется атрибутdisabled
(по умолчаниюfalse
), а неenabled
. -
Вместо этого используйте атрибут со строковым или числовым значением.
Включение отражения атрибутов¶
Вы можете настроить свойство так, чтобы при каждом изменении его значение отражалось на соответствующем атрибуте. Отраженные атрибуты полезны, потому что атрибуты видны в CSS и в DOM API, таких как querySelector
.
Например:
1 2 3 4 |
|
При изменении свойства Lit устанавливает соответствующее значение атрибута, как описано в разделе Использование конвертера по умолчанию или Предоставление пользовательского конвертера.
Атрибуты, как правило, считаются вводимыми в элемент от его владельца, а не контролируемыми самим элементом, поэтому отражение свойств в атрибуты следует делать крайне редко. Сегодня это необходимо для таких случаев, как стилизация и доступность, но ситуация может измениться, поскольку платформа добавляет такие функции, как псевдо-селектор :state
и Accessibility Object Model, которые заполняют эти пробелы.
Не рекомендуется отражать свойства типа object
или array
. Это может привести к сериализации больших объектов в DOM, что может привести к снижению производительности.
Вы, наверное, уже поняли, что если изменения свойства отражаются на атрибуте, а изменения атрибута обновляют свойство, то это может привести к созданию бесконечного цикла. Однако Lit отслеживает момент установки свойств и атрибутов специально для того, чтобы этого не происходило
Пользовательские аксессоры свойств¶
По умолчанию LitElement
генерирует пару getter/setter для всех реактивных свойств. Сеттер вызывается каждый раз, когда вы устанавливаете свойство:
1 2 3 4 5 6 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
Сгенерированные аксессоры автоматически вызывают requestUpdate()
, инициируя обновление, если оно еще не началось.
Создание пользовательских аксессоров свойств¶
Чтобы указать, как работает получение и установка свойства, вы можете определить свою собственную пару геттер/сеттер. Например:
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
Чтобы использовать пользовательские аксессоры свойств с декораторами @property
или @state
, поместите декоратор на сеттер, как показано выше. Декорированные сеттеры @property
или @state
вызывают requestUpdate()
.
В большинстве случаев создавать пользовательские аксессоры свойств не нужно. Для вычисления значений из существующих свойств рекомендуется использовать обратный вызов willUpdate
, который позволяет устанавливать значения во время цикла обновления, не вызывая дополнительного обновления. Для выполнения пользовательского действия после обновления элемента рекомендуется использовать обратный вызов updated
. Пользовательский сеттер можно использовать в редких случаях, когда важно синхронно подтвердить любое значение, установленное пользователем.
Если ваш класс определил свои собственные аксессоры для свойства, Lit не будет перезаписывать их сгенерированными аксессорами. Если ваш класс не определил аксессоров для свойства, Lit сгенерирует их, даже если свойство или аксессоры определены в суперклассе.
Запретить Lit генерировать аксессоры для свойств¶
В редких случаях подклассу может потребоваться изменить или добавить параметры свойства, существующего в его суперклассе.
Чтобы Lit не генерировал аксессор свойства, который перезаписывает определенный аксессор суперкласса, установите значение noAccessor
на true
в объявлении свойства:
1 2 3 |
|
Вам не нужно устанавливать noAccessor
при определении собственных аксессоров.
Настройка обнаружения изменений¶
Все реактивные свойства имеют функцию hasChanged()
, которая вызывается, когда свойство установлено.
hasChanged
сравнивает старое и новое значения свойства и оценивает, изменилось ли свойство. Если hasChanged()
возвращает true
, Lit запускает обновление элемента, если оно еще не запланировано. Подробнее об обновлениях см. в разделе Реактивный цикл обновления .
Реализация hasChanged()
по умолчанию использует строгое сравнение неравенств: hasChanged()
возвращает true
, если newVal !== oldVal
.
Чтобы настроить hasChanged()
для свойства, укажите его в качестве опции свойства:
1 2 3 4 5 6 |
|
1 2 3 4 5 6 7 |
|
В следующем примере hasChanged()
возвращает true
только для нечетных значений.