Speee DEVELOPER BLOG

Speee開発陣による技術情報発信ブログです。 メディア開発・運用、スマートフォンアプリ開発、Webマーケティング、アドテクなどで培った技術ノウハウを発信していきます!

jsx-slack が v2 になりました: 注目の更新点をご紹介します

プロジェクト推進室の服部@リモートワーク 3 ヶ月目 (@yhatt) です。

さて、2019 年 3 月 より継続的にメンテナンスを行なっている Speee のオープンソースライブラリ、 jsx-slack (@speee-js/jsx-slack) の公開から 1 年が経過し、2020 年 4 月に メジャーアップデート jsx-slack v2 のリリースを迎えることができました。🎉

jsx-slack v2
jsx-slack v2 ではロゴも新しくなりました

本記事では、前回の紹介記事 (実践 jsx-slack: jsx-slack + Bolt で Slack のモーダルを自在に操ろう) 以降に追加・変更された機能を中心 に、新しくなった jsx-slack の注目ポイントや変更点などをご紹介いたします。

jsx-slack とは?

jsx-slack は、Slack Block Kit を使ったメッセージやモーダルを、 JSX で記述できるようにするための JavaScript ライブラリです。

定義が冗長になりやすい JSON に変わって、JSX を使うと、React で HTML を定義するのと同じように、自然な記述で Block Kit によるリッチなメッセージ・モーダルを定義できます。


また、React と同様に、関数を使って独自のコンポーネントを定義できる ので、既存のブロックを組み合わせたコンポーネントを作って Slack アプリ内で使うこともできます。

より詳しい使い方については、 GitHub のリファレンス や、実際の JSX 変換をプレビューできる REPL デモ を参照してください。

過去の紹介記事

過去 2 回に渡り、jsx-slack に関する紹介記事を執筆しておりますので、順にご覧いただくとより理解が深まると思います。

🔰 基本機能について

tech.speee.jp

💪 モーダル機能の紹介&Bolt フレームワークによる実践編

tech.speee.jp

Block Kit を知りたい方は… 🧱

この記事では Block Kit の基本説明を省きますが、日本語資料は豊富ですので、Block Kit がよくわからないという方は、以下が参考になるかもしれません。

注目の更新

▶️ 公式の jsx-slack v2 ハイライトを見る… (英語)

最新の Slack API への対応

App Home (ホームタブ)

今年 1 月に、Slack アプリ で "App Home" (ホームタブ) が正式に利用できるようになりました。これは、Slack アプリが各ユーザー毎に固有の画面を提供できるようになる機能で、アプリのダッシュボードなど、よりリッチな UI/UX の画面を Slack ユーザーに提供できます。

今回新しくリリースされた機能、App Home は、ユーザと Slack を1対1で繋ぐことができるスペースで、さらにユーザに直感的にアプリを使ってもらうために加えられた機能なのです。

