やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

PWA開発について

 ネイティブアプリでなくPWAにすることでクロスプラットフォーム性を優先する。反面、パフォーマンスが悪いため工夫にコストがかかる。何を気にして開発すべきか。どのようにすべきか。ざっくり考えてみる。

気にすること

  • UX(ユーザにとっての使いやすさ)
  • DX(デベロッパにとっての作りやすさ)

 最高のソフトウェアとは、ユーザにとって使いやすいものを、デベロッパが簡単につくれるものである。もしバグや機能追加があっても簡単に編集できる。あまりに簡単なのでユーザとデベロッパの垣根さえなくなる。それくらい、使用と製造が簡単なのが理想である。

 現実は理想どおりにならない。UXとDXはトレードオフになる。つまりユーザの使いやすさを追求すれば、デベロッパは厳しい作り込みを求められ開発コストがあがる。逆にデベロッパが開発コストをさげて楽に作ろうとすれば、ユーザの使いやすさに問題が生じる。

 UXとDXのトレードオフでいいとこどりをしたもののひとつがPWAである。

 PWA開発において検討すべき項目をピックアップする。

  • ROM量(ストレージ)
  • RAM量(メモリ)
  • 通信量(パケット通信費)
  • 通信速度
  • 応答速度
  • 非同期
項目 UX DX
ROM量 少ないほうが他のアプリもたくさん入れられて嬉しい 多いほうがコンテンツを増やせて多くのサービスを提供できる
RAM量 少ないほうが低スペックの安価なマシンでも動作させられて嬉しい 多いほうが高速に高品質なサービスを提供できる
計算量 少ないほうが消費電力が少なくて嬉しい 多いほうが高精度で高品質なサービスを提供できる
通信量(費) とくにモバイルは少ないほうが金銭的に嬉しい 多いほうがたくさんのサービスを提供できる。減らすためにファイル統合や圧縮する
通信速度 とくにモバイルは環境によって大きく変動する。オフラインでも動作してほしい キャッシュの実装でオフライン時に備えるべきだが開発コストがかさむ
応答速度 速いほうが嬉しい。遅いと使う気が失せる。 多機能にすると遅くなってしまう。非同期で体感速度をあげる工夫がいる
非同期 画面が真っ白になったりフリーズしたら使う気が失せる バックグラウンドで処理を走らせ、完了したタイミングで画面に反映させる。それまではローディング中マークとキャッシュ内容を表示する

デベロッパ動線

 仮に最高のソフトウェアを作れたとして、それを普及させるにはどうしたらいいか。答えは以下3ステップである。

  1. アップロードする
  2. ダウンロードできるようにする
  3. 告知する

1. アップロードする

 デベロッパが作ったアプリをサーバにアップロードする。このサーバはネット接続によりどのユーザからもアクセスできること。

2. ダウンロードできるようにする

 ユーザがアプリをダウンロードできるようにする。アカウントが必要などの条件ができるだけ少ないほうが好ましい。

  • インターネット接続が必要
  • CDとリーダが必要
  • USBメモリとリーダが必要
  • 金銭が必要
  • インターネット決済が必要
  • 特定サービスのアカウントが必要

 オンライン/オフラインどちらでもインストール・動作するのが好ましい。

 スマホのネイティブアプリならGoogle PlayApple Storeでダウンロード・インストールする。PWAならそのサイトからインストールする。

3. 告知する

 アプリを紹介する。ダウンロードURLを知らせる。

 アプリをアップロードしたらそのダウンロードURLを取得し、それを紹介したWEBサイトを、Googleなどのサーチエンジンのクローラに通知する。クローラに通知する仕組みとしてWebSub(旧PubSubHubbub)がある。

 SEO検索エンジン最適化)にて、自分のサイトやアプリを検索結果の上位にするよう調整する。キーワードを使って本文を構成したり、HTMLのmetaタグを作り込む。

 また、Open Graphによりツイッターなどで画像を表示するタグを埋め込めばアクセス率を高められる。

 ほかにもコンテンツが豊富なら、Open SearchにてブラウザのURL欄からコンテンツ検索できる。

 SNSや動画サイトなどを使って宣伝してもいい。とにかく、どうにかして紹介URLの表出をつくり、アクセスの入り口を増やす。

ユーザ動線

  1. 存在を知る
  2. 価値を知る
  3. 使い方を知る
  4. パーソナライズする
  5. 紹介する

 もしデベロッパが最高のソフトを作り、世に送り出したあと、それが普及するとしたらどのような経緯になるか。答えは上記5ステップである。ユーザはソフトの存在と価値を知り、便利に使いこなす。良いものであれば拡散したくなるのが人情なので、そのまま他のだれかに紹介する。知らされた別のユーザは同じようなステップを経て拡散してゆく。

