BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース REPLicant、超シンプルなSvelte REPL - Peter Allen氏のSvelte Summit 2020での講演より

REPLicant、超シンプルなSvelte REPL - Peter Allen氏のSvelte Summit 2020での講演より

原文(投稿日:2020/10/24)へのリンク

Peter Allen氏は先日のSvelte Summit 2020で講演し、REPL(Read-Print-Eval-Loop)プレイグラウンドのメリットについて説明した。Svelte REPLは多くの境界条件を扱う必要があるために複雑だが、コードプレイグラウンド実装の根底にある原理は単純だ。Allen氏は講演の視聴者に対して、段階的に、Svelte REPLの最も単純な実装バージョンに至るまで説明した。

Allen氏が最初に強調したのは、ターゲットユーザにテクノロジを届ける上で、オンラインのプレイグラウンドが果たす役割の重要性だ。革新的なテクノロジをマーケティングファネル(marketing funnel)で言うところの認知段階(awareness stage)から評価段階(evaluation stage)へと進めるためには、このことが特に大きな意味を持ち得る。Allen氏はその例として、MDsveXに対する関心の継続においてMDsveXプレイグラウンドの果たした役割を挙げた。

18か月ほど前に私は、[Markdownドキュメント内でSvelteコンポーネントを使用可能にする]ライブラリ[MDsveX(MDX for Svelte)]を公開したのですが、正直言って反響はまったくありませんでした。[…] そんなある日、友人のひとりが、MarkdownとSvelteを組み合わせて文書を書きたい、という話を私に持ち掛けてきたのです。MDsveXが最適だと思ったのですが、友人らはそれが何なのかも知りませんでした。READMEも、私の説明も、あまり役に立っていないようだったのです。[…] そこで、何日かの夜を使って実例サイトを立ち上げて、一緒にユーザが自身で試すことのできるオンラインプレイグラウンドを用意しました。リンクをSvelte Discordにポストして、それがTwitterで広まると、急に関心が集まるようになったのです。

Allen氏は、自身がいくつかのプログラミング言語を試す上で、インタラクティブなオンラインプレイグラウンドを試した経験について言及した。プログラミング言語RubyのブラウザベースのチュートリアルであるTryRubyの他、RustTypeScriptGoなどもプレイグラウンドを持っている。Svelteドキュメントサイトはそれ自体、Svelte REPLを多用して作成されている。

続いてAllen氏は、講演の背景となった理論的な根拠について説明した。

Svelte REPLはかなり前からありました。その複雑さゆえに、十分に理解されているとは言えませんが、複雑さのほとんどはエッジケースの処理や付加的な機能によるもので、Svelte REPLのコアは比較的シンプルです。いくつかのコンセプトを知っていれば十分なのです。今日はそれを紹介したいと思います。ここでは可能な限りシンプルなバージョンのSvelte REPLを作成して、その動作を内側から確認します。

単純化したREPLの画面サンプルを次に示す。

単純化したSvelte REPLのスクリーンショット

ユーザが<App>コンポーネントを書き、そのコードをApp.svelteエントリファイルに保存する。そのコンポーネントから、近隣のタブにソースコードのある他のコンポーネントをインポートすることができる。新たなタブを作成して(+ボタン)、Svelteコンポーネントを追加することも可能だ。

そのためプレイグラウンドは、ソースファイルがタブ領域に、ソースコードがテキスト領域に、それぞれ表示されているコンポーネントの配列を処理する必要がある。従って最もシンプルなREPLは、コンポーネントの配列、各コンポーネントのソースコード(<textarea>のコンテンツなど)、現在選択されているコンポーネントによって構成されることになる。画面の左側に表示されているマークアップは、REPLの状態を反映したものだ。

Appコンポーネントマークアップは次のような構成である。

<main>
  <Input ... />
  <Output ... />
</main>

Inputコンポーネントはタブ領域とテキスト領域を表示する。Outputコンポーネントはコンパイルされ、バインドされたAppコンポーネントを表示する。

