JavaScriptでサイトをアプリ化する|Macで選べる道と、コードを書かずに済む境目

「javascript サイトアプリ化」で検索して出てくるのは、たいてい2種類のページです。窓を1つ作ってURLを読み込むだけの短い入門記事か、製品の案内ページか。どちらも、始めてよいかどうかを決める材料には触れていません。判断に要るのは、窓が開いたあとに何が残るか、そして半年後に誰がそれを保守するのかという点です。書くコードの量は、この仕事の小さいほうの半分でしかありません。

「アプリ化」で実際に変わるのはどこか

言葉が誤解を生みやすい部分から片付けます。アプリ化しても、サイトそのものは今までどおりのサーバーで動いたままです。HTTPSで配信される中身も、ページが持っているJavaScriptも変わりません。コンパイルされるわけでも、サイト側から特別な何かが降ってくるわけでもない。

新しく作るのは、ブラウザのエンジンを抱えてそのURLを開く、外側の入れ物のほうです。検索語に入っている「JavaScript」が指しているのも、サイト側の処理ではなく、この入れ物を組み立てるコードを指しています。ここを取り違えたまま入門記事を読み進めると、サイトの作り方の話が出てこないことに戸惑うことになります。

入れ物が変わると手に入るものは、次の5つに整理できます。

  • Dockに専用のアイコンが並ぶ
  • Command+Tabの切り替えとMission Controlに、ブラウザとは別の項目として出る
  • アドレスバーもタブバーも無い窓になり、別のページへ迷い込む経路が消える
  • システム設定の「通知」に単独のアプリとして並び、そのサイトの通知だけを個別に調整できる
  • Cookieの保管場所がブラウザと分かれ、同じサービスの2つ目のアカウントを同時に開ける

このうち何が足りていないのかを先に決めておくと、選ぶ道は自然に絞れます。5つ全部が要る場面は多くありません。逆に、1つも満たさない作り方は、見た目の良いブックマークにとどまります。

遠隔のURLを開く形と、資源を同梱する形

入れ物には2つの形があり、入門記事の混乱の大半はここの取り違えから生まれます。

1つ目は、遠隔のURLをそのまま読み込む薄い形です。アプリの中身はエンジンと数十行の設定だけで、サイトを更新すればアプリの表示も即座に新しくなります。アプリ側は単なる利用者なので、既存の公開手順に手を入れる必要がありません。社内の管理画面や業務用のSaaSを包む用途は、ほぼこの形で足ります。

2つ目は、ビルド済みの資源をアプリの中に同梱し、ローカルのファイルから読み込む形です。回線が無くても起動できる代わりに、アプリがバージョンを持つことになります。画面を1行直すたびに新しいビルドを全員へ配り直す必要が生まれ、APIの接続先は設定で切り替えられるようにしなければならず、これまで同一オリジンだった通信が別オリジンからの通信に変わるためサーバー側の許可設定も要ります。

検索してたどり着いた人の多くが望んでいるのは1つ目の形で、読み始める資料は2つ目の手順書という食い違いが起きがちです。どちらなのかを決めるのに要る時間は1分ほどで、その1分が数日分の作業を減らします。

Electron embeds Chromium and Node.js to bring JavaScript to the desktop. 出典: electronjs.org

エンジンとNode.jsを抱え込む、という一文が、この種の道具の性格をそのまま表しています。抱え込むから何でもできて、抱え込むから重く、抱え込むから更新の責任が作った側に移ります。

JavaScriptで包む道を並べて比べる

同梱されるエンジン 書くコード 回線なしで起動 配布物の大きさ
Electron ChromiumとNode.jsを同梱 主プロセスの設定ファイル 同梱すれば可能 数百MB規模
Tauri 同梱せずmacOSのWebKitを使う 設定ファイルとRustの環境 可能 数MB規模
NW.js ChromiumとNode.jsを同梱 マニフェストのみで足りる 同梱すれば可能 数百MB規模
Nativefier Electronを自動生成 不要(コマンド1回) 不可(遠隔URLのみ) 数百MB規模
SafariのDockに追加 macOSのものを使う 不要 不可 増えない
Chromeのアプリとして導入 導入済みのChromeを使う 不要 不可 増えない

