Reactを学習できるサービスmosya Reactの技術的な紹介
2024年3月15日の一粒万倍日に、mosya ReactというReactを学習できるサービスをリリースしました。
こちら1年間の開発期間を経て、ようやくリリースできました!
mosyaの開発期間と合わせると約2年間の開発期間を経てのリリースとなります。
いやー、長かった!
良かったら下のリンクから試してみてください!
どんなサービスか
mosya ReactはReactをオンライン上で学習できるサービスです。
エディターに書いたコードがリアルタイムにプレビューできるようになっていて環境構築なしでReactを学習できます!
採点機能が搭載されているのでReactを自学習したい方におすすめです!
このサービスの開発で特に頑張ったのが以下の特徴です!
- 最新の技術にキャッチアップしている
- ライブラリの型がエディター上で確認できる
- Biomeを動かしていてリントエラーがエディター上で確認できる
最新の技術にキャッチアップしている
mosya Reactでは最新の技術を取り入れた内容になっています!
たとえばReactは当然hooksを教えていますし、TypeScriptを使ったReactの開発も学べます!
また開発が止まってしまっているRecoilではなく、Jotaiを使った状態管理も学べます!
React Routerのレッスンもあるのですが、古いバージョンのv5ではなく、v6を使ったレッスンになっています!
レッスンを作ってる最中で技術的なトレンドが変わってしまってそのレッスンを全て破棄した上で新しいレッスンを作り直すこともあり、とても大変でした。。。
個人開発だからこそできることだと思います!
ライブラリの型がエディター上で確認できる
mosya Reactではライブラリの型がエディター上で確認できます!また自分が書いたコードの型もエディター上で確認でき、まるでVSCodeのような開発環境を提供しています!
チェックしたい変数や関数にカーソルを合わせると型が表示されるので、型を確認しながら開発ができます!
Biomeを動かしていてリントエラーがエディター上で確認できる
mosya ReactではBiomeを動かしていて、リントのレッスンや実践編のレッスンでリントエラーがエディター上で確認できます!
採点時にもリントエラーがあるとクリアできないようになっていて自然と良いコードが書けるようになります!
技術的な紹介
mosya Reactは以下の技術を使って開発しています。
特に頑張った以下の項目について紹介していきます!
- Service Workerを使ったReactのコンパイル
- エディターのリント
- エディター内での型の解決
- 型のテスト
Service Workerを使ったReactのコンパイル
特に頑張ったのがコードを書いた際にリアルタイムに反映されるプレビュ画面です!
このプレビュー画面にはService Workerを使ってReactのコードをコンパイルしています!
どのように実現しているかというと、Service Workerを使ってエディターに書いたコードを受け取り、そのコードをコンパイルしてプレビュー画面に表示しています!
詳しく説明すると、まずエディターで書いたファイルの内容をフロント側でIndexedDBに保存します。
IndexedDBに保存している理由としては、Service WorkerがIndexedDBに保存されたファイルを取得できるためです。
次にコードを書き換えたタイミングでiframeをReactのkeyを使って更新します。
<iframe key={refreshKey} />
iframeの中のファイルへのリクエストに対してService Workerが役に立ちます!
Service Workerの機能の一つで、リクエストに対して処理を介在して返すことができる機能を使います!
通常はここでキャッシュを返すことが多いのですが、今回はService WorkerがIndexedDBに保存されたファイルを取得してコンパイルして返します!
この際に相対パスへのimport文はすべてesmのインポートとして解決するために拡張子を.mjs
に変換した上でコードをesbuild
を使ってコンパイルします。
例えば、Hoge.mjs
のファイルにアクセスがあった場合、Hoge.tsx
のファイルをIndexedDBから取得してコンパイルして、それをService Workerから返します。
Reactなどのライブラリの解決
Reactなどのライブラリの解決はServiceWorkerではなくimportmapという機能を使って解決します。
例えば下のようなコードを書くとしましょう。
import React from 'react'
これができるようにするためには、importmapを使ってreact
をhttps://esm.sh/react
から読み込むように設定します。
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react"
}
}
</script>
esm.sh
はcommonJS
で書かれたライブラリーをCDN経由でesm
に変換してくれるサービスです。
めっちゃ便利です!
エディターのリント
エディターのリントはWeb Workerを使って実現しています。
Web Worker上でBiome
というライブラリを使ってリントを行っています。
ただ、そのままブラウザーで動かすと少しBiome
は重かったのでWorker
上でリント結果を計算してそれをブラウザーに返すようにしました。
ここでComlink
というライブラリを使ってWeb Workerとメインスレッドの通信を行っています。
Worker側のメソッドをフロント側で直感的に呼べてとても使いやすいです。
フロント側
const worker = new Worker(
new URL("../../workers/biome.worker", import.meta.url),
{
type: "module",
}
);
const api = Comlink.wrap<BiomeWorkerApi>(worker);
const diagnostics = await api.getJSReport(code, pathName);
Worker側
// 上記省略
const workerApi = {
getJSReport,
};
Comlink.expose(workerApi);
このとき@biomejs/wasm-web
というWasm化されたBiome
のライブラリがめっちゃ役に立ちました!
ブラウザー上でリンターが動かせるのすごい!
エディター内での型の解決
先ほど紹介したようにmosya Reactでは型を調べたい変数や関数にカーソルを合わせると型が表示されます。
これはあらかじめレッスンごとにnode_modules
のファイル内容をJSON化
しておき、それをMonaco Editor
の機能を使って読み込ませています。
大体こんな感じで読み込ませています。
const deps = await fetch(
`${process.env.NEXT_PUBLIC_CLOUD_FLARE_PUBLIC_URL}/${lessonName}/deps.json`
);
const depsJson = await deps.json()
Object.entries(depsJson).forEach(([key, value]) => {
monaco.languages.typescript.typescriptDefaults.addExtraLib(
value,
`file:///node_modules/${key}`
);
});
node_modulesのファイル内容を読み込ませるだけで型を解決してくれるMonaco Editor
は優秀ですね!
型のテスト
TypeScriptのレッスンでは型レベルのテストも採点時に行っています!
TypeScriptのCompiler API
を使って型のテストを行っています!
これを使うと以下のような感じで型のテストを書くことができます。
import { TypeTester } from "browser-type-tester";
const code = `
type Concat<T extends any[], U extends any[]> = [...T, ...U];
type Result = Concat<[1], [2]>; // expected to be [1, 2]
`;
const typeTest = new TypeTester({ code });
typeTest.it("Concat<A, B>型が正しい", () => {
typeTest.expect("Concat<['a', 'b'], ['c', 'd']>").toBe("['a', 'b', 'c', 'd']");
});
typeTest.it("Concat<A, B>はanyを返さない", () => {
typeTest.expect("Concat<['a', 'b'], ['c', 'd']>").not.toBeAny();
});
const results = await typeTest.run();
console.log(results);
詳しくは以前書いたZennの記事を見てみてください!
今後のアップデート
今後のアップデートではNext.jsを使った開発のレッスンを追加する予定です!
Next.jsに限っては上記の技術では再現不可能なので、WebContainers
というライブラリーを使ってプレビューを実現する予定です!
WebContainers
は商用利用には許可が必要なのですが友人の協力によりなんとかDiscord上でWebContainersの開発者と繋がることができました!
1万セッションユーザーまでは無料で使えてそれ以降は支払いが発生するという形になりました。
Next.jsのレッスンはサブスクに登録いただいているユーザーのみに提供予定なのでなかなか1万セッションユーザーに達することはないと思いますが、もし達した場合はちゃんと支払いをする予定です!
WebContainersに関する記事も実は以前Zennに書いてます!
他のサービスの技術的な紹介
以前リリースしたmosyaやmosya<TC>の開発の詳細については以下のZennの記事にまとめています。
宣伝
mosya Businessという法人向けサービスも運営しています!
mosyaやmosya Reactなどのサービスが法人の社員向けに提供できるもので、社員の進捗や書いたコードを可視化できるようになっています!
進捗の確認画面
コードの確認画面
必要なレッスンも私に相談していただける窓口も用意していますので興味のある方はぜひこちらをご覧ください!
会社でもmosyaを使ってくれるととても嬉しいです!
Discussion