— 新機能、アプリのホーム・ヴューを活用しよう🏡 | Slack
(https://api.slack.com/lang/ja-jp/app-home-with-modal)

ホームタブに表示する画面は、従前より提供されている Block Kit の JSON で定義します。jsx-slack を使えば、JSX を使って React 感覚で JSON を構築できます。

画面を構成するブロックは <Home> コンテナコンポーネントの中で定義します。もうすでに jsx-slack を使ったことがあるなら、使い方は基本的に メッセージモーダル と一緒なので、カンタンです。

<Home>
  <Section>*Welcome back!* It's your home! 🏠</Section>
  <Divider />
  <Image src="https://source.unsplash.com/256x256/?home" alt="home" />
</Home>
// HTML 風に書くこともできます
<Home>
  <section>
    <p><strong>Welcome back!</strong> It's your home! 🏠</p>
  </section>
  <hr />
  <img src="https://source.unsplash.com/256x256/?home" alt="home" />
</Home>

▶️ ホームタブの例を見る… (REPL デモ)

シンタックスは HTML との互換性も重視しているので、Web の知識があれば、 どのような画面が表示されるか JSX の定義から推測しやすい のが jsx-slack を使う最大のメリットです。

jsx-slack の REPL デモ で、JSON への変換結果を確認できます。JSX の内容を編集したり、右下の "Preview in Slack Block Kit Builder" から、JSON を Block Kit Builder に転送し、Slack 上での実際の見た目を確認することもできます。

地味ながら、デモも第2の Block Kit Builder としての利用に耐え得るよう改良されました
地味ながら、デモも第2の Block Kit Builder としての利用に耐え得るよう改良されました

チェックボックス&ラジオボタン

Block Kit では、ボタン・セレクトボックス・日付選択などのインタラクティブなコンポーネントが提供されています。昨今の更新により、ホームタブとモーダル限定ですが、新たに チェックボックス&ラジオボタン が使えるようになりました。

<Home>
  <section>
    <b>TODO リスト</b>
    <CheckboxGroup>
      <Checkbox value="todo-1">📝 MTG 資料作成</Checkbox>
      <Checkbox value="todo-2">
        👥 メンバーの進捗確認
        <small>毎週金曜13時までに確認→上長へ報告</small>
      </Checkbox>
      <Checkbox value="todo-3" checked>
        <s>👕 洗剤が切れたので買う</s>
      </Checkbox>
    </CheckboxGroup>
  </section>
</Home>

▶️ チェックボックスの例を見る… (REPL デモ)

<Modal title="アプリの設定" close="キャンセル">
  <RadioButtonGroup label="予定の通知" required>
    <RadioButton value="off" checked>
      通知を送信しない
    </RadioButton>
    <RadioButton value="summary">
      <b>まとめて通知する</b>
      <small>
        当日入っている予定を、 <b>午前 9:00</b> にまとめて通知します。
      </small>
    </RadioButton>
    <RadioButton value="on">
      <b>各予定の直前に通知する</b>
      <small> それぞれの予定の <b>開始 5 分前</b> に通知されます。 </small>
    </RadioButton>
  </RadioButtonGroup>
  <input type="submit" value="設定を反映" />
</Modal>

▶️ ラジオボタンの例を見る… (REPL デモ)

チェックボックスもラジオボタンもよく使われるコンポーネントなので、Slack アプリにおいては様々な場面で活用できることでしょう。

Block Kit 仕様への継続的な追従

Slack API の Changelog ページ を見るとわかりますが、Slack API の更新は結構激しく、毎月のように機能の追加・変更があります。

API で使えるようになったはずの機能にサードパーティライブラリの実装が追いついておらず、フラストレーションを感じたことのある開発者もいることでしょう。

そのような事態を極力避けるべく、jsx-slack の開発にあたっては RSS で Slack API の更新を監視 1 し、Block Kit 関連の新機能には素早く (1〜3 営業日を目標に) 追従するよう心がけています。以下はその一例です。

  • <RadioButton>
    • HTML による書式設定ができるよう改善 (v1.4.0)
  • <ConversationsSelect>
    • response_url_enabled フィールド (responseUrlEnabled: v1.6.0)
    • filter 構成オブジェクト (v1.6.0)
      • jsx-slack では専用の個別プロパティとして実装
    • default_to_current_conversation フィールド (v2.1.0)
      • jsx-slack では特殊チャンネル current として実装
  • <Confirm>
    • 確認ダイアログのボタンの色を変える style フィールド (v1.7.0)

これらは、いずれも Slack による実装から 1〜2 営業日でリリースされました。さらに、jsx-slack ではより直感的に使えるような改良を加えています。jsx-slack は、常に最新の Block Kit の機能を 「心地よく」 扱うことができるよう心がけています。💝

jsx-slack の各種改善点

基本的な使い方は v1 も v2 も全く同じなのですが、v2 では よりシンプルなインターフェースを実現する ため、ほぼ全てのコードを書き直しました。✏️

JSX から直接 Slack 向けの JSON を出力

かつての jsx-slack では、JSX が出力するのは Slack 用の JSON ではなく、 jsx-slack の中間表現 で、 JSXSlack() 関数を使って Slack 用の JSON に変換する必要がありました。2

const jsx = (
  <Blocks>
    <Section>Hello, world!</Section>
  </Blocks>
);

// jsx-slack v1 では JSX の中間表現を返す
console.log(jsx);
// {
//    "type": Blocks,
//    "props": null,
//    "children": [
//      {
//        "type": Section,
//        "props": null,
//        "children": ["Hello, world!"]
//      }
//    ]
// }

// JSXSlack() を使って Slack 用の JSON にレンダリング
console.log(JSXSlack(jsx));
// [
//   {
//     "type": "section",
//     "text": {
//       "type": "mrkdwn",
//       "text": "Hello, world!",
//       "verbatim": true
//     },
//   }
// ]

jsx-slack v2 ではインターフェースがシンプルになり、JSX が直接 Slack 用の JSON を生成する ようになりました。JSXSlack() を使わずとも、JSX をそのまま Slack の SDK に渡すことができます。 3

// jsx-slack v2 は JSX から直接 JSON が生成される
console.log(
  <Blocks>
    <Section>Hello, world!</Section>
  </Blocks>
);
// [
//   {
//     "type": "section",
//     "text": {
//       "type": "mrkdwn",
//       "text": "Hello, world!",
//       "verbatim": true
//     },
//   }
// ]

v2 で全面的に書き直された jsx-slack ビルトインのコンポーネントは、原則的に Slack 用 JSON の一部分を返すようになりました。そのため、Slack アプリの JSON 定義全てを jsx-slack で置き換えずとも、以下のように部分的に jsx-slack を使うこともできます。

api.chat.postMessage({
  channel: "C0123456789",
  blocks: [
    {
      type: "section",
      text: (
        <Mrkdwn>
          <blockquote>
            <b>Hello</b>, <i>world</i>!
          </blockquote>
        </Mrkdwn>
      ),
    },
  ],
});

少しだけ内部の話をすると、JSX で生成された JSON には、実は v1 と同じように中間表現が隠されています(Object.defineProperty() で定義)。v2 で提供するコンポーネントは、引き続きこの中間表現に基づいて JSON を構築していますが、非列挙プロパティにしたことで、普通に jsx-slack を使っている限りは中間表現を意識する必要が無くなりました。

React 互換 API の追加

jsx-slack は、使い勝手が React「風」なだけで、React を使っている訳ではない ものの、カスタムコンポーネントを定義して使う時の感覚は React とほぼ同じです。

v2 では、カスタムコンポーネント内でより React に近い感覚で子要素を操作できるよう、JSXSlack.isValidElement()JSXSlack.Children ユーティリティなど、React と互換性のある API をいくつか追加しました。

たとえば、JSXSlack.Children.toArray()React.Children.toArray() 互換 API)を使うと、渡された子要素をノーマライズ(<JSXSlack.Fragment> の展開などを含む)して、一次元配列で返してくれます。コンポーネントの中で子要素を操作・変換する際に便利です。

