2021/04/22に新しいWeb Componentsライブラリ、Lit
(Lit 2.0)がリリースされ、同日ローンチイベントもYouTubeで生配信されました。
https://twitter.com/buildWithLit/status/1384929943386214401?s=20
https://www.youtube.com/watch?v=f1j7b696L-E
それに伴いPolymer Project
がLit
に改名されロゴが刷新されました。
https://twitter.com/buildWithLit/status/1384572664002478093?s=20
ウェブサイトも新しく公開されました。チュートリアルとPlaygroundが刷新され、非常に便利になりました。
https://lit.dev/
実装はhttps://github.com/lit/lit に公開されています。
npm i lit
旧Polymer Project(Lit 1.0)では、lit-html
(HTMLテンプレートライブラリ)とLitElement
(Web Componentsを実装するためのライブラリ)の2つのライブラリが提供されていました。LitElement
にもテンプレート機能があったので、2つとも独立したライブラリとして提供されていました。
Lit
はlit-html
やLitElement
などのWeb Componentsを開発するのに必要なライブラリを1つにまとめたものです。
コードも1つのリポジトリにまとめられmonorepoになっています。
https://github.com/lit/lit
Simple. Fast. Web Components. というフレーズからわかるようにシンプルかつスタンダードなWeb Componentsをより簡単に開発することにフォーカスしているのが特徴です。
詳しい機能についてはドキュメントを読んでください。
APIの再設計や不要な機能の削除、polyfillの分離などを行った結果、新機能を追加したにもかかわらず軽量化とパフォーマンス向上に成功しました。ローンチイベントで発表された具体的な数値は次のとおりです。
Lit
はDeclarative Shadow DOM
という仕様を利用してShadow DOMのSSRに対応しました。(おそらくWeb Components系ライブラリでは初。)
SSR対応のために内部実装の再設計やlit-html
に実装されていたマイナーな低レベルAPIを削除したとイベントの中で言っていました。
SSR対応はまだ正式にはリリースされていませんが、実装はすでにありGitHubでコードが公開されています。(後述のlit-labs/ssr
を参照。)
Lit
の基本的な書き方は次のとおりです。
import{LitElement, css, html}from'lit';import{customElement, property}from'lit/decorators.js';@customElement('simple-greeting')exportclassSimpleGreetingextendsLitElement{// Define scoped styles right with your component, in plain CSSstatic styles= css`:host{color:blue;}`;// Declare reactive properties@property() name?:string='World';// Render the UI as a function of component staterender(){return html`<p>Hello,${this.name}!</p>`;}}
ドキュメントのサンプルコードはほぼ全てデコレーターを使って書かれていますが、デコレーターを使わずに書くことも可能です。
import{LitElement, css, html}from'lit';exportclassSimpleGreetingextendsLitElement{ name?:string;static styles= css`:host{color:blue;}`;staticgetproperties(){return{ name:{}}}constructor(){super();this.name='World';}render(){return html`<p>Hello,${this.name}!</p>`;}}customElements.define('simple-greeting', SimpleGreeting);
Lit 2.0で新しく追加された機能の中から注目度の高い3つの機能を紹介します。
ReactiveController
は今回の目玉機能の1つです。
https://lit.dev/docs/composition/controllers/
ReactiveController
は、コンポーネントのライフサイクルにアクセスできるオブジェクトです。この仕組を使うことでロジックや状態管理をコンポーネントの外側にまとめて定義でき、複数のコンポーネントで再利用できるようになります。
以下のサンプルコードでは時計を表示するコンポーネントをコントローラーを利用して構築しています。
import{ReactiveController, ReactiveControllerHost}from'lit';exportclassClockControllerimplementsReactiveController{ host: ReactiveControllerHost; value=newDate(); timeout:number;private _timerID?:number;constructor(host: ReactiveControllerHost, timeout=1000){(this.host= host).addController(this);this.timeout= timeout;}hostConnected(){// Start a timer when the host is connectedthis._timerID=setInterval(()=>{this.value=newDate();// Update the host with new valuethis.host.requestUpdate();});}hostDisconnected(){// Clear the timer when the host is disconnectedclearInterval(this._timerID);this._timerID=undefined;}}
import{LitElement, html}from'lit';import{customElement}from'lit/decorators.js';import{ClockController}from'./clock-controller.js';@customElement('my-element')classMyElementextendsLitElement{// Create the controller and store itprivate clock=newClockController(this,100);// Use the controller in render()render(){const formattedTime= timeFormat.format(this.clock.value);return html`Current time:${formattedTime}`;}}const timeFormat=newIntl.DateTimeFormat('en-US',{ hour:'numeric', minute:'numeric', second:'numeric',});
ReactiveController
の要はこのReactiveControllerHost
APIにあります。
ReactiveControllerHost
はコントローラーのコンポーネントへの追加や更新のリクエストを行い、Lit
のライフサイクルメソッドを呼び出す役割を持つオブジェクトです。
ReactiveControllerHost
はLitElement
である必要はなく、公開されているinterfaceに則ったオブジェクトであれば何でもOKです。
interfaceReactiveControllerHost{addController(controller: ReactiveController):void;removeController(controller: ReactiveController):void;requestUpdate():void;readonly updateComplete:Promise<boolean>;}
そのため、このReactiveControllerHost
の役割を持ったオブジェクトを実装すれば他のフレームワーク中でもコントローラーを利用できるようになります。
後述するlit-labs/react
はReactとコントローラーを接続するための機能を提供するライブラリで、内部でReactiveControllerHost
が利用されています。現在、他のフレームワーク向けの実装も進められているみたいです。
https://github.com/lit/lit/issues/1682
ref
はLit
のBuilt-in directives
の1つで、DOM要素への参照を取得します。
https://lit.dev/docs/templates/directives/#ref
下のサンプルコードではinput
要素への参照を取得しフォーカスさせています。
@customElement('my-element')classMyElementextendsLitElement{ inputRef: Ref<HTMLInputElement>=createRef();render(){// Passing ref directive a Ref object that will hold the element in .valuereturn html`<input${ref(this.inputRef)}>`;}firstUpdated(){const input=this.inputRef.value; input.focus();}}
ディレクティブとは、テンプレートのレンダリングをカスタマイズできる関数です。
様々なディレクティブがLit
に組み込まれている他、カスタムディレクティブを作ることもできます。
https://lit.dev/docs/templates/custom-directives/
import{Directive, directive}from'lit/directive.js';// Define directiveclassHelloDirectiveextendsDirective{render(){return`Hello!`;}}// Create the directive functionconst hello=directive(HelloDirective);// Use directiveconst template= html`<div>${hello()}</div>`;
これはInternal reactive state
を定義するためのデコレーターです。@state
を使って定義されたプロパティはコンポーネントの外部から参照されず、コンポーネントのアトリビュートとして利用することはできません。
exportclassMyElementextendsLitElement{// Private. Doesn't have an attribute.@state()protected _active=false;// Public. May have an assosiated attribute.@property() name:string;}
また、デコレーターを使わなくてもInternal reactive state
を定義できます。
staticgetproperties(){return{ _active:{ state:true}}}constructor(){this._active=false;}
Lit
はLitElement
やlit-html
で書かれたほとんどのコードが動作するように設計されています。
APIのリネームやマイナーなBreaking Changeはありますが、大抵の場合はライブラリを置き換えるだけで問題ないでしょう。
1つ注意が必要な点は、IE11対応のためのPolyfillが別パッケージ(lit/polyfill-support.js
)として切り離されたことでしょう。LitElement
では本体に組み込まれていましたが、Lit
ではPolyfillを別途インポートする必要があります。(公式ドキュメントのサンプルコードではplatform-support.jsとなっていますがpolyfill-support.jsが正しいです。)
<scriptsrc="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"><scriptsrc="node_modules/lit/polyfill-support.js">
より詳しい変更点についてはアップグレードガイドを参照してください。
https://lit.dev/docs/releases/upgrade/
今回のリリースの重要なポイントの1つとしてこのlit-labs
があります。
https://github.com/lit/lit/tree/main/packages/labs
lit-labs
はLit
の実験的なアイデアや機能を試す場所で、コミュニティにそれらを公開しフィードバックをもらうことで強力なエコシステムを構築するとともにコアの機能をより良くしていくことを目的にしているとローンチイベントで紹介されていました。
現在リポジトリにあるパッケージの中から注目度の高いものを2つ紹介します。
これはReact
のコンポーネントとLit
を接続するためのパッケージで、createComponent
とuseController
の2つの関数が用意されています。
実装はhttps://github.com/lit/lit/tree/main/packages/labs/react
ReactでWeb Componentsを利用する際、Custom ElementsのプロパティとReactのpropsの接続が難しいという問題がありましたが、Custom ElementsをcreateComponent
でラップすることでpropsやイベントを簡単に接続できます。
createComponent
を使うときは、第1引数はReactモジュール、第2引数にCustom Elementsのタグ名、第3引数にCustom Elementsのクラス名(customElements.define
で使用しているもの)を渡します。
第4引数はこのコンポーネントが受け取ることのできるイベントをリストアップしたオブジェクトで、オブジェクトのキーはReactのpropsで渡されるイベントプロパティ名、オブジェクトの値はそれに対応するCustom Elementsで生成されるイベントの名前です。
下記の例では、MyElementComponent
のonactivate
を介してイベント関数が渡され、Custom Elementsのactivate
イベントが発生したときにその関数が呼び出されます。
import*as Reactfrom'react';import{createComponent}from'@lit-labs/react';import{MyElement}from'./my-element.ts';exportconst MyElementComponent=createComponent( React,'my-element', MyElement,{ onactivate:'activate', onchange:'change',});
定義したコンポーネントは他のReactコンポーネントと同じように利用できます。
<MyElementComponentactive={isActive}onactivate={(e)=>(isActive= e.active)}/>
useController
はLit
のReactive Controller
をReactコンポーネント中で利用できるようにするためのReactフックです。
詳しいことはリポジトリのREADMEを読んでください。:pray:
import*as Reactfrom'react';import{useController}from'@lit-labs/react/use-controller.js';import{MouseController}from'@example/mouse-controller';// Write a React hook function:constuseMouse=()=>{// Use useController to create and store a controller instance:const controller=useController(React,(host)=>newMouseController(host));// return the controller: return controller;// or return a custom object for a more React-idiomatic API:return controller.position;};// Now use the new hook in a React component:constComponent=(props)=>{const mousePosition=useMouse();return(<pre> x:{mousePosition.x} y:{mousePosition.y}</pre>);};
これはLit
のテンプレートやコンポーネントをSSRするためのパッケージです。
Next.jsやNuxt.jsなどを中心にSSRが普及した現在、Web ComponentsをSSRしたいという需要も高まっており、それに応えるかたちになったと言えます。
https://github.com/lit/lit/tree/main/packages/labs/ssr
まだプレリリースの段階だとREADMEには記載されていますが、ローンチイベントではEleventy
のプラグインとして利用しマークダウン内に埋め込まれたCustom ElementsをレンダリングするデモやKoa
のミドルウェアで利用するデモが披露されていました。
Lit
のSSRはDeclarative Shadow DOM
という仕様を使って実現されています。この仕様は2021年4月現在Chromeでのみ実装されており、他のブラウザではPolyfillを使ってSSRを実現するようです。Declarative Shadow DOM
についてはこちらの記事で詳しく解説されています。
https://web.dev/declarative-shadow-dom/
Litの登場でますますWeb Componentsの利用が広まっていく予感がします。
まずはPlaygroundでいろいろ触ってみましょう!
https://lit.dev/playground/
バッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。