見出し画像

SolidJSがどうやら魅力的です

はじめに

SHIFT DAAE(ダーエ) 開発グループ所属の武藤です。

うっかりSolidJSに触れてみたら、沼にハマりそうです。そんな個人的に第一印象抜群のSolidJSは、高パフォーマンス&Reactの様にコードが書けることが特徴の、最近注目を集めるJavaScriptフレームワークです。

早速、ご紹介していきます。入門編としてご覧ください!

SolidJSとは

概要

ReactやVueと同じく。宣言型のJavaScriptフレームワークです。仮想DOMを使用せず、一度作成された実DOMノードをSignalやMemoといったプリミティブの変化に伴い部分的に更新させることで、高パフォーマンスなUIを実現しています(SignalとMemoについては後程の実装で触れますので、分からない場合はとりあえず読み飛ばしていただいて大丈夫です)。

Reactの様に書ける

JSX, Fragment, Context, Typescript等のモダンフレームワークが採用する機能を持ち、React Hooksライクな関数もあることから、Reactと似たコードでコンポーネントを作ることが出来ます。

以下、SolidJSのHPのトップ画面にあるSolidJSの場合のコンポーネントソースの転載です。Reactをご存知の方であれば、見るだけでどんな処理が行われるかイメージできるのではないでしょうか。

// main.tsx
import { render } from "solid-js/web";
import { onCleanup, createSignal } from "solid-js";

const CountingComponent = () => {
  const [count, setCount] = createSignal(0);
  const interval = setInterval(
    () => setCount(count => count + 1),
    1000
  );
  onCleanup(() => clearInterval(interval));
  return <div>Count value is {count()}</div>;
};

render(() => <CountingComponent />, document.getElementById("app"));

とはいえ、SolidJSとReactで微妙に仕様が異なる部分があり、そこが起因で混乱を招くケースもありそうです。後程、数ケースご紹介します。

高パフォーマンス

とにかくパフォーマンスが良いらしく、他JavaScriptフレームワークとの比較において、VanillaJSに次ぐパフォーマンスを叩き出してます。 高パフォーマンスでほぼReact。これはもう、触ってみたくなりますね!

学習材料について

SolidJSはまだ注目され始めてから間もないためか、参考となる情報ソースはそう多くない印象です。日本語となると更に少ない。。。

ただ、SolidJSのHPそのものに日本語バージョンがあり、かつガイドやチュートリアルが充実しています。なので、基本はHPの内容をもとに手を動かし、その他サイトの紹介記事で知見を吸収していくのが良いのではないかと思います。

個人的には、HPのチュートリアルに各機能の役割が丁寧に記載されており非常に参考になりました。

SolidJSのHP

試しに動かしてみる

それでは、SolidJSを試してみます!

プロジェクト作成

ターミナルにて、以下コマンドを実行します。

$ npx degit solidjs/templates/js my-app # JavaScript用の場合
$ npx degit solidjs/templates/ts my-app # TypeScript用の場合(以下、TypeScriptで説明します)

プロジェクトフォルダに移動し、パッケージをダウンロード後、サーバを起動します(yarnの場合は適宜読み替えてください)。

$ npm install
$ npm run dev

ブラウザにてhttp://localhost:3000に接続すると、ページが表示されました!

機能確認

それでは、SolidJSの関数の内、状態変化に主に使うであろう3つを試してみます!

createSignal

createSignalは、イベントによって変化させたい項目を取得/更新するための関数です。

Reactで言うところのuseStateに相当します。

// src/App.tsx
const App: Component = () => {
  const [count, setCount] = createSignal<number>(0);

  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>countUp</button>
      <button onClick={() => setCount(0)}>clear</button>
    </div>
  );
};

count()を値取得に、setCount()を値更新に使用します。Reactのノリだと値取得はcountとしてしまいそうですが、createSignalは2つの関数を返却するため、関数として扱うことに注意です。

createEffect

createEffectは、Signal等の変更に伴う副次的な処理を扱うための関数です。

Reactで言うところのuseEffectに相当します。