const CustomComponent = ({ children }) =>
  JSXSlack.Children.toArray(children).join("");

console.log(
  <CustomComponent>
    a{"b"}c<JSXSlack.Fragment>{["d", "e"]}f</JSXSlack.Fragment>
  </CustomComponent>
);
// -> abcdef

v2 の組み込みコンポーネントでも、この API を使用して、子要素の各種処理・変換を行っています。

JSDoc および内部エラーの改善

Block Kit が多くの場面で利用できるようになったことで、初回リリース時に比べて機能も増えてきています。しかし、ドキュメントは GitHub に集約 されていたので、コードと jsx-slack のドキュメントを行ったり来たりしながらコーディングする必要がありました。多くの場合、これに Slack API のドキュメント も加わるので、なおさら目が忙しくなります。

そこで v2 では、ほぼ全てのコンポーネント・API に JSDoc を定義しました。これにより、コーディング中でも簡単なヘルプやサンプルを IDE 上で確認できるようになりました。

また、間違った JSX を書いた時に出力されるエラーがより親切になり、エラーが発生した組み込みコンポーネントの名前・原因・解決策などが可能な限り表示されるようになりました。

モーダル上の選択項目に必要な "label" 属性が定義されていないことを指摘するエラー
☝️ モーダル上の選択項目で必要な label 属性が定義されていないことを指摘するエラー

