PolymerユーザーのためのLit
LitはPolymerライブラリの後継です。Polymerで構築されたプロジェクトがあり、それをLitに移行したい場合、またはPolymerに精通していて、それがLitとどのように比較されるかを知りたい場合は、このドキュメントが適切です。
このドキュメントでは、LitとPolymerの関係の概要を説明し、一般的なPolymerコードがLitにどのように変換されるかを示すクックブックを提供します。
LitとPolymerの関係は?
「LitとPolymerの関係は?」へのパーマリンクPolymerは、Webコンポーネントを構築するための最初のライブラリの1つでした。LitはPolymerの後継であり、同じチームによって構築され、同じ目的の多くを持っています。プロジェクトは多くの目標を共有していますが、LitはPolymerの開発中に学んだ教訓を活用しています。
PolymerはLitの前身であるため、2つの間には多くの類似点があります。どちらのライブラリも、組み込みのHTML要素のように動作するコンポーネントを簡単に構築でき、宣言的なHTMLテンプレートを備えています。
LitはPolymerといくつかの点で異なります。
Litのレンダリングはデフォルトで非同期であり、バッチ処理されます。いくつかの例外を除き、すべてのPolymer更新は同期です。
Litは、プロパティの変更を監視し、そこから派生値を計算するための強力なメカニズムを提供する更新ライフサイクルを公開します。Polymerには宣言的なオブザーバーと算出プロパティがありますが、オブザーバーが実行される順序を予測するのは難しい場合があります。
Litは、ネイティブJavaScriptモジュールを使用したJavaScriptファーストのオーサリングに焦点を当てています。Polymerは、Webプラットフォームから削除されたHTML Imports仕様によって可能になった、元々HTMLファーストのオーサリングに焦点を当てていました。
Lit式は標準のJavaScriptを使用します。Polymerは、バインディングで限定されたドメイン固有言語を使用します。Litは標準のJavaScriptを使用するため、Polymerが専用のヘルパー要素を使用する式(条件付きテンプレートと繰り返しテンプレート)内でJavaScriptを制御フローに使用することもできます。
Litは、単方向データフローを備えたシンプルで理解しやすいメンタルモデルを目指しています。Polymerは双方向データバインディングとオブザーバーをサポートしており、シンプルなプロジェクトでは非常に優れていますが、プロジェクトが複雑になるにつれてデータフローについて推論するのが難しくなる傾向があります。
PolymerからLitへの移行
「PolymerからLitへの移行」へのパーマリンクPolymerからLitへの移行を計画している場合、一度にすべてを行う必要はありません。PolymerとLitは連携して動作するため、一度に1つのコンポーネントを移行できます。
Litの使用を開始する前に、プロジェクトを更新するためにいくつかのことを行う必要があります。
プロジェクトをPolymer 3に更新します。
プロジェクトのツールが新しいJavaScript機能をサポートしていることを確認します。
双方向バインディングを削除します。
Polymer 3への更新
「Polymer 3への更新」へのパーマリンクPolymer 2.x以前はHTMLインポートを使用しており、これはWebプラットフォームから削除されました。Polymer 3とLitはどちらもJavaScriptモジュールとして配布されているため、連携してうまく機能し、最新のWebツールを幅広く活用できます。
ほとんどの場合、移行プロセスのほとんどは、Polymer modulizerツールを使用して自動化できます。詳細については、Polymer 3.0アップグレードガイドを参照してください。
言語サポート
「言語サポート」へのパーマリンクPolymerは、JavaScript仕様のECMAScript 2015バージョンからの機能を使用しました。Polymerスターターキットのいずれかから始めた場合、ツールチェーンが新しいJavaScriptをサポートしていない可能性があります。
LitはECMAScript 2019の機能を使用しています(一部のLitのサンプルコードには、より新しい言語機能が含まれている場合があります)。これらの新しいJavaScript機能を処理しない場合は、ツールを更新する必要があります。例:
パブリックインスタンスフィールドとパブリック静的フィールド。これらは、サンプルコードで広く使用されています。
static styles = [ css`:host { display: block; }` ];
非同期とawait。これらはpromiseベースのコードを簡略化するために使用できます。例:
async _loginClickHandler() {
this.loggedIn = true;
// Wait for `loggedIn` state to be rendered to the DOM
await this.updateComplete;
this.dispatchEvent(new Event('login'));
}
Litの言語要件の詳細については、要件を参照してください。
双方向バインディングの削除
「双方向バインディングの削除」へのパーマリンクPolymerの双方向バインディングは、ホストプロパティを子プロパティと効果的に緊密に結合します。この緊密な結合により、多くの問題が発生するため、チームはLitに双方向バインディングを追加しないことを選択しました。
Litに移行する前に双方向バインディングを削除すると、移行の複雑さが軽減され、移行を開始する前に、双方向バインディングなしでPolymerでアプリケーションをテストできます。
アプリケーションが双方向バインディングを使用していない場合は、このセクションをスキップできます。
双方向バインディングの問題点
「双方向バインディングの問題点」へのパーマリンク双方向バインディングの場合、Polymerは独自のプロトコルを使用します。これには、3つの主要なコンポーネントがあります。
ホストから子へのバインディング。
子要素からのプロパティ変更イベントを処理するイベントリスナー。
プロパティが変更されたときに、子が変更イベントを自動的に発生させる機能(
notify: true
)。
この最後の項目が最も問題です。コンポーネントはプロパティの変更に対して変更イベントを発生させ、各変更イベントは同期的に処理されます。アプリケーション全体で双方向バインディングを広く使用すると、データフローとコンポーネントが自身を更新する順序について推論するのが難しくなる可能性があります。
理想的には、イベントは、他の方法では簡単に観察できない明示的な変更を伝えるために送信される個別のシグナルです。Polymerが行うように、プロパティの設定の副作用としてイベントを送信すると、通信が冗長になり、暗黙的になる可能性があります。特にこの暗黙的な動作により、データフローを理解するのが難しくなる可能性があります。
カスタム要素のベストプラクティスガイドラインを要約すると、コンポーネントは次の場合にイベントを発生させる必要があります。
ユーザーインタラクションの結果として要素の状態が変化した場合 - ボタンをクリックしたり、コンポーネント内のテキストフィールドを編集したりする場合など。
コンポーネント内で何らかの内部変化が発生した場合 - タイマーがオフになったり、アニメーションが完了したりする場合など。
理想的には、コンポーネントは、低レベルのUIイベントがバブルアウトするのではなく、変更された内容を記述するセマンティックイベントを発生させる必要があります。たとえば、ユーザーがプロファイルデータを更新できるフォームは、ユーザーが完了ボタンをクリックしたときにprofile-updated
イベントを発生させる可能性があります。profile-updated
イベントは親に関連しますが、クリックイベントはそうではありません。
双方向バインディングの置き換え
「双方向バインディングの置き換え」へのパーマリンク双方向バインディングを置き換える方法はたくさんあります。既存のPolymerアプリケーションをLitに移行しようとしている場合は、双方向バインディングを同様の機能を提供するコードに置き換えるだけです。
自動プロパティ変更イベントを削除します(
notify: true
)。自動プロパティ変更イベントをより意図的な変更イベントに置き換えます。
双方向バインディング注釈を、一方向バインディングとイベントハンドラーに置き換えます。(このステップはオプションですが、Litへの移行が簡単になります。)
たとえば、ユーザーインタラクションによって子が通知プロパティを更新している場合は、notify: true
を削除し、ユーザーインタラクションイベントに基づいて、手動でイベント変更イベントを発生させます。
次のPolymerコードについて考えてみましょう。
static get properties() {
return {
name: {
type: String,
notify: true
}
};
}
static get template() {
return html`
Name: [[name]]<br>
Enter name: <input value={{name::input}}>
`;
}
このコンポーネントは、input
要素への双方向バインディングを使用し、通知プロパティを持っているため、親要素はname
プロパティで双方向バインディングを使用できます。input
イベントが発生したときにのみバインディングが設定されるため、入力への双方向バインディングは意味があります。ただし、input
イベントのイベントリスナーを追加することにより、通知プロパティを削除できます。
static properties = {
name: {
type: String,
}
}
static get template() {
return html`
Name: [[name]]<br>
Enter name: <input on-input="inputChanged" value=[[name]]>
`;
}
inputChanged(e) {
this.name = e.target.value;
const propChangeEvent = new CustomEvent('name-changed', {
detail: { value: this.name }
});
this.dispatchEvent(propChangeEvent);
}
このコードは、明示的に入力イベントをリッスンし、name-changed
イベントを発生させます。このコードは、Polymerの双方向バインディングプロトコルを予期する親で機能するため、コンポーネントを一度に1つずつ更新できます。
このコードは、親がname
プロパティを設定するときにプロパティ変更イベントを発生させません。これは良いことです。そして、このコードをLitにかなり直接移行できます。
このようなイベントハンドラーを使用すると、たとえば、ユーザーが特定の時間間隔で入力を停止した場合にのみname-changed
イベントを発生させるなど、子コンポーネントにロジックを追加することもできます。
単方向データフロー。
もう1つの代替手段は、Reduxのような状態コンテナーを使用して、双方向バインディングを単方向データフローパターンに置き換えることです。個々のコンポーネントは、状態の更新をサブスクライブし、状態を更新するためのアクションをディスパッチできます。これは新しい開発に推奨されますが、双方向バインディングに基づくアプリケーションが既にある場合は、より多くの作業が必要になる場合があります。
PolymerからLitへのクックブック
「PolymerからLitへのクックブック」へのパーマリンクこのセクションでは、Polymerコードが一般的なタスクをどのように処理するかを示し、同等のLitコードを示します。これは、Polymerにすでに精通していてLitを学習したい場合や、既存のプロジェクトをPolymerからLitに移行している場合に役立ちます。
このセクションでは、Polymer 3.0を使用していることを前提としています。プロジェクトをPolymerからLitに移行する場合は、まずPolymer 3.0に移行する必要があります。
既存のプロジェクトの移行に関する詳細については、PolymerからLitへの移行を参照してください。
JavaScriptまたはTypeScript?
Litはどちらも適切に動作します。ほとんどのLitの例は、切り替え可能なコードサンプルウィジェットを使用して表示されるため、TypeScriptまたはJavaScriptの構文を選択できます。
コンポーネントの定義
「コンポーネントの定義」へのパーマリンクPolymerとLitはどちらもWebコンポーネントに基づいているため、コンポーネントの定義は両方のライブラリで非常によく似ています。最も単純なケースでは、唯一の違いはベースクラスです。
Polymer
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
export class MyElement extends PolymerElement { /* ... */ }
customElements.define('my-element', MyElement);
Lit
import {LitElement} from 'lit';
import {customElement} from 'lit/decorators.js';
@customElement('my-element')
export class MyElement extends LitElement { /* ... */ }
import {LitElement} from 'lit';
export class MyElement extends LitElement { /* ... */ }
customElements.define('my-element', MyElement);
デコレーターについて
「デコレーターについて」へのパーマリンクLitは、開発者のエクスペリエンスを向上させるためのデコレーターのセットを提供します。前のセクションのTypeScriptの例で示されているcustomElement
などがそうです。このサイトのTypeScriptサンプルにはデコレーターが含まれていますが、JavaScriptサンプルでは省略されています。しかし、実際にはJavaScriptでもデコレーターを使用でき、TypeScriptでもデコレーターを使用しないことができます。それはあなた次第です。JavaScriptでデコレーターを使用するには、Babelのようなコンパイラーが必要です。(TypeScriptにはすでにコンパイラーが必要なので、デコレーターを処理するように設定するだけです。)詳細については、デコレーターを参照してください。
DOMテンプレート
「DOMテンプレート」へのパーマリンクPolymer 3とLitはどちらも、テンプレートを定義するためのhtml
タグ関数を提供します。
Polymer
import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
export class MyElement extends PolymerElement {
static get template() {
return html`<b>Hello</b>`;
}
}
customElements.define('my-element', MyElement);
Lit
import {LitElement, html} from 'lit';
import {customElement} from 'lit/decorators.js';
@customElement('my-element')
export class MyElement extends LitElement {
render() {
return html`<b>Hello</b>`;
}
}
import {LitElement, html} from 'lit';
export class MyElement extends LitElement {
render() {
return html`<b>Hello</b>`;
}
}
customElements.define('my-element', MyElement);
Litのhtml
はPolymerのhtml
とは異なることに注意してください。基本的な目的は同じですが、動作が異なります。Polymerのhtml
は要素の初期化中に1回だけ呼び出されます。Litのhtml
は通常、更新ごとに呼び出されます。設定作業はテンプレートリテラル文字列ごとに1回実行されるため、その後の増分更新のためのLitのhtml
関数の呼び出しは非常に高速です。
Litテンプレートの詳細については、テンプレートの概要を参照してください。
スタイル
「スタイル」へのパーマリンクPolymerコンポーネントには通常、テンプレートに直接スタイルが含まれています。
static get template() {
return html`
<style>
.fancy { color: blue; }
</style>
...
`;
}
Litでは、通常、css
タグ関数を使用して、静的なstyles
フィールドにスタイルを提供します。
import {LitElement, css, html} from 'lit';
...
static styles = css`.fancy { color: blue; }`;
Polymerで行うように、テンプレートに直接styleタグを追加することもサポートされています。
import {LitElement, html} from 'lit';
...
render() {
return html`
<style>
.fancy { color: blue; }
</style>
...
}
styleタグを使用すると、スタイルはクラスごとに1回ではなく、インスタンスごとに1回評価されるため、静的なstyles
フィールドよりもわずかにパフォーマンスが低下する可能性があります。
詳細については、スタイルを参照してください。
データバインディング
「データバインディング」へのパーマリンクPolymerにデータバインディングがある場所では、Litはテンプレート内に式を持っています。Litの式は、テンプレート内の特定の場所にバインドされた標準のJavaScript式です。そのため、Litの式はPolymerのデータバインディングでできるほぼすべてのこと、およびPolymerでは簡単にできない多くのことを実行できます。
双方向バインディング。
Litチームは、双方向データバインディングを実装しないという意図的な選択をしました。この機能はいくつかの一般的なニーズを簡略化するように見えますが、実際にはデータフローとコンポーネントが自身を更新する順序について推論するのが困難になります。Litに移行する前に、双方向バインディングを削除することをお勧めします。詳細については、双方向バインディングの削除を参照してください。
Polymer
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
class UserView extends PolymerElement {
static get properties() {
return {
name: String
};
}
static get template() {
return html`
<div>[[name]]</div>
`;
}
}
Lit
import {html, LitElement} from 'lit';
import {property} from 'lit/decorators.js';
class UserView extends LitElement {
@property()
name: string;
render() {
return html`
<div>${this.name}</div>
`;
}
}
import {html, LitElement} from 'lit';
class UserView extends LitElement {
static properties = {
name: {}
}
render() {
return html`
<div>${this.name}</div>
`;
}
}
ご覧のとおり、主な違いは、Polymerのバインディングを囲む二重角括弧を置き換えることです。
[[式]]
タグ付きテンプレートリテラルの式構文を使用します。
${式}
また、Litの式では、単にname
の代わりにthis.name
を使用していることにも注意してください。Polymerのバインディングでは、プロパティ名やパス(user.id
やshapes[4].type
など)など、バインディングアノテーション内の一部のもののみが許可されています。プロパティ名またはパスは、現在のバインディングスコープを基準に評価されます。
Litでは、標準のJavaScript式を使用でき、標準のJavaScriptスコープが適用されます。たとえば、render()
メソッド内で作成されたローカル変数にアクセスしたり、this
を使用してインスタンス変数にアクセスしたりできます。
render() {
let count = this.getMatchingItems(this.pattern);
return html`You have ${count} matches.`
}
Polymerと同様に、Litは式を使用してプロパティ、属性、およびイベントハンドラーを設定することをサポートしています。Litはわずかに異なる構文を使用し、サフィックスではなくプレフィックスを使用します。次の表に、PolymerとLitのバインディング構文の違いをまとめます。
タイプ | Polymer | Lit |
---|---|---|
プロパティ | プロパティ名=[[値]] | .propertyName=${値} |
属性 | 属性名$=[[値]] | 属性名=${値} |
ブール属性 | 属性名?=[[値]] | ?属性名=${値} |
イベント | on-イベント名$=[[ハンドラー]] | @イベント名=${ハンドラー} |
メモ
プロパティ式。Litは、ピリオドで始まるリテラルプロパティ名を使用します。Polymerは、対応する属性名を使用します。
イベントハンドラー。Litでは、ハンドラーは、
${this.clickHandler}
のようなメソッド、またはアロー関数にすることができます。アロー関数を使用すると、他のデータを閉じたり、異なるシグネチャで関数を呼び出したりできます。例:<input @change=${(e) => this.setValue(e.target.value)}>
詳細については、式を参照してください。
双方向バインディング
「双方向バインディング」へのパーマリンク一般的に、PolymerプロジェクトをLitに移行する前に、双方向バインディングを削除することをお勧めします。詳細については、双方向バインディングの削除を参照してください。
双方向バインディングを一方通行バインディングとイベントリスナーに置き換えた場合は、それらをLitに直接移行できるはずです。
以前は双方向バインディングを使用してPolymerの子コンポーネントと通信していた親コンポーネントを置き換えるためにLitコンポーネントを作成している場合は、プロパティを設定するためのプロパティ式と、property-changed
イベントを処理するためのイベントリスナーを追加します。
Polymer
static get template() {
return html`
<polymer-child
childprop="[[parentprop]]"
on-childprop-changed="childpropChanged">
</polymer-child>
`;
}
childpropChanged(e) {
this.parentprop = e.detail.value;
}
Lit
static template = html`
<polymer-child
.childprop=${this.parentprop}
@childprop-changed=${(e) => this.parentprop = e.detail.value}>
</polymer-child>
`;
条件付きテンプレート
「条件付きテンプレート」へのパーマリンクPolymerは、dom-if
ヘルパー要素を使用して条件をサポートします。
<template is="dom-if" if="">
<div>condition is true</div>
</template>
Litでは、JavaScriptの条件式を使用できます。条件演算子(または三項演算子)がうまく機能します。
html`${this.condition
? html`<div>condition is true</div>`
: ''
}`;
Polymerのdom-if
とは異なり、条件演算子を使用すると、trueとfalseの両方の条件のコンテンツを提供できますが、例のように、空の文字列を返して何もレンダリングしないこともできます。
詳細については、条件を参照してください。
DOMの非表示または再作成
「DOMの非表示または再作成」へのパーマリンクデフォルトでは、Polymerのdom-if
はLitの条件とは少し異なる動作をします。条件が真の値から偽の値に変わると、dom-if
は条件付きDOMをDOMツリーから削除するのではなく、単に非表示にします。これにより、条件が再び真になったときに一部のリソースを節約できる場合があります。
Polymerのdom-if
をLitに移行する場合、いくつかの選択肢があります。
単純なJavaScript条件を使用します。Litは、条件が偽に変わると、条件付きDOMを削除して破棄します。
dom-if
は、restamp
プロパティをtrue
に設定した場合と同じことを行います。標準の
hidden
属性を使用して、ページから削除せずにコンテンツを非表示にします。<header hidden=${this.headerHidden}>
これは非常に軽量です。ただし、条件がfalseの場合でも、最初のレンダリングでDOMが作成されます。
条件が変更されたときにDOMの破棄と再作成を避けるには、条件を
cache
ディレクティブでラップします。
ほとんどの場合、単純な条件がうまく機能します。条件付きDOMが大きく複雑で、条件がtrueに切り替わるときにDOMを再作成するのに遅延が見られる場合は、Litのcache
ディレクティブを使用して条件付きDOMを保持できます。cache
を使用すると、DOMはツリーから削除されますが、メモリにキャッシュされるため、条件が変更されたときにリソースを節約できます。
import {LitElement, html} from 'lit';
import {cache} from 'lit/directives/cache.js';
...
return html`${cache(this.condition
? html`<div>condition is true</div>`
: ''
)}`;
これにより、条件がfalseの場合は何もレンダリングされないため、初期ページロード時に複雑なDOMを作成することを避けるために使用できます。
繰り返しテンプレート
「繰り返しテンプレート」へのパーマリンクPolymerは、繰り返しテンプレートにdom-repeat
ヘルパーを使用します。
<template is="dom-repeat" items="">
<li></li>
</template>
dom-repeat
内のテンプレートは、ホスト要素のプロパティ、およびdom-repeat
によって追加されたitem
およびindex
プロパティという、限られたプロパティセットにアクセスできます。
条件付きと同様に、Litは式が値の配列を返すことによって、JavaScriptを使用して繰り返しを処理できます。Litのmap
ディレクティブは、map()
配列メソッドのように機能しますが、セットやジェネレーターなどの他の種類の反復可能オブジェクトも受け入れます。
import {map} from 'lit/directives/map.js';
...
render() {
return html`
<ul>
${map(this.items, (item) =>
html`<li>${item.name}</li>`)
}
</ul>
`;
}
Polymerのバインディングは特定のプロパティにのみアクセスできますが、Litの式はJavaScriptスコープで利用可能なものすべてにアクセスできます。
render()
メソッドで配列を生成することもできます。
render() {
const itemTemplates = [];
for (i of this.items) {
itemTemplates.push(html`<li>${i}</li>`);
}
return html`
<ul>
${itemTemplates}
</ul>
`;
}
詳細については、リストを参照するか、リストの操作に関するインタラクティブなチュートリアルをお試しください。
繰り返しテンプレートからのイベントの処理
繰り返し要素にイベントリスナーを追加する方法はいくつかあります。
イベントがバブルする場合は、イベント委任を使用して、親要素に単一のリスナーを追加できます。
イベントがバブルしない場合は、繰り返し要素ごとリスナーを追加できます。
両方の手法を使用した例については、イベントセクションの繰り返しテンプレートから発生するイベントのリスニングを参照してください。
繰り返しテンプレートによって生成された要素からのイベントを処理する場合、多くの場合、イベントを発生させた要素と、その要素を生成したデータの両方を識別する必要があります。
Polymerは、イベントオブジェクトにデータを追加することにより、後者を支援します。
Litはこの追加データを追加しませんが、参照しやすいように、一意の値を各繰り返し要素にアタッチできます。
render() {
return html`
<div @click=${this.handleClick}>
${map(this.items, (item) => {
return html`<button data-id=${item.id}>${item.text}</button>`
}
</div>
`;
}
個々の項目にリスナーを追加する場合は、アロー関数を使用して、イベントハンドラーにデータを直接渡すこともできます。
render() {
return html`
<div>
${this.items.map((item) => {
return html`<button
@click=${() => this._handleClick(item)}>
${item.text}
</button>`;
}
}
</div>
`;
}
プロパティ
「プロパティ」へのパーマリンクLitのリアクティブプロパティは、Polymerの宣言されたプロパティと非常によく一致します。リアクティブプロパティは、プロパティと属性間で値を同期するなど、宣言されたプロパティと同じ機能の多くをサポートしています。
プロパティの構成
「プロパティの構成」へのパーマリンクPolymerと同様に、Litでは静的なproperties
フィールドを使用してプロパティを構成できます。
Litは、リアクティブプロパティを宣言するために@property
デコレーターを使用することもサポートしています。詳細については、デコレーターについてを参照してください。
Polymer
static get properties() {
return {
user: String,
count: {
type: Number,
notify: true
}
}
}
Lit
@property()
user: string;
@property({type: Number})
count: number;
static properties = {
user: {},
count: {
type: Number
}
}
PolymerとLitの両方が、プロパティを宣言するときに多数のオプションをサポートしています。次のリストは、PolymerオプションとそのLitの同等のオプションを示しています。
type
Litの
type
オプションは同じ目的を果たします。value
Litはこのようにプロパティの値を設定することをサポートしていません。代わりに、コンストラクターでデフォルト値を設定します。
@property
デコレーターを使用する場合は、クラスフィールドイニシャライザーを使用することもできます。reflectToAttribute
Litでは、このオプションは
reflect
に短縮されました。readOnly
Litには、読み取り専用のリアクティブプロパティに対する組み込みのサポートは含まれていません。計算されたプロパティをコンポーネントのパブリックAPIの一部にする必要がある場合は、セッターなしでゲッターを追加できます。
notify
この機能は、双方向バインディングをサポートするために使用されます。これは、双方向バインディングの問題で説明されている問題のため、Litには実装されませんでした。
Litコンポーネントは、ネイティブのWeb API(
dispatchEvent
など)を使用して、ユーザー入力に応答したり、内部状態が変更されたときにイベントを発生させることができます。computed
宣言型の計算プロパティはLitではサポートされていません。代替案については、計算プロパティを参照してください。
observer
Litはオブザーバーを直接サポートしていません。代わりに、変更されたプロパティに基づいてアクションを実行できるライフサイクルで、いくつかのオーバーライドポイントを提供します。
静的ゲッター対静的クラスフィールド
「静的ゲッター対静的クラスフィールド」へのパーマリンクPolymerの例では、ゲッター—static get properties()
—を使用し、LitのJavaScriptの例では、クラスフィールド—static properties
—を使用していることに注意してください。これらの2つの形式は同じように機能します。Polymer 3が公開されたとき、クラスフィールドのサポートは普及していなかったため、Polymerのサンプルコードではゲッターを使用しています。
既存のPolymerアプリケーションにLitコンポーネントを追加する場合、ツールチェーンがクラスフィールド形式をサポートしていない場合があります。その場合は、代わりに静的ゲッターを使用できます。
static get properties() {`
return {
user: {},
count: {
type: Number
}
}
}
配列とオブジェクトの型プロパティ
「配列とオブジェクトの型プロパティ」へのパーマリンクPolymerと同様に、Litもプロパティの変更時にダーティチェックを行い、不必要な処理を避けます。プロパティがオブジェクトまたは配列を保持している場合、これが問題につながる可能性があります。オブジェクトまたは配列を直接変更した場合、Litは変更を検出しません。
ほとんどの場合、これらの問題を回避する最善の方法は、不変のデータパターンを使用することです。これにより、既存のオブジェクトや配列を直接変更するのではなく、常に新しいオブジェクトや配列の値を割り当てるようにします。Polymerでも一般的に同じことが当てはまります。
Polymerには、サブプロパティを監視可能に設定するAPIや、配列を直接変更するAPIが含まれていますが、適切に使用するのはやや難しい場合があります。これらのAPIを使用している場合は、不変のデータパターンに移行する必要があるかもしれません。
詳細については、オブジェクトと配列のプロパティの直接変更を参照してください。
読み取り専用プロパティ
「読み取り専用プロパティ」へのパーマリンク読み取り専用プロパティはPolymerではあまり使われていない機能であり、必要に応じて実装するのが難しくないため、Lit APIには含まれていません。
コンポーネントの公開APIの一部として内部状態の値を公開するには、セッターのないゲッターを追加できます。また、状態が変化したときにイベントを発火させることもできます(たとえば、ネットワークからリソースをロードするコンポーネントには、「ローディング」状態があり、その状態が変化したときにイベントを発火させることがあります)。
Polymerの読み取り専用プロパティには、隠されたセッターが含まれています。コンポーネントにとって意味がある場合は、プロパティにプライベートセッターを追加できます。
private _name: string = 'Somebody';
@property({attribute: false})
get name() { return this._name; }
private _setName(name: string) {
this._name = name;
this.requestUpdate('name');
}
static properties = {
name: {attribute: false}
};
constructor() {
super();
this._name = 'Somebody';
}
get name() { return this._name }
_setName(name) {
this._name = name;
this.requestUpdate('name');
}
プロパティがコンポーネントのテンプレートに含まれていない場合、宣言(@property
またはstatic properties
を使用)したり、requestUpdate()
を呼び出したりする必要はありません。
算出プロパティとオブザーバー
「算出プロパティとオブザーバー」へのパーマリンクLitには、リアクティブプロパティが変更されたときに呼び出される、オーバーライド可能なライフサイクルメソッドが多数用意されています。これらのメソッドを使用して、Polymerの算出プロパティまたはオブザーバーに記述していたロジックを実装します。
次のリストは、異なる種類の算出プロパティとオブザーバーを移行する方法をまとめたものです。
算出プロパティ
テンプレート内で一時的に使用される値については、
render()
でローカル変数として値を算出し、テンプレート内で使用してから返します。永続的に保存する必要がある値、または計算にコストがかかる値については、
willUpdate()
で計算します。依存関係が変更された場合にのみ計算するように、
changedProperties.has()
メソッドを使用して、更新ごとにコストのかかる再計算を回避します。
オブザーバー
オブザーバーがプロパティの変更に基づいてDOMに直接作用する必要がある場合は、
updated()
を使用します。これは、render()
コールバックの後に呼び出されます。それ以外の場合は、
willUpdate()
を使用します。いずれの場合も、プロパティが変更されたかどうかを確認するには、
changedProperties.has()
を使用します。
パスベースのオブザーバー
これらは、
foo.bar
やfoo.*
のようなパスを監視する複雑なオブザーバーです。observers: ['fooBarChanged(foo.bar)', 'fooChanged(foo.*)']
この機能は、Litには同等のものがないPolymerのデータシステムに非常に特有のものでした。
深いプロパティの変更をより相互運用可能な方法で伝えるために、不変のパターンを使用することをお勧めします。詳細については、オブジェクトと配列のプロパティの直接変更を参照してください。
render()での一時的な値の計算
「render()での一時的な値の計算」へのパーマリンクレンダリングにのみ使用される一時的な値を計算する必要がある場合は、render()
メソッドで直接計算できます。
Polymer
static get template() {
return html`
<p>Text: <span></span></p>
`;
}
static get properties() {
return {
/* ... */
cyphertext: {
type: String,
computed: 'rot13(cleartext)'
}
}
}
Lit
render() {
const cyphertext = rot13(this.cleartext);
return html`Text: ${cyphertext}`;
}
また、Litではテンプレート内で任意のJavaScript式を使用できるため、Polymerの算出バインディングは、関数呼び出しを含むインライン式で置き換えることができます。
Polymer
static get template() {
return html`
My name is <span>[[_formatName(given, family)]]</span>
`;
}
Lit
render() {
html`
My name is <span>${this._formatName(given, family)}</span>
`;
}
Lit式はプレーンなJavaScriptであるため、インスタンスプロパティまたはメソッドにアクセスするには、式内でthis
を使用する必要があります。
willUpdate()でのプロパティの計算
「willUpdate()でのプロパティの計算」へのパーマリンクwillUpdate()
コールバックは、他のプロパティ値に基づいてプロパティを計算するのに最適な場所です。willUpdate()
は、変更されたプロパティ値のマップを受け取るため、現在の変更を処理できます。
willUpdate(changedProperties: PropertyValues<this>) {
if (changedProperties.has('userId') || changedProperties.has('avatarId')) {
this.imageUrl = this._getImageUrl(this.userId, this.avatarId);
}
}
willUpdate(changedProperties) {
if (changedProperties.has('userId') || changedProperties.has('avatarId')) {
this.imageUrl = this._getImageUrl(this.userId, this.avatarId);
}
}
willUpdate()
を使用すると、変更されたプロパティの完全なセットに基づいて何を行うかを選択できます。これにより、複数のオブザーバーまたは算出プロパティが予測不可能な方法で相互作用する問題を回避できます。
ミックスイン
「ミックスイン」へのパーマリンクミックスインは、Polymerコンポーネントで使用するための再利用可能な機能をパッケージ化するいくつかの方法の1つでした。PolymerミックスインをLitに移植する場合は、いくつかのオプションがあります。
スタンドアロン関数。Polymerのデータバインディングはインスタンスメンバーにしかアクセスできないため、データバインディングで関数を使用できるようにするために、ミックスインを作成することがよくありました。Litでは、テンプレート内で任意のJavaScript式を使用できるため、これは必要ありません。ミックスインを使用する代わりに、別のモジュールから関数をインポートして、テンプレートで直接使用できます。
Litミックスイン。LitミックスインはPolymerミックスインとほぼ同じように機能するため、多くのPolymerミックスインをLitミックスインとして再実装できます。詳細については、ミックスインを参照してください。
リアクティブコントローラー。リアクティブコントローラーは、再利用可能な機能をパッケージ化する別の方法です。ミックスインとリアクティブコントローラーの比較の詳細については、コントローラーとミックスインを参照してください。
ライフサイクル
「ライフサイクル」へのパーマリンクLitコンポーネントには、Polymerコンポーネントと同じ標準のWebコンポーネントライフサイクルコールバックのセットがあります。
さらに、Litコンポーネントには、リアクティブ更新サイクルをカスタマイズするために使用できるコールバックのセットがあります。
Polymer要素でready()
コールバックを使用している場合は、同じ目的でLitのfirstUpdated()
コールバックを使用できる場合があります。firstUpdated()
コールバックは、コンポーネントのDOMが最初にレンダリングされた後に呼び出されます。たとえば、レンダリングされたDOM内の要素にフォーカスを当てたい場合に使用できます。
firstUpdated() {
this.renderRoot.getElementById('my-text-area').focus();
}
詳細については、更新の完了を参照してください。