1. 存在を知る

  • 口コミ
  • テレビ、新聞、ラジオ
  • チラシ、街頭広告
  • インターネット(SNS、ブログ、...)

 アプリの存在を知らなければ、存在しないのと同じである。なにはなくとも存在を知らせることが重要だ。

 アプリであれば友人、知人などからスマホなどで実物をみせてもらうのが早い。信頼する相手から勧められたら高確率で使われるだろう。

 コロナもあって対面による情報交換の機会は減り、自宅にてネットでググって存在を知ることも増えたと思う。SEO対策と、それを紹介するサイトやコンテンツを作れば、それだけアクセス数は増えるだろう。

 SNSで知ることも多いはずだ。ネットを通じて友人・知人から紹介されたら使ってみたくなるだろう。

2. 価値を知る

 存在を知ったところで、価値がなければ意味がない。ターゲットに対して訴求できるコンテンツを作り込めるかが勝負。

3. 使い方を知る

 直感的にわかるのが理想的。せめてすぐに取説をみれること。

4. パーソナライズする

 自分にとって使いやすいようにカスタマイズできること。できれば最小限の手間で最大限の効果を発揮すること。PCやスマホなど異なるデバイス間におけるデータ共有もこれに含める。

5. 紹介する

 価値があるとわかれば友人・知人などに紹介する。それをワンタッチでできること。

DX

 やりたいことをいかに素早く簡単かつ確実に実現できるか。

  1. 自動化できる
  2. DRYに書ける
  3. クロスプラットフォームである(同一コードで全ターゲット環境用にビルドできる)
  4. コード整形の自動化
  5. テストの自動化+カバレッジ見える化
  6. ビルド時間が短い
  7. デプロイ時間が短い
  8. ドキュメント自動生成(トラブル発生時、問題のコード箇所と修正内容がすぐわかるようにする)

 これらを実現するための開発環境をととのえ、学習する必要がある。いくつもの製品があるため、その選別だけで死ぬほど大変。短期間でほかの製品に入れ替わったりすることもよくある。そのため、ライブラリやフレームワークではなく、標準APIだけを使う戦略もアリ。

運用

  1. サーバの永続性
  2. サーバの不変性
  3. サーバのリクエスト上限

 サーバは借用する。借用サーバは儲からなくなったらサービス終了してしまう。また、リクエスト上限によりPVやデータ通信量に上限がかかっている。ほかにも勝手に広告が挿入されるなど、何らかの不都合なことが起こるかもしれない。時間経過とともに改悪されるのが通例だ。よって、デプロイ先となるサーバをすぐに切り替えることができるような実装であるべき。

動作環境

 ユーザはできるだけ少ない準備でソフトを動作させたい。以下のようなパターンがある。

  1. OS+実行ファイル
  2. OS+実行アプリ+ファイル
  3. OS+実行環境(RE(RuntimeEnvironment))+ファイル
  4. OS+開発環境(SDK(SoftwareDevelopmentKit))+ファイル
  5. OS+ブラウザ+ファイル(HTMLファイル)
  6. OS+ブラウザ+ブラウザDB(CacheAPI、IndexedDB、localStorage, FileAPIなどブラウザが管理するストレージが必要)
  7. OS+内部鯖+ブラウザ+ファイル(HTMLファイル+ServiceWorkerAPIなどサーバが必要なAPIを使っている/鯖側の実装がある)
  8. OS+外部鯖+ブラウザ+ファイル(WebAPIなど外部サーバが必要/鯖側の実装がある)

 ネイティブアプリなら特定のOSにあった実行ファイルをダウンロードすれば動作する。CPUアーキテクチャ、OS、それらのバージョンにあわせたファイルを選択する必要がある。

 JavaPythonなどは実行ファイルやコードに加えて、それが動作するREやSDKが必要になる。もしアプリを動かすならコードだけでなくREやSDKもセットアップせねばならない。

 HTMLならそのファイル一式に加えて、ブラウザが必要。もしブラウザがデータ管理するAPI(CacheAPI, IndexedDB, localStorage, FileAPIなど)を使っているなら、他のデバイスへデータコピーするための工夫が必要である。キャッシュはブラウザのデータまるごとコピーするしかないかもしれない。

 HTMLでServiceWorkerAPIなどサーバが必要なAPIを使っているなら、ローカルサーバが必要。ローカルサーバをつくるためのSDKを用意せねばならない。

 WebAPIを使っているなら、そのAPIが動作する外部サーバと、通信するためのネットワーク接続が必要。

 以下パターンのうち真ん中のおいしいどこどりをするのが理想。

  • 一度ダウンロードしたらオフラインですべて動作する
  • 一度ダウンロードしたらオフラインで動作する。オンライン機能についてはオンラインのときだけ動作する
  • 常にオンラインでなければ動作しない

