diff --git "a/source/_posts/2025/20250825a_\345\244\217\343\201\256\350\207\252\347\224\261\347\240\224\347\251\266\351\200\243\350\274\211_2025.md" "b/source/_posts/2025/20250825a_\345\244\217\343\201\256\350\207\252\347\224\261\347\240\224\347\251\266\351\200\243\350\274\211_2025.md" index 5ea6dc8192a9..4008cffc79d7 100644 --- "a/source/_posts/2025/20250825a_\345\244\217\343\201\256\350\207\252\347\224\261\347\240\224\347\251\266\351\200\243\350\274\211_2025.md" +++ "b/source/_posts/2025/20250825a_\345\244\217\343\201\256\350\207\252\347\224\261\347\240\224\347\251\266\351\200\243\350\274\211_2025.md" @@ -35,8 +35,8 @@ lede: "夏の自由研究2025のブログリレーのインデックス記事で | 8/28(木) | 大前七奈 | [dbt fusion engine・dbt profiler](/articles/20250828a/) | | 8/29(金) | 澁川喜規 | [PostgreSQLの標準の全文検索](/articles/20250829a/) | | 🌻 | - | - | -| 9/1(月) | 清水雄一郎 | Cursor+GitHub Actionsで、自分でソースコード書かずにiOSアプリをApp Storeに公開できるか | -| 9/2(火) | 永井優斗 | クロスプラットフォームフレームワークLynX | +| 9/1(月) | 清水雄一郎 | [Cursor+GitHub Actionsで、自分でソースコード書かずにiOSアプリをApp Storeに公開できるか](/articles/20250901a/) | +| 9/2(火) | 永井優斗 | [クロスプラットフォームフレームワークLynX](/articles/20250902a/) | | 9/3(水) | 真野隼記 | ソフトスキル | | 9/4(木) | 神崎林太郎 | nftablesを使った透過型プロキシの構築 | | 9/5(金) | 仲田帆志弥 | 情報に溺れないためのDeep Researchの活用方法 | diff --git "a/source/_posts/2025/20250902a_\346\226\260\343\201\227\343\201\204\343\203\236\343\203\253\343\203\201\343\203\227\343\203\251\343\203\203\343\203\210\343\203\225\343\202\251\343\203\274\343\203\240\343\203\225\343\203\254\343\203\274\343\203\240\343\203\257\343\203\274\343\202\257Lynx\343\202\222\350\247\246\343\201\243\343\201\246\343\201\277\343\201\237\343\200\202.md" "b/source/_posts/2025/20250902a_\346\226\260\343\201\227\343\201\204\343\203\236\343\203\253\343\203\201\343\203\227\343\203\251\343\203\203\343\203\210\343\203\225\343\202\251\343\203\274\343\203\240\343\203\225\343\203\254\343\203\274\343\203\240\343\203\257\343\203\274\343\202\257Lynx\343\202\222\350\247\246\343\201\243\343\201\246\343\201\277\343\201\237\343\200\202.md" new file mode 100644 index 000000000000..fd6d819caa15 --- /dev/null +++ "b/source/_posts/2025/20250902a_\346\226\260\343\201\227\343\201\204\343\203\236\343\203\253\343\203\201\343\203\227\343\203\251\343\203\203\343\203\210\343\203\225\343\202\251\343\203\274\343\203\240\343\203\225\343\203\254\343\203\274\343\203\240\343\203\257\343\203\274\343\202\257Lynx\343\202\222\350\247\246\343\201\243\343\201\246\343\201\277\343\201\237\343\200\202.md" @@ -0,0 +1,170 @@ +--- +title: "新しいマルチプラットフォームフレームワークLynxを触ってみた。" +date: 2025/09/02 00:00:00 +postid: a +tag: + - Lynx + - クロスプラットフォーム + - React + - React Native +category: + - Mobile +thumbnail: /images/2025/20250902a/thumbnail.png +author: 永井優斗 +lede: "![image.png]" +--- + +image.png + +[夏の自由研究2025](/articles/20250825a/)ブログ連載の6日目です。 + +## はじめに + +こんにちは、HealthCare Innovation Group(HIG)[^1]の永井優斗です。 + +2025年3月にTikTokの運営会社である、ByteDance社が [Lynx](https://lynxjs.org/)というクロスプラットフォームフレームワークを公開しました。 + +LynxはJavaScriptベースでのマルチプラットフォームフレームワークです。そう書かれると、React Nativeを思い浮かべない人はいないでしょう。React Nativeの競合になりうるフレームワークです。 + +まだまだLynx自体の知名度は低く、LynxとGoogle検索すると、元の意味であるオオヤマネコが出てきます。リンクスティップ(耳の先から出ている、狩りに役立つ毛)が可愛いですね + +image.png + +- Lynx lynx, Nationalpark Bayerischer Wald, Deutschland の写真 +- 著作者:Martin Mecnarowski +- CC表示-継承 3.0 https://commons.wikimedia.org/wiki/File:Lynx_lynx_1_(Martin_Mecnarowski).jpg による + +この記事では、Lynx を触ってみた体験を簡単にまとめます。なお、開発した環境は、私物のMac mini(2020 M1)で、iPhone上での動作を試してみました。 + +## セットアップ + +※セットアップにはNode.js 18以上がインストールされている必要があります。 + +まずは公式の `create-rspeedy` でプロジェクトを作成します。 + +```bash +npm create rspeedy@latest +cd +npm install +npm run dev +``` + +- セットアップについては、[公式ドキュメントの[Quick Start](https://lynxjs.org/guide/start/quick-start.html#ios-simulator-platform=macos-arm64,explorer-platform=ios-simulator)もご確認ください + +`npm run dev`でビルド成功後、QRコードとURLがターミナルに表示されます。 + +動作確認には、「Lynx Explorer」というアプリが必要です。 + +公式サイトに記載の「Download LynxExplorer-arm64.app.tar.gz.」のリンクから落として解凍し、XCodeのシミュレーターにドロップすることで、アプリをシミュレーターにインストールできます。ターミナルに表示されたURLをシミュレーター上の「Lynx Explorer」のCard URLに入力することで、動作確認ができます。 + +image.png + +また、「Lynx Explorer」は、[App Store](https://apps.apple.com/us/app/lynx-go-dev-explorer/id6743227790)でも公開されており、スマホ実機にもインストールすることが可能です。 + +同じく、ターミナルに表示されたURLを入力するか、QR コードをアプリで読み込むと、そのままスマホ実機で動作確認できます。 +開発している横で、ケーブルに繋ぐこともなく、即実機で試せるのは便利だなと感じました。 + +## カウンターアプリを作ってみた。 + +あまり時間をかけずに触ってみたいと思ったため、簡単なカウンターアプリを作成することにしました。 + +Lynx では HTML タグではなく、 **``** や **``** を使います。 +イベントハンドラも React っぽくはなく、 **`bindtap`** , **`bindlongpress`** のように書くのが特徴的です。 + +```jsx App.jsx + setCount(c => c + 1)}> + +1 + +``` + +CSS もそのまま使えるので、スタイリングは Web の知識でいけそうです。 + +## イベント拡張:「長押し」「ダブルタップ」 + +次に、イベントAPIを活かしてカウンターを拡張しました。 + +- **+ボタン** → タップで +1、長押しで +10 +- **–ボタン** → タップで –1、ダブルタップで –10 +- **Resetボタン** → 0 にリセット + +### ダブルタップ判定の実装例 + +```jsx App.jsx + const lastTapTimeStampRef = useRef(0); + const singleTapTimeoutIdRef = useRef(null); + const DOUBLE_TAP_GAP = 250; + + // -1 ボタンのtapハンドラ + const handleMinusTap = () => { + const now = Date.now(); + const gap = now - lastTapTimeStampRef.current; + + if(gap < DOUBLE_TAP_GAP){ + // ダブルタップと判定する + if(singleTapTimeoutIdRef.current) { + // ダブルタップが成立したらシングルタップ予約をキャンセル + clearTimeout(singleTapTimeoutIdRef.current); + singleTapTimeoutIdRef.current = null; + } + + lastTapTimeStampRef.current = 0; + setCount((c) => c - 10); + return; + } + // シングルタップかもしれない場合は、少し待って確定する。 + lastTapTimeStampRef.current = now; + singleTapTimeoutIdRef.current = setTimeout(() => { + setCount((c) => c - 1); + lastTapTimeStampRef.current = 0; + singleTapTimeoutIdRef.current = null; + }, DOUBLE_TAP_GAP); + }; +``` + +標準で「ダブルタップ」イベントがあるわけではないので、`useRef` を使って「直前のタップ時刻」を保存し、一定時間内にもう一度押されたらダブルタップとみなすようにしました。 + +作成したアプリのUIはこんな感じになりました。 + +image.png + +[GitHubでソースを公開](https://github.com/yut0naga1/counter-lynx)しています。 + +## 触ってみて感じた疎結合感 + +カウンターアプリを作っていてまず感じたのは、**「ReactとLynxががっちり結合しているわけではない」** という点です。 + +イベントの書き方ひとつ取っても、React Native なら `onPress` を書くところを Lynx では `bindtap` を書きます。Reactのクリックイベントが`onClick`であるのに対してのReact Nativeの`onPress`はとてもわかりやすいなと思いますが、そのまま持ってきたという感じもします。 + +LynxはReactのAPIをそのまま持ってきたのではなく、Lynx側に独自のインターフェースがあり、それをReactでラップしているんだなと感じました。 + +また、スタイルが素のCSSで書けるのも印象的です。React Native のように StyleSheet API に閉じていないため、「Webの知識をそのまま活かせる」一方で「別のフロントエンドフレームワークからも同じ描画基盤を使える」余地があると感じました。 + +## React Nativeとの違いとしてのフレームワーク依存性 + +React NativeはReactに強く結合しており、他のUIフレームワークを載せることはできません。Lynxは描画基盤とフレームワークが、(React Nativeと比較してという意味で)疎結合です。React 以外に他のJSフレームワークを載せることもできるかもしれません。 + +実際、公式のLynx公開時の説明でもLynxが対応するフレームワークReactに限らないことを表明しています。 + +> We are open-sourcing ReactLynx ("React on Lynx") as Lynx's initial frontend framework flavor, enabling componentized, declarative UI on Lynx. +> **However, Lynx isn't limited to React.** In fact, other frameworks already represent roughly half of Lynx's overall usage, demonstrating its neutrality in hosting different flavors. + +※太字はこちらでつけたものです。 + +実際、[GitHub Issue #193](https://github.com/lynx-family/lynx/issues/193) のとおりVue.jsをLynxに載せられないかというディスカッションはすでに始まっており、今後Vue.jsに対応するかもしれません。その話はまた、[別の機会](https://vuefes.jp/2025/speaker/yut0naga1)で深掘りしたいと思います。 + +## 最後に + +Lynxはまだ3月に出たばかりでもあり、エコシステムの発展やLynx自体も「枯れる」までにはもうちょっと時間がかかるとは思います。 +いますぐ業務で使えるというわけではないですが、今後React NativeやFlutterの対抗馬として、マルチプラットフォーム開発に使える日が来るかもしれません。 + +その頃にはVue.jsにも対応しているといいな。 + +## おまけ + +今回Lynxを触るということまでは決めていたのですが、何つくろっかなーと悩んでいました。ChatGPT5と相談することで、簡単に試すことができました。 + +カウンターアプリを作ることや、作ったカウンターの拡張(長押し、ダブルタップ)はChatGPTのアイデアです。また、ChatGPTが生成したサンプルのソースコードに対して「この行何やってるんだろうか」と質問したり、逆にChatGPTが明らかにおかしいところを指摘しながら、対話を進めるとだいぶ理解が捗りました。 + +新しいプログラミング言語に入門するとき、LLMは大きなアシスタントになってくれるんだなと感じました。 + +[^1]:医療・ヘルスケア分野での案件や新規ビジネス創出を担う、2020年に誕生した事業部です。設立エピソードは[未来報の記事](https://note.future.co.jp/n/n8b57d4bf4604)をご覧ください。 diff --git a/source/images/2025/20250902a/image.png b/source/images/2025/20250902a/image.png new file mode 100644 index 000000000000..a7baf7f30de3 Binary files /dev/null and b/source/images/2025/20250902a/image.png differ diff --git a/source/images/2025/20250902a/image_2.png b/source/images/2025/20250902a/image_2.png new file mode 100644 index 000000000000..170f292bcba2 Binary files /dev/null and b/source/images/2025/20250902a/image_2.png differ diff --git a/source/images/2025/20250902a/image_3.png b/source/images/2025/20250902a/image_3.png new file mode 100644 index 000000000000..b9c3bf7a90b5 Binary files /dev/null and b/source/images/2025/20250902a/image_3.png differ diff --git a/source/images/2025/20250902a/image_4.png b/source/images/2025/20250902a/image_4.png new file mode 100644 index 000000000000..04999c5e3dc6 Binary files /dev/null and b/source/images/2025/20250902a/image_4.png differ diff --git a/source/images/2025/20250902a/thumbnail.png b/source/images/2025/20250902a/thumbnail.png new file mode 100644 index 000000000000..738b069ab08d Binary files /dev/null and b/source/images/2025/20250902a/thumbnail.png differ