LitElement и .bind (это)

Я нахожу немного запутанным, иногда вам нужно привязать контекст к вызову функции, а иногда нет. Когда именно можно этого избежать, а когда нет?

В чем разница между этими строками?

<a @click="${this.handler.bind(this)}">link</a>

<a @click="${this.handler()}">link</a>

<a @click="${this.handler}">link</a>

<a @click="${()=>this.handler()}">link</a>

Также иногда в constructor() вам нужно

this.handler = this.handler.bind(this)

чтобы вызов функции работал, а иногда и нет. В чем разница между всеми этими случаями?


person alfredopacino    schedule 20.10.2019    source источник


Ответы (2)


Я предполагаю, что в первых 4 строках вы обычно помещаете их между ${ и }. При этом упомяните эти 3 строки:

<a @click="${this.handler.bind(this)}">link</a>
<a @click="${this.handler}">link</a>
<a @click="${()=>this.handler()}">link</a>

будет очень похож по эффекту. Когда вы присоединяете обработчик событий, вам нужно передать ссылку на функцию / метод для вызова, а вторая строка:

<a @click="${this.handler()}">link</a>

скорее всего будет просто неправ. Это потому, что вы вызовете this.handler(), как только закончится рендеринг. Единственный способ, которым это может иметь смысл, - это если метод handler возвращает функцию.

Возвращаясь к первым трем строкам: они действительно будут иметь очень похожий эффект, поскольку во всех из них вы передаете ссылки на локальный метод рендеринга, но:

  • первый не требует привязки, так как метод все равно будет вызываться в this контексте (но, думаю, это тоже не повредит)
  • третий добавляет определение анонимной функции в качестве обработчика событий, который при вызове будет вызывать this.handler

Так что второй в основном самый простой и легкий для чтения.

Когда дело доходит до необходимости вызывать bind в некоторых случаях: это необходимо, когда ваш метод вызывается из другого контекста. Допустим, вы передаете его как параметр обратного вызова методу, работающему в другом классе. Когда ваш метод будет вызван там, он будет по умолчанию иметь контекст этого другого объекта. Поэтому вам нужно будет использовать .bind (this), чтобы убедиться, что при вызове метода в контексте вашего элемента.

Ваш метод работает, но он запущен из другого this. Так что, возможно, именно поэтому иногда вы не замечаете необходимости .bind(this). Если метод просто показывает сообщение, вызывает какую-либо другую внешнюю службу или что-то еще, что не является частью определения вашего объекта (которое не использует this.something), он будет работать без использования привязки.

ОБНОВЛЕНИЕ: на самом деле я думаю, что обработчик событий всегда будет запускаться в контексте элемента, который вызвал это событие. Я предполагаю, что lit как раз делает это .bind(this), когда мы используем этот @click= синтаксис, поскольку это имело бы большой смысл.

person mishu    schedule 21.10.2019
comment
позвольте мне прояснить, конечно, здесь this.handler() - это метод класса, который расширяет LitElement, поэтому с this я ожидаю, что буду ссылаться на класс и иметь доступ к его свойствам и методам - person alfredopacino; 24.10.2019

Обратите внимание, что привязка напрямую в шаблонах, как правило, не является хорошей идеей. Это может привести к проблемам с производительностью и повторному рендерингу компонентов, когда этого не должно быть. https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/no-template-bind.md

Что касается того, почему вам иногда нужно связывать, подумайте вот о чем:

@customElement('main-page')
export class MainPage extends LitElement {
  @property()
  public name = "bob";

  render() {
    return html`<button @click=${this.clicked}>Click Me!</button>`;
  }

  clicked() {
      console.log("clicked " + this.name);
      console.log("this in clicked: " + this)
      setTimeout(this.sayHi, 1000);
  }

  sayHi() {
    console.log("this in sayHi: " + this)
    alert(`Hello, ${this.name}!`);
  }
 }

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

Но имя не отображается в предупреждении:

введите описание изображения здесь

Если мы посмотрим на this в обеих функциях, мы получим разные результаты:

введите описание изображения здесь

Один из них - это HTMLElement, а другой - это окно. Что означает this, изменится после того, как мы передадим sayHi в setTimeout.

Bind исправит это, убедившись, что это относится к правильному объекту.

Хотя мы могли бы сделать setTimeout(this.sayHi.bind(this), 1000);, и это решило бы нашу проблему, но это не очень чисто и подвержено ошибкам. Мы могли бы привязать его в конструкторе, но это тоже ненамного лучше.

Самый простой способ сделать это - напрямую привязать this, используя вместо этого стрелочную функцию:

sayHi = () => {
[...]

Эта запись привязывает this к самому объекту. Тогда нам не нужно явно связывать его сами. Как только мы это сделаем, оба this будут ссылаться на нужный объект:

введите описание изображения здесь

person Hastaroth    schedule 20.04.2021