// src/App.tsx
const App: Component = () => {
  const [count, setCount] = createSignal<number>(0);
  const [count2, setCount2] = createSignal<number>(0);

  createEffect(() => console.log(`count: ${count()}`));

  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>countUp</button>
      <button onClick={() => setCount(0)}>clear</button>
      <p>Count2: {count2()}</p>
      <button onClick={() => setCount2(count2() + 1)}>countUp</button>
      <button onClick={() => setCount2(0)}>clear</button>
    </div>
  );
};

createEffectでは依存関係を自動で把握し変更を検知した場合に再実行されます。上記例ではcreateEffectの処理に影響するcount()の変更はトリガとなり再実行されますが、count2()の変更では再実行されません。

また、React(useEffect)の場合は依存関係を処理とは別の引数としてセットする必要がありますが、createEffectの場合は自動判断なので、その必要がありません。

createMemo

createMemoは、Signalの値を用いて別の結果を返す派生Signal(SolidJSのガイドの呼称)にて、値をキャッシュさせるための関数です。

Signalの変化をトリガに再実行されるのはメモ化の有無によらず同じですが、メモ化することで結果をキャッシュするため、何度も計算する必要がなくなります。

// src/App.tsx
const App: Component = () => {
  const [count, setCount] = createSignal<number>(0);

  // 派生signal
  const squareCount = () => {
    console.log(`squareCount: ${count()}`);
    return count() ** 2;
  }

  // 派生signal メモ化
  const squareCountMemo = createMemo(() => {
    console.log(`squareCountMemo: ${count()}`);
    return count() ** 2;
  });


  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>countUp</button>
      <button onClick={() => setCount(0)}>clear</button>
      <br></br>
      <span style={`margin-right: 8px`}>square:</span>
      <span style={`margin-right: 8px`}>{squareCount()}</span>
      <span style={`margin-right: 8px`}>{squareCount()}</span>
      <span style={`margin-right: 8px`}>{squareCount()}</span>
      <span>{squareCount()}</span>
      <br></br>
      <span style={`margin-right: 8px`}>square_memo:</span>
      <span style={`margin-right: 8px`}>{squareCountMemo()}</span>
      <span style={`margin-right: 8px`}>{squareCountMemo()}</span>
      <span style={`margin-right: 8px`}>{squareCountMemo()}</span>
      <span>{squareCountMemo()}</span>
    </div>
  );
};

メモ化している場合はコンソールログへの出力回数が1回で済んでおり、キャッシュによる効果が確認できます。

Reactに慣れている方への注意点

Reactと類似しているからこその、SolidJSでのコード作成時の注意点もいくつかあります。

あくまでほんの一部ですがご紹介します。

  • jsxのcssクラス名指定は、className={}ではなくclass={}

  • jsxのstyle表現においてcamelCaseではなくkebab-caseを使う(backgroundColor -> "background-color")

  • inputタグによるテキストフィールドにて、onChangeはフォーカスが外れた時に処理が行われる。入力中の内容に対応したい場合はonInputを使用する。

  • propsの分割代入はしてはいけない(参考

おわりに

SolidJSの入門的なご紹介でした!

今主流なフレームワークのReactに似たコードで、パフォーマンスが向上されるらしい(いつかパフォーマンスも検証して「らしい」を外したい)のはとてもいいですね!

もちろんReact及びそれを取り巻く豊富なライブラリ群は魅力的ですので、こちらも継続してウォッチしていきつつ、SolidJSでの開発も選択肢として取り入れられるよう、情報収集を続けていきます。

\もっと身近にもっとリアルに!DAAE公式Twitter/


執筆者プロフィール:武藤 将太
SHIFT DAAE部のエンジニア。主にWebシステムの開発経験を経て、SHIFTに入社。
新しくM1 Macを買いたいと思ってはいるものの、値段の高さに足踏み中。
ブログ投稿による入りは、Mac代の足しにはならんのです。

お問合せはお気軽に
https://service.shiftinc.jp/contact/

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/