Electronが最初の答えになりやすいのは、資料の量が最も多いからです。自前のChromiumを持つので表示はどの端末でも同じになり、主プロセスからファイルやメニューバーやトレイに手が届きます。その代わり、アプリ1つごとにブラウザ1つ分を抱えることになります。

Tauriは逆の取引をします。macOSではWKWebViewという、OSに元から入っている表示エンジンを使うため配布物が小さい。ただし表示は利用者のmacOSが持つWebKitの版に従い、画面側がJavaScriptのままでもビルドにはRustの環境が要ります。

NW.jsはElectronより古く、入口をマニフェストで指定する作りなので既存のページに向けるのが速い。Nativefierはさらに踏み込んで、URLを渡したコマンド1回でElectronのアプリを生成します。ただしこの企画はすでに保管状態に置かれており、生成物は最後に更新された時点のエンジンのまま止まります。生成系の道具を選ぶときは、最終更新がいつかを先に見ておく価値があります。更新の止まった入れ物は、更新の止まったブラウザと同じ意味を持つためです。

入門記事が終わったところから始まる作業

窓が開くところまでは半日で終わります。そこから先が、実際の仕事です。

作った本人以外が使うアプリは、Apple Developer IDで署名し、Appleの公証を通す必要があります。通っていないアプリは、開発元を確認できないという表示が出て起動できません。署名にはApple Developer Programへの参加が要り、費用は年額99米ドルです。公証の手順も、最初の1回だけでなく毎回の配布に組み込むことになります。自分の端末だけで使うなら省略できますが、同僚に渡した瞬間に必要になります。

配布のあとには更新の経路が要ります。ブラウザのタブはサーバーを更新すれば新しくなりますが、同梱型のアプリはそうではありません。署名済みの配布物を置く更新用の窓口を用意するか、新しい版を毎回手で配るかのどちらかになります。遠隔のURLを読み込む薄い形を選んでいれば、この問題はほとんど消えます。

そして最も見落とされるのが、エンジンの更新です。Chromiumを同梱するということは、Chromiumの脆弱性対応も引き受けるということです。ブラウザなら数日で自動的に直りますが、包んだアプリは誰かが新しいエンジンで作り直して配り直すまで直りません。作ったきりアプリケーションフォルダに置かれた入れ物は、作った日のブラウザのまま止まっています。

遠隔のページを開くときに触っておく設定

遠隔のページを読み込む入れ物は、ブラウザのタブより強い権限で、ネットワーク越しのコードを動かしています。近年の版は安全側の初期値になっていますが、古い記事から写した設定はそうとは限りません。

画面側のNode連携は切ったままにし、コンテキストの分離は有効なままにし、サンドボックスも有効にしておく。OSの機能がどうしても要るときは、preloadの層で名前を付けた関数だけを渡し、モジュールの読み込み機構やファイル操作の全体をそのまま渡さない。この3点が土台です。

もう1つ、行き先の制御も要ります。何も指定しないと、外部サイトへのリンクがアプリの窓の中で開きます。アドレスバーが無い窓なので、利用者からは今どこを見ているのか分かりません。想定したドメインの中だけを窓の中で開き、それ以外は既定のブラウザへ送り出す。数行で済むうえ、アドレスバーの無いブラウザが1つ増える状態を防げます。自分で管理しているサイトでも、そこから外へ出るリンクまでは管理下にないので、入れておく価値があります。

認証も、配り始める前に確かめておく項目です。認証が別のドメインを経由する仕組みだと、戻る経路の無い窓の中で行き先を見失うことがあります。14日目に切れるセッションは、初日に落ちる不具合より厄介で、期限が来るまで見つかりません。

コードを書く価値がある場合とない場合

自分で入れ物を書く価値が出るのは、性格のはっきりした場面に限られます。共通しているのは、ブラウザがWebページに渡さないと決めている機能が要る、という点です。

  • 他人に配る。名前とアイコンとメニューを持つ署名済みのアプリは製品として渡せる
  • 他のアプリを使っている最中でも効く全体のキーボードショートカットが要る
  • メニューバーの常駐項目が要る、または窓を閉じても動き続けてほしい
  • 独自のURLスキームを登録して、メールやチャットのリンクから直接開きたい
  • 選択ダイアログを出さずに決まった場所のファイルを読み書きしたい
  • 窓の大きさを固定する、常に最前面にする、キオスク表示にする

