Mixin

クラスmixinは、標準のJavaScriptを使用してクラス間でコードを共有するためのパターンです。「has-a」の構成パターン(リアクティブコントローラーなど)とは対照的に、クラスが動作を追加するためにコントローラーを所有できる場合、mixinは「is-a」の構成を実装します。mixinによって、クラス自体が共有される動作のインスタンスとしてなるようにします。

mixinを使用して、APIを追加したり、ライフサイクルコールバックをオーバーライドしたりすることで、Litコンポーネントをカスタマイズできます。

mixinは、適用先のクラスをオーバーライドし、mixinの動作で拡張されたサブクラスを返す「サブクラスファクトリー」と考えることができます。mixinは標準のJavaScriptクラス式を使用して実装されているため、新しいフィールド/メソッドの追加、既存のスーパークラスメソッドのオーバーライド、superの使用など、サブクラス化で使用できるすべてのイディオムを使用できます。

読みやすくするために、このページのサンプルでは、mixin関数のTypeScript型の一部を省略しています。TypeScriptでのmixinの適切な型付けの詳細については、「TypeScriptでのmixin」を参照してください。

mixinを定義するには、superClassを受け取り、それを拡張する新しいクラスを返す関数を記述し、必要に応じてフィールドとメソッドを追加します。

mixinを適用するには、mixinが適用されたサブクラスを生成するために、クラスを渡すだけです。ほとんどの場合、ユーザーは新しいクラスを定義するときに、mixinを基本クラスに直接適用します。

mixinを使用して、ユーザーが通常のクラスのように拡張できる具象サブクラスを作成することもできます。この場合、mixinは実装の詳細です。

クラスmixinはLit固有ではなく、標準のJavaScriptパターンであるため、コードの再利用のためにmixinを活用する方法に関する多くの情報がコミュニティに存在します。mixinに関する詳細については、次の参考文献を参照してください。

  • MDNのクラスmixin
  • Justin FagnaniによるJavaScriptクラスを使用した実際のmixin
  • TypeScriptハンドブックのMixin
  • open-wcによるDedupe mixinライブラリ。mixinの使用が重複につながる可能性がある場合や、重複排除ライブラリを使用して重複を回避する方法に関する説明が含まれています。
  • Elix Webコンポーネントライブラリが従うMixinの規則。Lit固有ではありませんが、Webコンポーネントのmixinを定義する際に規則を適用するための思慮深い提案が含まれています。

LitElementに適用されるmixinは、constructor()connectedCallback()などの標準のカスタム要素ライフサイクルコールバック、およびrender()updated()などのリアクティブ更新ライフサイクルコールバックを実装またはオーバーライドできます。

たとえば、次のmixinは、要素が作成、接続、および更新されたときにログを記録します。

mixinは、常にLitElementによって実装される標準のカスタム要素ライフサイクルメソッドへのスーパーコールを行う必要があることに注意してください。リアクティブ更新ライフサイクルコールバックをオーバーライドする場合は、スーパークラスに既に存在する場合は、スーパーメソッドを呼び出すことをお勧めします(上記のオプションチェーン呼び出しsuper.updated?.()のように)。

また、mixinは、スーパーコールを行うタイミングの選択を通じて、標準のライフサイクルコールバックの基本実装の前または後のいずれかで作業を行うことを選択できることにも注意してください。

mixinは、サブクラス化された要素にリアクティブプロパティスタイル、およびAPIを追加することもできます。

次の例のmixinは、要素にhighlightリアクティブプロパティと、ユーザーがコンテンツをラップするために呼び出すことができるrenderHighlight()メソッドを追加します。highlightプロパティ/属性が設定されている場合、ラップされたコンテンツは黄色でスタイル設定されます。

上記の例では、mixinのユーザーは、render()メソッドからrenderHighlight()メソッドを呼び出すこと、およびmixinによって定義されたstatic stylesをサブクラスのスタイルに追加することに注意する必要があります。mixinとユーザー間のこのコントラクトの性質は、mixinの定義次第であり、mixinの作成者がドキュメント化する必要があります。

TypeScriptでLitElement mixinを記述する場合は、注意すべき点がいくつかあります。

superClass引数を、ユーザーが拡張することを期待するクラスの型(存在する場合)に制約する必要があります。これは、以下に示すようにジェネリックConstructorヘルパー型を使用して実現できます。

上記の例では、mixinに渡されるクラスがLitElementから拡張されていることを保証しているため、mixinはLitが提供するコールバックやその他のAPIに依存できます。

TypeScriptには、mixinパターンを使用して生成されたサブクラスの戻り値を推論するための基本的なサポートがありますが、推論されたクラスにprivateまたはprotectedアクセス修飾子を持つメンバーを含めることはできないという深刻な制限があります。

LitElement自体にプライベートメンバーとプロテクトメンバーがあるため、デフォルトでは、LitElementを拡張するクラスを返す場合、TypeScriptは「エクスポートされたクラス式のプロパティ'...'は、プライベートまたはプロテクトにすることはできません。」というエラーを返します。

上記のエラーを回避するために、mixin関数からの戻り値をキャストするという2つの回避策があります。

mixinが新しいパブリック/プロテクトAPIを追加しない場合

「mixinが新しいパブリック/プロテクトAPIを追加しない場合」へのパーマリンク

mixinがLitElementのメソッドまたはプロパティのみをオーバーライドし、独自の新しいAPIを追加しない場合は、生成されたクラスを渡されたスーパークラスの型Tにキャストするだけで済みます。

mixinが新しいパブリック/プロテクトAPIを追加する場合

「mixinが新しいパブリック/プロテクトAPIを追加する場合」へのパーマリンク

mixinが、クラスで使用できるようにする必要のある新しいプロテクトまたはパブリックAPIを追加する場合は、実装とは別にmixinのインターフェイスを定義し、mixinインターフェイスとスーパークラス型の交差として戻り値をキャストする必要があります。

TypeScriptの型システムの制限により、デコレータ(@property()など)は、クラス式ではなくクラス宣言ステートメントに適用する必要があります。

実際には、これはTypeScriptのmixinが、クラスを宣言してから返す必要があり、アロー関数からクラス式を直接返す必要がないことを意味します。

サポートされている

サポートされていない