HTML,CSS,JS要素

 ユーザへの告知も含めると、以下のような要素を実装することになる。

SEO

 SEOのためにメタ情報を作り込む。検索エンジンSNSカード表示、インストール時のアイキャッチ画像など。仕様が乱立しており、重複データもたくさんある。どうにかして最小化し、楽につくりたい。

 Web Storyとかいう新しい表現手法がある。これはメタデータではなく、いわゆるスライドのようなもの。スマホに最適化されたスライドのようなものである。画像やアニメーションを多用し、短いテキストで紹介する。流行るかどうかは未知数。

コンテンツ作成方法

 コンテンツ作成方法によってDXが大きく変わる。UXにも関わってくる。とくにパフォーマンス。

 SSG(静的サイトジェネレータ)でテンプレートエンジンを使って静的HTMLファイルを作成する方法がある。HTMLテンプレート構文とMarkdownを使って入力する。この方法が基本になるだろう。SSGの発展としてJamstackがある。これはヘッドレスCMSやCIなど外部サーバによるサービスを利用して動的コンテンツを生成したり、デプロイを自動化したりする。サーバ依存による問題が伴うため、どの方法で構築するか判断する必要がある。

 JSで動的にHTMLを作ることになるだろう。WebComponentにより自作のHTML要素をJSやCSSを含めて作り込んだり、Nuxt.jsなどのフレームワークを使ったり。あまりにも多種多様な選択肢がありすぎる。まずは基礎であるJSのAPIを勉強するところからはじめたほうがよいだろう。それからサードパーティ製のライブラリを勉強したほうが応用が効くはずだ。途方もない学習コストになるだろう。

PWA

 PWAにするためにService Worker APIが必要。この実装がめちゃくそ大変。ざっくりいえばキャッシュしてダウンロード回数を最小限にするだけなのだが、そのための制御を作り込むのが大変。

バイススマホ/PC)

 デバイススマホとPCに大別される。スマホの場合、タッチ操作と画面の小ささが特徴である。それに対してPCはマウスやキーボード操作で、画面が大きい。これらを同じ画面構成や操作方法にはできない。つまり、スマホ用とPC用で異なる画面をつくる必要がある。さもなくばスマホからみるとスクロールしまくらねばならないUX最悪のクソ画面になってしまう。

 CSSメディアクエリにより画面サイズのブレークポイントを調べてコーディングすることになるのだが、あまりにもパターンが増えすぎたのでJSによる計算で動的にするのが最善だろう。もちろん開発コストはあがる。

 スマホはタッチ操作である。そのためイベント実装も変えねばならない。これまではPCのマウスイベントだけだったが、今やPointer eventsとかいう、マウス、ペン、タッチの3つを兼ね備えたイベントとして実装するのがデファクト・スタンダードのようだ。ただし、マウスホイールや、ピンチインなど、各種デバイスにしかない動作については、ひきつづき専用インベントハンドラで実装するしかない。もちろん開発コストはあがる。

 デバイスの多様化=開発コスト上昇。つらい。

ブラウザ・仕様・規格

 残念ながらブラウザの種類やバージョンによってHTML, CSS, JSで使えるAPIや挙動がちがう。「は? クロスプラットフォームに書けるのがHTMLじゃなかったのかよ!」とキレそうになるが、これが現実である。CSSならベンダープレフィックスをつけたりする。@supportsにより実装されているときだけ適用するようにも書ける。というか、そう書かねば環境によって動作しなくなってしまう。

 時間の経過とともにブラウザの実装状況もかわる。そのときはまたコードを書き換えて最適化するハメになるだろう。終わりなきエンドレス・コーディング地獄である。永遠のアップデートともいう。デベロッパにとってアップデートは義務なのか。アプデ圧に苦しみ開発が苦痛になりそう。でも対応しないとあっという間に古くなって動作しなくなるクソ環境。それがHTML界隈の常識である。

 HTMLはW3Cの策定していたHTML5からWHATWGの策定していたHTML Living Statndardが標準になった。

 ECMA ScriptJavaScriptの規格であり次々と更新されている。

 これらの変更を常に追いかけたり、どのブラウザが、いつ、どのAPIを実装したかを把握するのがとてつもなく大変。

 さらにいえば、Open Graphschema.orgmanifest.jsonなどの規格もある。新しい概念があらわれ、その規格ができて、それに対応すべくコードを作らねばならない。つねに更新しつづけねばならず、永遠に完結しない地獄のような沼。これが嫌だから今までHTML,CSS,JSの学習を避けてきたが、もう逃げられない。時代がHTMLをもとめているから。