逆に、1人が1台のMacで1つのサイトを開くだけなら、OSに入っている道で足りることのほうが多くなります。SafariはmacOS Sonoma 14以降でページを独立したアプリとしてDockに追加でき、Cookieの保管場所も通知の設定も分かれます。Chromeもページをアプリとして導入できます。どちらも環境構築も証明書も保守計画も要りません。

考えるべきは比率です。入れ物のコードは百行ほどで終わります。証明書、公証、更新の窓口、エンジンの入れ替え、脆弱性が出るたびの作り直しが残りの全部で、しかもこちらは繰り返し発生します。

書き始める前に通す5項目

作りたいものが決まったら、先に挙げた5つの項目に照らしてください。専用のアイコン、切り替え画面での独立、アドレスバーの無い窓、通知設定での独立、Cookieの分離。この5つで足りるなら、OSに入っている道で今日から終わります。

そこにアイコンの差し替え、リンクをブラウザへ逃がさない制御、決まった大きさの窓、そして10人への配布が加わるなら、専用の道具が候補に入ります。どんな設定項目を持つかはできることにまとまっており、手順の細かさは使い方ガイドで見当が付きます。

コードを書く判断が正しいのは、要件が本当に特殊なとき、たとえばローカルのファイルを常時監視する、背景で動き続ける、といった場合です。そこまで来ていれば、入門記事の内容は最初から難所ではありません。

対応表と料金表から読み取れること

道具を選ぶ前に見ておくと判断が早くなる材料が2つあります。

1つは、想定されている使い方の幅です。この種の道具の一覧ページには300を超える雛形が並ぶものもあり、その並びを見れば偏りが分かります。チャットとメールしか無い一覧なら、社内の管理画面を包む用途は想定の外かもしれません。実際にどこまで届くのかは対応サービス一覧を眺めるのが早く、名前の出てこないサイトを包めるかどうかもそこで見当が付きます。

もう1つは、費用の形です。買い切りか継続課金か、対応するmacOSの版はどこまでかを料金で先に確かめておくと、あとで乗り換える手間が減ります。判断に迷いやすい点はよくある質問にまとまっているので、導入前に目を通しておくと質問が減ります。

そして、これらの道は排他ではありません。通知と2つ目のアカウントが要るものはSafariで、拡張機能が要るものはChromeで、アイコンとリンクの制御まで欲しいものだけ専用の道具で作り、ファイル監視や常駐が要るものだけ自分で書く。長く残っている構成は、この混在型がほとんどです。

よくある質問

サイト側のコードを書き換える必要はありますか?

遠隔のURLをそのまま読み込む形なら不要です。ページはブラウザで開いたときと同じように動き、包まれていることを知る必要もありません。書き換えが要るのは、画面の資源をアプリに同梱する形を選んだ場合で、APIの接続先を設定で切り替えられるようにし、別オリジンからの通信をサーバー側で許可する作業が発生します。

作ったアプリを他の人に配るには何が要りますか?

Apple Developer IDでの署名と、Appleの公証が要ります。これを通していないアプリは、開発元を確認できないという表示が出て起動できません。署名にはApple Developer Programへの参加が必要で、費用は年額99米ドルです。公証は最初の1回だけでなく、配布するたびに繰り返します。

1ページのアプリがなぜ数百MBにもなるのですか?

ElectronやNW.jsはChromiumとNode.jsを配布物の中に同梱するため、大きさの正体はページではなくエンジンです。TauriはmacOSに元から入っているWebKitを使うので配布物は数MB規模に収まりますが、表示は利用者のOSが持つ版に従うことになります。

包んだアプリにもブラウザの更新は届きますか?

エンジンを同梱している場合は届きません。作った時点の版のまま止まるため、脆弱性の修正は誰かが新しいエンジンで作り直して配り直したときに初めて反映されます。OSのエンジンを使う道であれば、macOSの更新に追随します。

記事一覧へ戻る