コンポーネントのソースコードを保持するテキスト領域は、Svelteの双方向データバインディングメカニズムを使用して<App>コンポーネントのローカル状態にバインドされている。タブ領域と+ボタンの実装についての説明は、講演では割愛されていた。Inputコンポーネントを実装したスクリーンショットは次のようになる。

単純化したSvelte REPL — Inputコンポーネントのスクリーンショット

Allen氏は次に、ユーザが修正を行う都度にAppコンポーネントのコンパイルと描画を行うOutputコンポーネントに話題を移した。

Allen氏はここで、コンパイルプロセス(コンシューマ)がユーザのキー入力(プロデューサ)に比較して非常に遅いことから生じる、バックプレッシャに関する問題について説明した。これは、ユーザ入力とリアルタイムコンパイル(compilation-as-you-type)の2つを同じスレッドで処理することによって、メインスレッドがブロックされ、ユーザエクスペリエンスのクオリティが低下する、という問題である。

これを解決するためにAllen氏は、コンパイル処理をワーカスレッドにデレゲーションすることにした。ワーカスレッドとメインスレッド(UI)はメッセージを介してコミュニケーションする。コンポーネントが更新された場合に、コンポーネントのソースコードを含んだメッセージをワーカへ送るためのトリガには、Svelteのリアクティブ性を使用した。

これが終わると、話題は次の問題であるコンパイルに移った。ソースコード配列のコンパイルは、SvelteコードをJavaScriptに変換して、生成されたファイルをバンドルする作業である。Allen氏はRollupバンドラのJavaScript APIを使用して、Rollupプラグインの作成方法について説明した。

Rollupプラグインは、バンドラの処理中のある点において起動される一連のメソッドを備えた、シンプルなオブジェクトである。Allen氏はimport文を解析するresolved、ソースコードをロードするload、SvelteのソースコードをJavaScriptに変換するtransformの3メソッドを使用した。以下のコードはworkerの実装を抜粋したものだ。

// The entry file is fixed and set to App.svelte
const bundle = await rollup({input: './App.svelte'}, plugins: [{
  name: 'repl-plugin',
  
  // Importee is the file to import from
  // Importer is the source file containing the import statement 
  resolveId (importee:string, importer:string) {...},
  
  // `load` receives the file to load as parameter
  load(id: string){...},
  
  transform(code: string, id: string) {...}
}]);

const output: string = (await bundle.generate({format: 'esm'})).output[0].code 

// Sends the compiled bundle back to the main thread
self.postMessage(output)

Rollupプラグイン記述の単純化については、以前にSurma氏とArchibald氏が、JSConf Budapest 2019での講演("making things fast in the world of build tools")で取り上げている。Archbald氏は16のカスタムRollupプラグインについて、そのパフォーマンスを大幅に向上したことを認知されている

私たちがWebpackをこき下ろしている、と思われるのであれば、まさにその通りです。正直に言って、Webpackを使ったプロジェクトでの作業と、Rollupを使ったプロジェクトでの作業には、まさに昼と夜ほどの違いがあったのです。何が起きたかを理解できるだけではなく、必要ならば起こっていることを変えることも可能なのだ、と感じました。

コンパイルが完了したならば、生成された文字列を実行してAppコンポーネントを描画する必要がある。ワーカからメインに送り返されたコンパイル後のバンドルを、Outputコンポーネントがiframe内で実行する。ここで再び、メインスレッドとiframepostMessage API経由でコミュニケーションして、適切なバインディングとイベントリスニングが行われる。メインスレッドがコンパイル済のバンドル文字列を送信すると、iframeがバンドルによってエクスポート(デフォルトエクスポート)された、実際にコンパイルされたコンポーネントに変換する。iframeによるこの変換は、文字列を動的にインポートされるURLオブジェクトに変換し、JavaScriptランタイムでパースすることによって行われる。

最終結果

読者には、詳細な技術的説明が数多く含まれた講演の全内容を確認するように推奨する。ソースコードや追加情報は、専用のGitHubプロジェクトで確認することが可能だ。

Svelte SummitはSvelteに関する仮想カンファレンスである。2020年版は10月にオンラインで開催された。すべての講演内容は、Svelte SocietyのYouTubeチャネルで公開される予定である。

この記事に星をつける

おすすめ度
スタイル

BT