パフォーマンス

 さらに作り込むとパフォーマンス問題にぶちあたる。コードが重複せぬようDRYに書こうとしただけでもパフォーマンス問題になる。とくにCSSvar()calc()を多用すると、あっという間に遅くなる。HTML要素数も一度に表示する量を減らさないと重くなる。そのため必然的にCSR, SSRのような動的HTMLをつくりこむことになる。体感速度を改善すべく非同期処理に手を染めざるを得ない。そして一気に難易度があがる地獄。こまかいことをいうと山のようにトピックがあるため割愛。

  • パフォーマンス
    • HTML要素作成方法をどうするか(CSR/SSR
    • ネットワーク通信とキャッシュ(Service Worker API
    • CSS パフォーマンス ハック
    • JS パフォーマンス ハック

 すぐれたコンテンツを作るなら各種バイナリデータの知識も必要になる。パフォーマンスにも直結する。

  • バイナリデータ
    • 画像
      • ラスタ/ベクタ
        • ラスタ(写真, ドット絵)
          • GIF/PNG/JPG
          • WebP/AVIF
        • ベクタ(アイコン、ロゴ、マーク、図形、イラスト)
    • フォント
    • 音声
    • 動画
      • mp4

画像

 AVIFが最新のラスタ画像形式であり圧縮率も最高。ただしそれを表示できるブラウザがどれだけあるか。環境依存度が高い。いまのところ、GIF,PNG,JPGの3種を使うのが最も安全である。

 画像に関しては開発コストが高い。スマホの普及などにより画面サイズとアスペクト比のパターンが膨大になってしまった。これにより各デバイスにて最もきれいに、かつ大きく見せるためには、そのデバイスの画面サイズに合わせた画像を用意する必要がある。

 もしデバイスが小さいのに画像サイズが大きければ、ムダな通信費とレスポンスの遅さが問題になる。もしデバイスが大きいのに画像サイズが小さければ、せっかく大きなディスプレイがあるのに小さい画像しか表示されず不満。これらの問題を回避し、デバイスごとに最適な画像を表示したい。

 もし画面アス比が4:3と小さいなら、16:9の画像はどう表示すべきか? ①両端を切り捨てる ②アス比はそのままに拡大/縮小する どちらにしても問題がある。①ならキャンバス領域が小さくなって描かれていたものが見えなくなる。②なら非常に小さくなってしまう上に、何も表示されない領域がたくさんできてしまう。

 ベクタ画像ならサイズの伸縮は自在であるため、アス比だけを気にすればよい。だが、写真のようなピクセルの集合は表現できない。なのでそうした画像はラスタ画像にせざるをえず、やはりサイズとアス比の問題に直面する。

 サイズとアス比。これを自動的に最適化してくれるツールはいまのところ存在しない。どうあってもトレードオフになってしまうため最適解がない。都度、考えて適切に処置するしかないので開発コストが大きい。

 画像サイズ問題は非常に大変なので詳細は割愛する。基本と最大公約数的な解をもとめた以下URLを参照すれば、最小コストで最大効果を得られるだろう。それだけでも超大変。

<link rel="icon" href="/favicon.ico"><!-- 32×32 -->
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png"><!-- 180×180 -->
<link rel="manifest" href="/manifest.webmanifest">
// manifest.webmanifest
{
  "icons": [
    { "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
    { "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" }
  ]
}

 画像サイズの仕様があまりにも各社、各サービスで乱立しすぎている。これらすべてに最適化させたらどれだけ大変か。画像をいじるだけで人生が終わってしまいそう。

動画

 動画はファイルサイズが大きいため、Stream APIの使用を推奨する。それを使いこなすための技術も。

所感

 なんでこんなに大変なの? ってくらいにヤバイ。PWAのキャッシュ戦略の決定と作り込みだけでも大変そう。アプリ独自の機能をつくる前に、キャッシュなどベース部分の作り込みだけで死ぬ。あたりまえだが、それ以前にHTML,CSS,JSの基本構文をおぼえる必要がある。はたしていつ終わるだろうか。人生をかけた戦いになりそうなくらい気の遠くなる話。

 HTML4時代、メモ帳で書いていたころとは違いすぎる。あのときですら苦痛だったのに。もはや次元がちがう。