Babel の development モードで JSX を変換している場合は、『どの JSX 要素でエラーが発生したか』という位置情報もエラーに含まれるようになったので、デバッグも容易です。

ライブラリの軽量化

以前の jsx-slack は、ライブラリの読み込みや変換処理のパフォーマンスがあまり良くありませんでした。4 そのため、v2 のリリースに至るまで、さまざまなアプローチで軽量化やパフォーマンス改善を試みています。

HTML パーサーの刷新 (v1.3.0〜)

一番のボトルネックは、JSX で定義された HTML 要素を Slack mrkdwn の記法に変換するためのベースエンジンとして使用していた turndown でした。

turndown は、Node.js 環境では HTML のパースに jsdom を使用するのですが、本来不要な Web 標準の実装が全て含まれてしまうため、かなりの重装備だったのです。そこで、v1.3.0 で HTML → Slack mrkdwn の変換処理を unified エコシステムに基づいたものに全面的に書き直しました、

結果、minify されたバンドルサイズは、"2.7MB → 41.4KB" と、驚異の 67 分の 1 になりました。jsx-slack の読込時間の改善はもちろん、変換処理のパフォーマンスも、turndown ベースのエンジンと比べると、約 10 倍高速化 しています。

v1 時代は、『レガシーパーサー』として turndown 実装を引き続き搭載していて、戻すこともできましたが、社内アプリ等での検証で、新しい変換処理の安定動作が確認できたため、v2 では完全に unified ベースのパーサーに切り替えられました。

ビルドの最適化 (v1.2.0〜)

webpack などの各種バンドラーでアプリをバンドルした際に、不必要な依存関係のバンドルを避けるため、 Tree shaking に対応したビルドの生成も実施しています。

その他

他にも、<Select> の初期選択項目を HTML と同じ方法で設定するための <Option selected> や、Babel 8 でデフォルトになる予定の新しい JSX 変換処理への実験的な対応 なども v2 に含まれています。

v1 以前のバージョンから移行する場合、一般的な使い方をしている限りは、ほとんどのケースでそのままアップデートできるはずです。 <Checkbox checked> や TypeScript を使っている場合は、一部破壊的変更があるので、公式ドキュメントの "Breaking Change" を参照の上、アプリの挙動をご確認ください。

おわりに

最近は 類似機能を持つ新進気鋭のライブラリも続々出てきています が、jsx-slack は公開から 1 年が経過し、ある種『枯れた』ライブラリとなったおかげで、社内でのSlack アプリ開発では、 jsx-slack + Slack 公式フレームワーク・Bolt の組み合わせが鉄板化しています。

また、jsx-slack を使ったアプリの例 も国内外問わず増えており、ありがたいことにSlack App ディレクトリ で一般公開しているアプリで使っている』という声もいただいています。 🤗

jsx-slack は今後も、最新仕様への追従など、必要なメンテナンスは引き続き実施していく予定ですが、基本的に jsx-slack はオープンソース (MIT License) ですので、開発への参加はどなたでも可能です。 GitHub リポジトリにて Issue によるフィードバックや、Pull Request をお待ちしています。

jsx-slack は、リッチな UX に必要な Block Kit の表現力を、簡潔な定義で 100% 引き出せるように設計していますので、優れた UX を持つ Slack アプリ開発のお役に立てれば幸いです。


  1. 通知させたい Slack チャンネルで /feed subscribe https://api.slack.com/changelog.rss コマンドを使うと、Changelog が更新されるたびに通知してくれるので便利です。

  2. jsx-slack の中間表現 ≒ React の仮想 DOM、 jsx-slack v1 の JSXSlack()ReactDOM.render()。v2 はこの垣根を崩した形になります。

  3. JavaScript の場合。TypeScript では型が一致しなくなるので、適切な型や any にキャストするか、従来と同様に JSXSlack() を使う必要があります。

  4. Slack とのやりとりは「リクエストから 3 秒以内で応答」というルールの API が多いのですが、FaaS 環境などで Slack アプリをホスティングしている場合、コールドブートで jsx-slack の読み込みに時間がかかってしまうと、タイムアウトすることもまちまちでした。