私は最初のCSSを作成した瞬間にコーディングに恋をしました:Hover Effect。数年後、ウェブ上のインタラクティブ性への最初の噛みつきは、私を新しい目標に導きました。ゲームを作ることです。
一緒に遊ぶ初期の瞬間:ホバーは特別なものでも、役に立たなかった。青い正方形のレスポンシブなグリッドを作成したことを覚えています(フロートで作られている場合、タイムラインのアイデアが得られる場合)。私は何時間も箱の上に覆い隠しているように感じたものを費やし、窓をサイズに変更してサイズとアライメントを変更し、それからもう一度やり直しました。それは純粋な魔法のように感じました。
私がウェブ上に構築したものは、当然、長年にわたって
時にはそれは単なるCodepenデモでした。時には、VercelまたはNetlifyに展開された小さなサイドプロジェクトでした。カラーフラッド、ハングマン、またはブラウザで4つを接続するなどのゲームを再現するという課題が大好きでした。
しばらくして、ゴールが大きくなりました。実際のゲームを作ったらどうでしょうか? Webアプリだけではありません。本当のライブ、誠実さ、アプリからダウンロードからのダウンロードゲーム。昨年8月、私はこれまでで最も野心的なプロジェクトに取り組み始めました。4か月後、私はそれを世界にリリースしました(読んで:それをいじるのにうんざりしました):Quinaと呼ぶワードゲームアプリ。
Quinaを説明する最も簡単な方法は、それは首謀者ですが、5文字の言葉があります。実際、首謀者は実際には古典的なペンアンドペーパーゲームのバージョンです。 Quinaは、同じオリジナルゲームの単なる別のバリエーションです。
Quinaのオブジェクトは、秘密の5文字の単語を推測することです。推測のたびに、コードワードに推測がどれほど近づいているかを示す手がかりが得られます。その手がかりを使用して次の推測を改善しますが、合計では10個しかありません。尽きるとあなたは負けます。
「Quina」という名前は、ラテン語で「一度に5つずつ」を意味するために生じました(または、とにかくGoogleは私に言った)。従来のゲームは通常、4文字の単語、または時には4桁(または首謀者の場合は4色)で再生されます。 Quinaは繰り返し文字なしで5文字の単語を使用しているため、ゲームには独自のルールで再生される名前があることにふさわしいと感じました。 (元のラテン語がどのように発音されたのかわかりませんが、「Quinn-Ah」と言います。
私は約4か月間、アプリを構築するために夜と週末を過ごしました。この記事に、ゲームの背後にある技術、関係する決定、そしてこれがあなたが自分自身を旅することに興味がある道である場合に学んだ教訓について話したいと思います。
私はVueの大ファンであり、このプロジェクトをそのエコシステムの知識を拡大する方法として使用したかったのです。私は別のフレームワークを使用することを検討しました(私もSvelteとReactでプロジェクトを構築しました)が、Nuxtは親しみやすさ、使いやすさ、成熟のスイートスポットに当たったと感じました。 (ちなみに、あなたが知らなかったか、推測していなかった場合:Nuxtはnext.jsに相当するVUEと公正に説明できます。)
私は以前にNuxtと深く深くなっていませんでした。非常に小さなアプリをいくつか。しかし、私はNuxtが静的アプリにコンパイルできることを知っていました。これはまさに私が望んでいたものです。また、 NuxtがVueコンポーネントをA /Pagesフォルダーにドロップするのと同じくらい簡単にルーティングを処理できることも知っていましたが、これは非常に魅力的でした。
さらに、Vuex(Vueの公式の国家管理者)はそれ自体がそれだけでは複雑ではありませんが、Nuxtがそれをさらに簡単にするためにほんの少しの砂糖を追加する方法に感謝しました。 (Nuxtは、使用する前にコンポーネントを明示的にインポートする必要がないなど、さまざまな方法で物事を簡単にします。マークアップに配置することができ、Nuxtは必要に応じてそれを把握し、自動輸入します。)
最後に、私は事前にプログレッシブWebアプリ(PWA)を構築していることを知っていたので、既に関連するすべての機能(オフライン機能のサービスワーカーなど)を構築するのに役立つNuxt PWAモジュールが既に存在しているという事実は、すでに大きな引き分けでした。実際、目に見えないハードルに利用可能なNUXTモジュールの印象的な配列があります。それにより、ヌックスは最も簡単で、最も明白な選択肢になり、私は決して後悔しなかったものです。
最終的に、私が行ったときに、より多くのモジュールを使用することになりました。これには、Markdownでページコンテンツを書くことができます。 「FAQ」ページと「再生方法」ページにもその機能を使用しました(Markdownでの執筆は、ハードコーディングHTMLページよりもはるかに優れているため)。
Quinaは最終的にGoogle Playストアに家を見つけるでしょうが、それがどのように、どこで再生されたかに関係なく、私はそれがGet-Goの本格的なアプリのように感じたいと思っていました。
開始するために、それはオプションのダークモードと、多くのネイティブアプリが持っているように最適な使いやすさのためにモーションを減らすための設定を意味します(そして、アニメーションのあるものと同じように、モーションが減少する場合)。
ボンネットの下では、両方の設定は最終的にアプリのVuexデータストアのブール値です。 Trueの場合、設定はアプリのデフォルトレイアウトで特定のクラスをレンダリングします。 Nuxtレイアウトは、すべてのコンテンツを「ラップ」し、アプリのすべて(または多くの)ページ(一般的に共有ヘッダーやフッターなどに使用されるが、グローバル設定にも役立つ)でレンダリングするVUEテンプレートです。
<div> <nuxt></nuxt> </div> テンプレート> 'vuex'から{mapgetters}をインポート デフォルトのエクスポート{ 計算:{ ... MapGetters(['DarkMode'、 'Recucemotion'])、 }、 //他のレイアウトコンポーネントコードはこちら }
設定といえば、Webアプリはいくつかの異なるページ(メニュー、設定、概要、再生など)に分割されていますが、共有されたグローバルVUEXデータストアは、アプリの領域間で物事を同期させ、シームレスに感じるのに役立ちます(ユーザーは1ページで設定を調整し、別のページでゲームに適用することを確認します)。
アプリ内のすべての設定は、ユーザーがページ間でナビゲートするときに設定を追跡することに加えて、セッション間で値を保存とロードすることを可能にするLocalStorageとVuexストアの両方に同期されます。
ナビゲーションについて言えば、ページ間を移動することは、フルページのトランジションを追加することで、キーナをネイティブアプリのように感じる機会がたくさんあると感じた別の領域です。
Vueの移行は一般的にかなり簡単です。「To」および「From」の遷移状態のCSSクラスと名付けられたCSSクラスを書くだけですが、Nuxtはさらに一歩進んで、ページのVueファイルに1行しかないページ遷移を設定できます。
デフォルトのエクスポート{ トランジション:「ページスライド」 // ...残りのコンポーネントプロパティ }
その移行プロパティは強力です。 Nuxtは、このページに移動するたびに、ページスライドトランジションをこのページに適用する必要があります。そこから、私たちがする必要があるのは、アニメーションを処理するクラスを定義することだけです。これが私のページスライドSCSです:
/ * assets/css/_animations.scss */ .page-slide { &-enter-active { 遷移:0.35キュービックベジエすべて(0、0.25、0、0.75); } &-Leave-active { 遷移:すべての0.35キュービックベジエ(0.75、0、1、0.75); } &-入力、 &-Leave-to { 不透明:0; 変換:翻訳(1REM); .reduce-motion&{ 変換:なし!重要。 } } &-Leave-to { 変換:translatey(-1rem); } }
.reduce-motionクラスに注意してください。それが私たちがすぐ上のレイアウトファイルで話したことです。ユーザーが、変換プロパティを無効にすることで、モーションの減少(メディアクエリまたは手動設定を介して)を好むことを示したときに視覚的な動きを防ぎます(これは、分裂的!重要なフラグの使用を保証するように思われました)。ただし、これは実際には動きではないため、不透明度はまだ消えていくことが許可されています。
トランジションと処理に関するサイドノート404:もちろん、トランジションとルーティングは、JavaScriptによってフードの下で処理されます(正確にはVueルーター)が、スクリプトがアイドルページで実行されるのを停止するイライラする問題に遭遇しました(たとえば、ユーザーがアプリまたはタブをしばらく開いた場合)。これらのアイドルページに戻ってリンクをクリックすると、Vueルーターが実行を停止していたため、リンクは相対的で404として扱われます。
例: /FAQページはアイドル状態になります。ユーザーが戻ってきて、リンクをクリックして /optionsページにアクセスします。アプリは /FAQ /オプションに移動しようとしますが、もちろん存在しません。
これに対する私の解決策は、カスタムエラーページ(これはすべてのエラーを自動的に処理するNUXTページです)で、着信パスで検証を実行し、パスの最後にリダイレクトします。
//レイアウト/error.vue mounted(){ const lastpage = '/' this。$ route.fullpath.split( '/')。pop() //リダイレクトループを作成しないでください if(lastpage!== this。$ route.fullpath){ これ。$ router.push({ パス:ラストページ、 }) } }
a)ネストされたルートがないため、これは私のユースケースで機能しました。 b)最後に、パスが有効でない場合、404にヒットします。
トランジションは素晴らしいですが、Quinaは、振動と音の両方なしで、特にスマートフォンでネイティブアプリのように感じないことも知っていました。
ナビゲーターAPIのおかげで、最近のブラウザでは振動が比較的簡単に達成できます。ほとんどの最新のブラウザを使用すると、window.navigator.vibrate()を呼び出してユーザーに少し話題になります。または、非常に短い期間を使用して、スマートフォンキーボードでキーをタップするときのように、ほんの少しの触覚フィードバックを使用できます。
明らかに、いくつかの理由で、振動を控えめに使用したいと考えています。第一に、多すぎると簡単に悪いユーザーエクスペリエンスになる可能性があるためです。第二に、すべてのデバイス/ブラウザがそれをサポートしているわけではないため、Vibrate()関数を呼び出そうとする方法と場所に非常に注意する必要があるため、現在実行中のスクリプトをシャットダウンするエラーを引き起こさないようにします。
個人的には、私の解決策は、ユーザーが振動を許可していることを確認するためにVuex Getterを設定することでした(設定ページから無効になる可能性があります)。現在のコンテキストがサーバーではなくクライアントであること。そして最後に、機能が現在のブラウザに存在すること。 (ES2020オプションチェーンは、その最後の部分でもここでも機能していたでしょう。)
// store/getters.js 振動(状態){ もし ( process.client && state.options.vibration && typeof window.navigator.vibrate!== '未定義' ){ trueを返します } falseを返します }、
サイドノート: Process.Clientのチェックは、NUXTで重要です - およびノードで実行される可能性のあるコードを使用して他の多くのフレームワーク - ウィンドウは常に存在するとは限りません。コンポーネントはビルド時にノードで検証されるため、これは静的モードでNUXTを使用していても真です。 Process.Client(およびその反対のProcess.Server)は、実行時にコードの現在の環境を検証するだけで、ブラウザのみのコードを分離するのに最適なNuxt nuxt nuxt nuxt nuxt nuxt nuxtです。
サウンドは、アプリのユーザーエクスペリエンスのもう1つの重要な部分です。私自身の効果を作るのではなく(間違いなくプロジェクトにさらに数十時間を追加したでしょう)、私はその領域で彼らが何をしているのかをよりよく知っていて、オンラインで無料のゲームのサウンドを提供した数人のアーティストのサンプルを混合しました。 (詳細については、アプリのFAQを参照してください。)
ユーザーは、好みのボリュームを設定したり、完全にサウンドをシャットダウンしたりできます。これと振動は、ユーザーのブラウザのLocalStorageにも設定されており、Vuexストアに同期されます。このアプローチにより、ブラウザに保存された「永続的な」設定を設定できますが、参照するたびにブラウザから取得する必要はありません。 (たとえば、音が再生されるたびに現在のボリュームレベルを確認し、経験を殺すのに十分な場合に毎回ローカルストレージコールを待つことの待ち時間を確認してください。)
何らかの理由で、サファリは音に関しては非常に遅れていることがわかります。すべてのクリック、ブープ、へこみは、イベントの後、特にiOSで実際にプレーするように彼らを引き起こしたイベントの後、顕著な時間がかかります。それは契約を破るものであり、ウサギの穴は私がかなりの時間を絶望的にトンネリングして過ごしました。
幸いなことに、クロスプラットフォームのサウンドの問題を非常に簡単に解決するhowler.jsというライブラリを見つけました(そして、それも楽しい小さなロゴを持っています)。 Howlerを依存関係としてインストールし、すべてのアプリのサウンドを実行するだけで、基本的に1行または2行のコードを実行するだけで、問題を解決するのに十分でした。
Sypariの問題が何であるか、 Howlerがそれを解決する方法がわからないので、Howlerを使用することを強くお勧めします。私が試したことは何もなかったので、オーバーヘッドやコードの変更が非常に少ないため、問題を簡単に解決しただけでうれしいです。
Quinaは特に最初は難しいゲームになる可能性があるため、個人的な好みに合わせてゲームの難しさを調整する方法はいくつかあります。
これらの設定により、さまざまなスキル、年齢、および/または英語の習熟度を持つプレイヤーが自分のレベルでゲームをプレイできます。 (強力なヒントを備えた基本的な単語セットは最も簡単です。ヒントがないことが最も難しいか、トリッキーまたはランダムになります。)
調整可能な難易度で一連の一連のゲームをプレイするだけで十分に楽しいかもしれませんが、実際の本格的なゲームというよりも標準的なWebアプリやデモのように感じるでしょう。したがって、そのネイティブアプリの感触の追求に合わせて、Quinaはゲームの歴史を追跡し、プレイ統計をさまざまな方法で示し、さまざまな成果にいくつかの「賞」を提供します。
ボンネットの下では、各ゲームは次のようなオブジェクトとして保存されます。
{ 推測:3、 難易度:「トリッキー」、 勝利:本当、 ヒント:「なし」、 }
このアプリは、ゲームの歴史的な配列の形式でプレイしたゲームをカタログにします(再び、Vuex StateがLocalStorageに同期されます)。その後、アプリはあなたの統計を表示するために使用します。
これはすべて、さまざまなVuexゲッターで十分に簡単に行われます。それぞれがGameHistoryアレイに.filter()や.reduce()などのJavaScriptアレイメソッドを使用します。たとえば、これはユーザーが「トリッキーな」設定でプレイしている間に勝ったゲームの数を示すゲッターです。
// store/getters.js TrickyGamesWon(STATE){ return state.gamehistory.filter( (game)=> game.win && game.difficulty === 'Tricky' )。長さ }、
さまざまな複雑さの他にも多くのゲッターがあります。 (ユーザーの最長の勝利連勝を決定したのは、特にgnarlyでした。)
賞を追加することは、それぞれが特定のVuex Getterに結び付けられ、それぞれが要件を持つ一連の賞のオブジェクトを作成することです。その賞がロック解除された時期を示す責任(つまり、ゲッターによって返された値が十分に高い)。これがサンプルです:
// assets/js/awards.js デフォルトのエクスポート[ { タイトル: '開始'、 要件: { ゲッター:「TotalGamesPlayed」、 しきい値:1、 テキスト:「Quinaの最初のゲームをする」、 } }、 { タイトル:「シャープ」、 要件: { ゲッター:「TrickyGamesWon」、 しきい値:10、 テキスト:「トリッキーで合計10ゲームに勝つ」、 }、 }、 ]
そこから、VUEテンプレートファイルの成果をループするという非常に簡単な問題であり、その要件を使用して最終出力を取得するために最終的な出力を取得します(ただし、賞の達成に向けたユーザーの進捗状況を示すためにゲージを埋めるために数学とアニメーションがかなりあります):
特定の数のゲームに勝つ、すべてのゲームモードを試し、最初の3つの推測内でゲームに勝つなど、さまざまな成果について、すべての賞(テーマに合わせて5×5)があります。 (それは「ラッキー」と呼ばれます。追加の小さなイースターエッグとして、各賞の名前は潜在的なコードワード、つまり、繰り返しのない5文字です。)
ロック解除賞は、自慢する権利を与える以外は何もしませんが、それらのいくつかは達成するのがかなり困難です。 (リリースしてから数週間かかりました。
「一度ビルド、どこにでも展開する」戦略については好きなことがたくさんありますが、いくつかの欠点もあります。
「Web用のビルド、Everywhere Where Where Where」を作る多くのテクノロジーがあります。
一般的に、これらは2つのカテゴリに要約されます。
最初のアプローチは、2つの中でより望ましいように思えるかもしれません(その最後に、理論的には「実際の」ネイティブアプリで終わるため)が、最大のハードルが付属していることがわかりました。すべてのプラットフォームまたは製品では、物事を行う方法を学ぶ必要があり、その方法はそれ自体がエコシステム全体とフレームワークになることになります。 「ただあなたが知っていることを書く」という約束は、私の経験において非常に強い誇張です。 1年か2年で、これらの問題の多くが解決されると思いますが、今のところ、Webコードの作成とネイティブアプリの出荷との間にかなりのギャップがあると感じています。
一方、2番目のアプローチは「TWA」と呼ばれるもののために実行可能です。これが、そもそもウェブサイトをアプリにすることを可能にします。
TWAは信頼できるWebアクティビティの略です。その答えはまったく役に立たない可能性が高いので、もう少し分解しましょう。
TWAアプリは、基本的に、Webサイト(またはWebアプリ、髪を分割する場合はWebアプリ)をネイティブアプリに変換します。
TWAアプリは変装したブラウザと考えることができます。これは、Webブラウザーを除き、内部のないAndroidアプリです。 TWAアプリは特定のWeb URLを指しており、アプリが通常のネイティブアプリのものを実行するのではなく、アプリが起動するたびに、代わりにそのWebサイトをロードするだけです。フルスクリーン、ブラウザーコントロールなしで、Webサイトを効果的に見た目と振る舞いを本物のネイティブアプリであるかのようにします。
ネイティブアプリでWebサイトをまとめるという魅力を見るのは簡単です。ただし、古いサイトやURLだけではありません。 TWAネイティブアプリとしてWebサイト/アプリを起動するには、次のボックスを確認する必要があります。
その最後のポイントは、「信頼できる」部分が登場する場所です。 TWAアプリは独自のキーをチェックし、Webアプリのキーが一致していることを確認して、適切なサイトをロードしていることを確認します(おそらく、アプリURLの悪意のあるハイジャックを防ぐため)。キーが一致しない、または見つけられない場合でも、アプリは機能しますが、TWA機能はなくなります。プレーンブラウザ、Chrome、およびすべてにWebサイトをロードするだけです。したがって、このキーは、アプリの経験にとって非常に重要です。 (それは重要な部分だと言えます。ごめんなさい。申し訳ありません。)
TWAアプリの主な利点は、コードをまったく変更する必要がないということです。通常のようなWebサイト/Webアプリを構築するだけで、それが完了したら、基本的にアプリコードも完了しました。
ただし、主な欠点は、(WebとJavaScriptの現代の到来を告げるのに役立つにもかかわらず)、AppleはTWAアプリを支持していないことです。 Apple App Storeにリストすることはできません。 Google Playのみ。
これは契約を破るように聞こえるかもしれませんが、いくつかのことを念頭に置いてください:
この時点で、このTWAビジネスはすべてうまく聞こえますが、実際にサイト/アプリを実際に採用してAndroidアプリに押し込むにはどうすればよいですか?
答えは、BubbleWrapと呼ばれる素敵な小さなCLIツールの形で来ます。
BubbleWrapは、入力とオプションを取得し、Androidアプリ(具体的には、Google Playストアで許可されているファイル形式の1つ)を入力から生成するツールと考えることができます。
BubbleWrapをインストールするのは少し難しいです。それを使用することはまったくプラグアンドプレイではありませんが、私が見つけた他の比較可能なオプションよりも平均的なフロントエンド開発の手の届くところに間違いなくあります。 BubbleWrapのNPMページのREADMEファイルは詳細に説明しますが、簡単な概要として:
NPM I -G @BubbleWrap/CLIを実行してBubbleWrapをインストールします(ここでは、NPMに精通し、コマンドラインを介してパッケージをインストールしています)。これにより、どこでもバブルラップを使用できます。
インストールされたら、実行します。
bubblewrap init-manifest https://your-webapp-domain/manifest.json
注: Manifest.jsonファイルはすべてのPWAに必要であり、BubbleWrapはアプリだけでなく、そのファイルにURLが必要です。また、注意してください:マニフェストファイルの生成方法によっては、その名前は各ビルドに固有の場合があります。 (たとえば、NUXTのPWAモジュールは、ファイル名に一意のUUIDを追加します。)
また、デフォルトでは、BubbleWrapは、このプロセスの一部としてWebアプリが有効なPWAであることを検証することに注意してください。何らかの理由で、私がこのプロセスを経験していたとき、Lighthouseは実際には完全に機能的なプログレッシブWebアプリであることを確認しているにもかかわらず、チェックはネガティブに戻り続けました。幸いなことに、BubbleWrapを使用すると、-skippwavalidationフラグでこのチェックをスキップできます。
BubbleWrapを使用するのが初めての場合、Java Development Kit(JDK)とAndroid Software Development Kit(SDK)をインストールしたいかどうかを尋ねます。これら2つは、Androidアプリを生成するために必要な舞台裏のユーティリティです。わからない場合は、「Y」を「はい」を押してください。
注: BubbleWrapは、これら2つの開発キットが非常に特定の場所に存在することを期待しており、そこにいない場合は適切に機能しません。 BubbleWrap Doctorを実行して確認するか、完全なBubbleWrap Cli Readmeを確認できます。
すべてがインストールされた後、提供されたURLでmanifest.jsonファイルを見つけたと仮定すると、バブルワラップはアプリについていくつかの質問をします。
質問の多くは、好み(アプリのメイン色など)か、基本的な詳細(アプリのドメインやエントリポイントなど)を確認するだけで、ほとんどはサイトのマニフェストファイルから事前に入力されます。
マニフェストが既に事前に入力している可能性のあるその他の質問には、アプリのさまざまなアイコンを見つける場所(ホーム画面アイコン、ステータスバーアイコンなどとして使用する)、アプリの開いたときにスプラッシュ画面の色、およびポートレートまたはランドスケープを強制したい場合に備えて、アプリの画面の向きが含まれます。また、BubbleWrapは、ユーザーのジオロケーションの許可を要求するかどうか、およびプレイ請求を選択するかどうかを尋ねます。
ただし、少し混乱するかもしれないいくつかの重要な質問があるので、ここでそれらをカバーしましょう。
パズルの最後のピースは署名キーです。これが最も重要な部分です。このキーは、プログレッシブWebアプリをこのAndroidアプリに接続するものです。アプリが期待しているキーがPWAで見つかったものと一致しない場合、再び:アプリはまだ機能しますが、ユーザーが開いたときにネイティブアプリのようには見えません。通常のブラウザウィンドウになります。
ここには、詳細には少し複雑すぎるアプローチが2つありますが、いくつかのポインターを与えようとします。
どちらのオプションを選択しても、ここで署名するアプリに関する詳細なドキュメントがあります(Androidアプリ向けに書かれていますが、そのほとんどはまだ関連しています)。
個人サイトにキーを取得する部分については、Androidアプリリンクを検証するためのこのガイドで説明されています。大まかに要約すると、Googleはサイトの正確なパスで /.well-known/assetlinks.jsonファイルを探します。ファイルには、一意のキーハッシュと他のいくつかの詳細が含まれる必要があります。
[{ 「関係」:["Delegate_permission/common.handle_all_urls"]、 「ターゲット」:{"namespace": "android_app"、 "package_name": "your.app.id" 「sha256_cert_fingerprints ":[" your:unique:hash:here "]} }]
始める前に、物事のアプリストア側には注意すべきハードルもいくつかあります。
While my goal with Quina was mostly personal — challenge myself, prove I could, and learn more about the Vue ecosystem in a complex real-world app — I had also hoped as a secondary goal that my work might be able to make a little money on the side for me and my family.
あまりありません。 I never had illusions of building the next Candy Crush (nor the ethical void required to engineer an addiction-fueled micro-transaction machine). But since I had poured hundreds of hours of my time and energy into the game, I had hoped that maybe I could make something in return, even if it was just a little beer money.
Initially, I didn't love the idea of trying to sell the app or lock its content, so I decided to add a simple “would you care to support Quina if you like it?” prompt after every so many games, and make some of the content unlockable specifically for supporters. (Word sets are limited in size by default, and some game settings are initially locked as well.) The prompt to support Quina can be permanently dismissed (I'm not a monster), and any donation unlocks everything; no tiered access or benefits.
This was all fairly straightforward to implement thanks to Stripe, even without a server; it's all completely client-side. I just import a bit of JavaScript on the /support page, using Nuxt's handy head function (which adds items to the
element specifically on the given page):// pages/support.vue 頭() { 戻る { script: [ { hid: 'stripe', src: 'https://js.stripe.com/v3', defer: true, callback: () => { // Adds all Stripe methods like redirectToCheckout to page component this.stripe = Stripe('your_stripe_id') }、 }、 ]、、 } }、
With that bit in place (along with a sprinkle of templating and logic), users can choose their donation amount — set up as products on the Stripe side — and be redirected to Stripe to complete payment, then returned when finished. For each tier, the return redirect URL is slightly different via query parameters. Vue Router parses the URL to adjust the user's stored donation history, and unlock features accordingly.
You might wonder why I'm revealing all of this, since it exposes the system as fairly easy to reverse-engineer. The answer is: I don't care . In fact, I added a free tier myself, so you don't even have to go to the trouble. I decided that if somebody really wanted the unlockables but couldn't or wouldn't pay for whatever reason, that's fine. Maybe they live in a situation where $3 is a lot of money. Maybe they gave on one device already. Maybe they'll do something else nice instead. But honestly, even if their intentions aren't good: so what?
I appreciate support, but this isn't my living, and I'm not trying to build a dopamine tollbooth. Besides, I'm not personally comfortable with the ethical implications of using a stack of totally open-source and/or free software (not to mention the accompanying mountain of documentation, blog posts, and Stack Overflow answers written about all of it) to build a closed garden for personal profit.
So, if you like Quina and can support it: sincerely, thank you . That means a ton to me. I love to see my work being enjoyed. But if not: that's cool. If you want the “free” option, it's there for you.
Anyway, this whole plan hit a snag when I learned about Google Play's new monetization policy, effective this year. You can read it yourself, but to summarize: if you make money through a Google Play app and you're not a nonprofit, you gotta go through Google Pay and pay a hefty fee — you are not allowed to use any other payment provider.
This meant I couldn't even list the app; it would be blocked just for having a “support” page with payments that don't go through Google. (I suppose I probably could have gotten around this by registering a nonprofit, but that seemed like the wrong way to go about it, on a number of levels.)
My eventual solution was to charge for the app itself on Google Play, by listing it for $2.99 (rather than my previously planned price of “free”), and simply altering the app experience for Android users accordingly.
Fortunately enough, Android apps send a custom header with the app's unique ID when requesting a website. Using this header, it was easy enough to differentiate the app's experience on the web and in the actual Android app.
For each request, the app checks for the Android ID; if present, the app sets a Vuex state boolean called isAndroid to true. This state cascades throughout the app, working to trigger various conditionals to do things like hide and show various FAQ questions, and (most importantly) to hide the support page in the nav menu. It also unlocks all content by default (since the user's already “donated” on Android, by purchasing). I even went so far as to make simple
<!-- /src/components/AndroidOnly.vue --> <div v-if="isAndroid"> </div> テンプレート> デフォルトのエクスポート{ computed: { isAndroid() { return this.$store.state.isAndroid }、 }、 }
For a time while building Quina, I had Firebase set up for logins and storing user data. I really liked the idea of allowing users to play on all their devices and track their stats everywhere, rather than have a separate history on each device/browser.
In the end, however, I scrapped that idea, for a few reasons. One was complexity; it's not easy maintaining a secure accounts system and database, even with a nice system like Firebase, and that kind of overhead isn't something I took lightly. But mainly: the decision boiled down to security and simplicity.
At the end of the day, I didn't want to be responsible for users' data. Their privacy and security is guaranteed by using localStorage, at the small cost of portability. I hope players don't mind the possibility of losing their stats from time to time if it means they have no login or data to worry about. (And hey, it also gives them a chance to earn those awards all over again.)
Plus, it just feels nice. I get to honestly say there's no way my app can possibly compromise your security or data because it knows literally nothing about you. And also, I don't need to worry about compliance or cookie warnings or anything like that, either.
Building Quina was my most ambitious project to date, and I had as much fun designing and engineering it as I have seeing players enjoy it.
I hope this journey has been helpful for you! While getting a web app listed in the Google Play Store has a lot of steps and potential pitfalls, it's definitely within reach for a front-end developer. I hope you take this story as inspiration, and if you do, I'm excited to see what you build with your newfound knowledge.
以上がGoogle Playでnuxtを使用してワードゲームアプリを構築したことを学んだことの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。