Lit(Web Components)でReactで使えるUIパッケージを作る
お届けするもの
web componentsの基礎
Litの簡単な説明
Litで生成したコンポーネントのパッケージ化
Reactから利用する
Web Componentsとは
いくつかの Web APIのことで、新しくHTMLタグ(カスタムコンポーネント)の定義とその再利用、カプセル化ができるようになります。Web Componentsベースで作られたカスタムコンポーネントは、JSのライブラリやフレームワークに関係なく使用可能です。
4つの主な機能
カスタムエレメントの定義
ES Modulesでの読み込み
code:index.js
class WordCount extends HTMLElement {
constructor() {
super();
// this.attachShadow({ mode: 'open' })
}
connectedCallback() {
console.log('connected a custom element')
this.innerHTML = ${this.innerHTML} : ${this.innerHTML.length};
// this.shadowRoot.innerHTML = ${this.innerHTML} : ${this.innerHTML.length};
}
}
window.customElements.define('word-count', WordCount);
code:index.html
<!DOCTYPE html>
<html>
<head>
<title>web components</title>
<meta charset="utf-8" />
</head>
<body>
<script type="module" src="./index.js"></script>
<word-count>text</word-count>
</body>
</html>
Litとは
Web ComponentsベースでUI構築をするためのライブラリのひとつです。ReactやVueのように、コンポーネントが持つ値の状態管理や、宣言的UIの機能を提供してくれます。
公式でライブラリサイズが5KBだと言っていてとても軽量。仮想DOMを使わない。
Litが生成するコンポーネントはWeb Componentsとして素のHTMLや他ライブラリでも使用できます。
code:lit.ts
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
@customElement("simple-greeting")
export class SimpleGreeting extends LitElement {
static styles = css`
:host {
color: blue;
}
`;
@property()
name?: string = "World";
render() {
return html<p>Hello, ${this.name}!</p>;
}
}
html css はテンプレート構文の機能で、reactだと JSX style、vueだと template styles のようなところ
LitElement はLit コンポーネントを定義するためのベースクラス
customElement でDOM(というかブラウザ?)にカスタムコンポーネントを追加する
Litをビルドしていく
@open-wc というプロジェクトでLitを使ったプロジェクトのテンプレート生成ができます。僕は今回これを使っていました。
対話形式で Lint / Test / Storybook などプロジェクト設定を聞かれて下のようなものができます。
code:dir.txt
/
├── project-dir/
│ ├── .storybook/
│ │ ├── main.js
│ │ └── server.mjs
│ ├── demo/
│ │ └── index.html
│ ├── src/
│ │ └── WebComponent.ts <<< --- 生成時に指定した名前でファイルができる
│ ├── stories/
│ │ └── index.stories.ts
│ ├── test/
│ │ └── web-component.test.ts
│ ├── .editorconfig
│ ├── .gitignore
│ ├── custom-elements.json
│ ├── index.ts
│ ├── LICENSE
│ ├── package.json
│ ├── README.md
│ ├── tsconfig.json
│ ├── web-component.ts
│ ├── web-dev-server.config.mjs
│ └── web-test-runner.config.mjs
生成された WebComponent.ts に少し追加する
code:src/WebComponent.ts
import { html, css, LitElement, property } from 'lit-element';
import { customElement } from 'lit-element/lib/decorators'; // 追加
@customElement('web-component') // 追加
export class WebComponent extends LitElement {
static styles = css`
:host {
display: block;
padding: 25px;
color: var(--web-component-text-color, #000); }
`;
@property({ type: String }) title = 'Hey there';
@property({ type: Number }) counter = 5;
__increment() {
this.counter += 1;
}
render() {
return html`
<h2>${this.title} Nr. ${this.counter}!</h2>
<button @click=${this.__increment}>increment</button>
`;
}
}
code:sh
yarn build
# -> /dist/src にReactから呼び出して使いたいコンポーネントファイルが生成される
GitHub Packages (npm)に公開する
GitHubにpushする
code:sh
gh repo create web-component
git push -u origin main
GitHubのpersonal access tokenを発行して、npmrcに追加する
code:~/.npmrc
//npm.pkg.github.com/:_authToken=${github_personal_access_token}
init.author.email=${email}
init.author.name=${github_username}
init.license=MIT
code:package.json
{
...
"name": "@ihch/web-component",
"author": "ihch",
"repository": "git://github.com/ihch/web-component.git",
"publishConfig": {
},
"scripts": {
...
"build": "...",
"prepub": "cp package.json ./dist/src && cp .npmrc ./dist/src",
"pub": "cd ./dist/src/ && npm publish"
},
...
}
code:sh
yarn pub
# -> GitHubのリポジトリのページにnpm packagesが追加されているはず
https://gyazo.com/ee338504226fa6720dabb18e4675a9be
Reactのプロジェクトで使う
code:sh
yarn add @ihch/web-component
React + TypeScript で使う用の型定義の対応ファイル
code:sh
yarn add react @types/react @lit-labs/react
code:WebComponent.react.ts
import React from 'react';
import { createComponent } from '@lit-labs/react';
import { WebComponent } from './WebComponent';
export const WebComponentReact = createComponent(React, 'web-component', WebComponent)
code:App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { WebComponentReact } from '@ihch/web-component/WebComponent.react';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
</header>
<main className="main">
<p style={{ margin: 0 }}>Lit Element</p>
<WebComponentReact />
</main>
</div>
);
}
export default App;
まとめ
Web Componentsによってカスタムエレメントの定義(コンポーネント化)ができるようになった
Litを使うことで他のライブラリでも利用可能で、リアクティブな状態管理ができる宣言的なコンポーネント定義ができる
現時点でReactやVueほど開発者体験は良くないかもしれないが、Web標準に近くて再利用性高くっていう思想がいい
(React, Vueが隠蔽してくれている複雑さ click handlerとかをaddEventListenerしないといけない)
SSR対応 未リリース
ReactiveController で状態管理・ライフサイクルに関わるロジックをコンポーネントから切り分け可能になる
@lit-labs/react/useController で上の ReactiveController のライフサイクルに Reactからアクセスできるようになるhooks