Je suis tombé amoureux du codage au moment où j'ai créé mon premier effet CSS: Hover. Des années plus tard, cette piqûre initiale dans l'interactivité sur le Web m'a conduit à un nouvel objectif: faire un jeu.
Ces premiers moments jouant avec: Hover n'étaient rien de spécial, ni même utile. Je me souviens avoir fait une grille réactive de carrés bleus (fait avec du flotteur, si cela vous donne une idée de la chronologie), chacun devenu orange lorsque le curseur s'est déplacé sur eux. J'ai passé ce qui ressemblait à des heures à soutenir les boîtes, à redimensionner la fenêtre pour les regarder changer la taille et l'alignement, puis tout recommencer. C'était comme une pure magie.
Ce que j'ai construit sur le Web est naturellement devenu plus complexe que cette grille d'éléments
Parfois, c'était juste une démo de codepen; Parfois, c'était un petit projet parallèle déployé sur Vercel ou Netlify. J'ai adoré le défi de recréer des jeux comme Color Flood, Hangman ou Rendre quatre dans un navigateur.
Après un certain temps, cependant, le but est devenu plus grand: que se passe-t-il si je faisais un match réel ? Pas seulement une application Web; Un véritable jeu en direct, honnête à la bonne et au téléchargement, au téléchargement. En août dernier, j'ai commencé à travailler sur mon projet le plus ambitieux à ce jour, et quatre mois plus tard, je l'ai publié au monde ( lire: je suis fatigué de jouer avec lui ): une application de jeu de mots que j'appelle Quina.
La façon la plus simple d'expliquer Quina est: c'est le cerveau, mais avec des mots à cinq lettres. En fait, Mastermind est en fait une version d'un jeu classique stylo et papier; Quina est simplement une autre variation de ce même jeu original.
L'objet de Quina est de deviner un mot secret à cinq lettres. Après chaque supposition, vous obtenez un indice qui vous indique à quel point votre supposition est proche du mot de code. Vous utilisez cet indice pour affiner votre prochaine supposition, etc., mais vous n'obtenez que dix suppositions totales; Courez et vous perdez.
Le nom «Quina» est né parce que cela signifie «cinq à la fois» en latin (ou du moins me l'a dit, de toute façon). Le jeu traditionnel est généralement joué avec des mots à quatre lettres, ou parfois à quatre chiffres (ou dans le cas de cerveau, quatre couleurs); Quina utilise des mots à cinq lettres sans lettres répétées, il semblait donc approprié que le jeu ait un nom qui joue selon ses propres règles. (Je ne sais pas comment le mot latin original a été prononcé, mais je le dis "Quinn-ah", ce qui est probablement faux, mais bon, c'est mon jeu, non?)
J'ai passé mes soirées et mes week-ends au cours d'environ quatre mois à construire l'application. J'aimerais passer cet article à parler de la technologie derrière le jeu, des décisions impliquées et des leçons apprises en cas de route que vous souhaitez voyager vous-même.
Je suis un grand fan de Vue et je voulais utiliser ce projet comme un moyen d'élargir ma connaissance de son écosystème. J'ai envisagé d'utiliser un autre framework (j'ai également construit des projets dans Svelte et React), mais je me suis senti que Nuxt a frappé le point idéal de la familiarité, de la facilité d'utilisation et de la maturité. (Au fait, si vous ne saviez pas ou si vous n'aviez pas deviné: Nuxt pourrait être décrit équitablement comme l'équivalent Vue de Next.js.)
Je n'étais pas allé trop profondément avec Nuxt auparavant; Juste quelques très petites applications. Mais je savais que Nuxt peut compiler avec une application statique, ce qui est exactement ce que je voulais - pas de serveurs (nœud) à craindre. Je savais également que Nuxxt pouvait gérer le routage aussi facilement que la suppression des composants Vue dans le dossier A / Pages, ce qui était très attrayant.
De plus, bien que Vuex (la direction officielle de l'État de Vue) ne soit pas terriblement complexe en soi, j'ai apprécié la façon dont Nuxt ajoute un peu de sucre pour le rendre encore plus simple. (Nuxt facilite les choses de diverses manières, comme ne vous oblige pas à importer explicitement vos composants avant de pouvoir les utiliser; vous pouvez simplement les mettre dans le balisage et Nuxt le comprendra et l'importance automatique selon les besoins.)
Enfin, je savais qu'à l'avance, je construisais une application Web progressive (PWA), donc le fait qu'il existe déjà un module PWA NUXT pour aider à développer toutes les fonctionnalités impliquées (comme un employé de service pour une capacité hors ligne) déjà emballée et prête à partir était un gros tirage. En fait, il y a un ensemble impressionnant de modules Nuxxt disponibles pour tous les obstacles invisibles. Cela a fait de Nuxt le choix le plus facile et le plus évident, et je n'ai jamais regretté.
J'ai fini par utiliser davantage de modules au fur et à mesure que je suis allé, y compris le module de contenu Nuxt Stellar, qui vous permet d'écrire du contenu de la page dans Markdown, ou même un mélange de composants Markdown et Vue. J'ai utilisé cette fonctionnalité pour la page "FAQ" et la page "Comment jouer" (car l'écriture dans Markdown est tellement plus agréable que les pages HTML codant en dur).
Quina finirait par trouver une maison sur le Google Play Store, mais peu importe comment ou où il était joué, je voulais qu'il se sente comme une application à part entière dès le départ.
Pour commencer, cela signifiait un mode sombre en option et un paramètre pour réduire le mouvement pour une convivialité optimale, comme de nombreuses applications natives l'ont fait (et dans le cas d'un mouvement réduit, comme tout ce qui devrait avoir des animations).
Sous le capot, les deux paramètres sont finalement des booléens dans le magasin de données Vuex de l'application. Lorsque cela est vrai, le paramètre rend une classe spécifique dans la disposition par défaut de l'application. Les dispositions Nuxxt sont des modèles Vue qui «enveloppent» tout votre contenu et rendent sur toutes les (ou plusieurs) pages de votre application (couramment utilisées pour des choses comme les en-têtes et les pieds partagés, mais également utiles pour les paramètres globaux):
<mpelate> <div :> <nuxt></nuxt> </div> template> <cript> import {mapgetters} à partir de 'Vuex' Exporter par défaut { calculé: { ... mapgetters (['darkmode', 'reducemotion']), }, // autre code de composant de mise en page ici } </cript></mpelate>
En parlant de paramètres: bien que l'application Web soit divisée en plusieurs pages différentes - menu, paramètres, sur, play, etc. - le magasin de données Vuex partagé aide à maintenir les choses en synchronisation et à se sentir transparente entre les zones de l'application (puisque l'utilisateur ajustera ses paramètres sur une page et les voir s'appliquer au jeu à un autre).
Chaque paramètre de l'application est également synchronisé à la fois avec LocalStorage et le Vuex Store, ce qui permet d'enregistrer et de charger des valeurs entre les sessions, en plus de garder une trace des paramètres à mesure que l'utilisateur navigue entre les pages.
Et en parlant de navigation: se déplacer entre les pages est un autre domaine où je sentais qu'il y avait beaucoup d'occasions de faire en sorte que Quina se sente comme une application native, en ajoutant des transitions pleine page.
Les transitions Vue sont assez simples en général - vous écrivez simplement des cours CSS spécifiquement nommé pour vos états de transition «à» et «à partir» - mais Nuxt va plus loin et vous permet de définir des transitions pleine page avec une seule ligne dans le fichier Vue d'une page:
<cript> Exporter par défaut { Transition: 'Page-Slide' // ... le reste des propriétés des composants } </cript>
Cette propriété de transition est puissante; Il permet à Nuxt de savoir que nous voulons que la transition de glissement de page appliquée à cette page chaque fois que nous nous le navigons ou nous éloignons. De là, tout ce que nous devons faire est de définir les classes qui gèrent l'animation, comme vous le feriez avec n'importe quelle transition Vue. Voici mon SCSS de la glisse de page:
/ * actifs / css / _animations.scss * / .Page-Slide { & -enter-active { Transition: tous les 0,35 s cubic-bezier (0, 0,25, 0, 0,75); } & -Leave-activ { Transition: tous les 0,35 s cubes-Bezier (0,75, 0, 1, 0,75); } &-entrer, & -Leave-to { Opacité: 0; Transform: Translatey (1Rem); .reduce-motion & { Transformer: aucun! IMPORTANT; } } & -Leave-to { Transform: tradlatey (-1rem); } }
Remarquez la classe .reduce-Motion; C'est ce dont nous avons parlé dans le fichier de mise en page juste au-dessus. Il empêche le mouvement visuel lorsque l'utilisateur a indiqué qu'il préfère le mouvement réduit (soit via la requête multimédia, soit par un paramètre manuel), en désactivant toute propriété de transformation (qui semblait justifier l'utilisation du drapeau de division! Important). L'opacité est toujours autorisée à s'estomper, cependant, car ce n'est pas vraiment un mouvement.
Note latérale sur les transitions et la manipulation 404S: les transitions et le routage sont, bien sûr, gérés par JavaScript sous le capot (Vue Router, pour être exact), mais j'ai rencontré un problème frustrant où les scripts cesseraient de fonctionner sur des pages inactives (par exemple, si l'utilisateur a laissé l'application ou un onglet ouvert à l'arrière-plan pendant un certain temps). Lorsque vous revenons à ces pages inactives et en cliquant sur un lien, le routeur Vue aurait cessé de fonctionner, et donc le lien serait traité comme relatif et 404.
Exemple: la page / FAQ devient inactive; L'utilisateur y revient et clique sur le lien pour visiter la page / Options. L'application tenterait d'aller à / FAQ / Options, qui n'existe bien sûr pas.
Ma solution à cela était une page Error.Vue personnalisée (il s'agit d'une page Nuxt qui gère automatiquement toutes les erreurs), où j'exécuterais la validation sur le chemin entrant et rediriger vers la fin du chemin.
// Disseuts / Error.vue monté () { const lastpage = '/' this. $ Route.fullpath.split ('/'). Pop () // Ne créez pas de boucle de redirection if (lastpage! == this. $ Route.fullpath) { this. $ router.push ({ Chemin: Lastpage, }) } }
Cela a fonctionné pour mon cas d'utilisation car a) Je n'ai pas de routes imbriquées; et b) à la fin, si le chemin n'est pas valide, il frappe toujours un 404.
Les transitions sont bien, mais je savais aussi que Quina ne se sentirait pas comme une application native - en particulier sur un smartphone - sans vibration et son.
Les vibrations sont relativement faciles à réaliser dans les navigateurs de nos jours, grâce à l'API Navigator. La plupart des navigateurs modernes vous permettent simplement d'appeler Window.Navigator.Vibrate () pour donner à l'utilisateur un peu de buzz ou une série de bourdonnements - ou, en utilisant une très courte durée, un tout petit peu de commentaires tactiles, comme lorsque vous appuyez sur une clé sur un clavier de smartphone.
De toute évidence, vous voulez utiliser des vibrations avec parcimonie, pour plusieurs raisons. Tout d'abord, car trop peut facilement devenir une mauvaise expérience utilisateur; Et deuxièmement, parce que tous les appareils / navigateurs ne le prennent pas en charge, vous devez donc faire très attention à la façon et à l'endroit où vous essayez d'appeler la fonction vibrée (), de peur que vous ne provoquez une erreur qui arrête le script en cours d'exécution.
Personnellement, ma solution était de définir un Getter Vuex pour vérifier que l'utilisateur autorise les vibrations (elle peut être désactivée à partir de la page Paramètres); que le contexte actuel est le client, pas le serveur; et enfin, que la fonction existe dans le navigateur actuel. (ES2020 Le chaînage facultatif aurait également fonctionné ici pour cette dernière partie.)
// Store / Getters.js vibration (état) { si ( process.client && state.options.vibration && typeof window.navigator.vibrate! == 'Undefined' ) { Retour vrai } retourner faux },
Remarque latérale: Vérification du processus. Cela est vrai même si vous utilisez Nuxt en mode statique, car les composants sont validés dans le nœud pendant le temps de construction. Process.client (et son opposé, process.server) sont des nucés Nuxt qui valident simplement l'environnement actuel du code à l'exécution, ils sont donc parfaits pour isoler le code du navigateur uniquement.
Le son est un autre élément clé de l'expérience utilisateur de l'application. Plutôt que de faire mes propres effets (qui auraient sans aucun doute ajouté des dizaines d'heures au projet), j'ai mélangé des échantillons de quelques artistes qui savent mieux ce qu'ils font dans ce domaine et qui offrait des sons de jeu gratuits en ligne. (Voir la FAQ de l'application pour les informations complètes.)
Les utilisateurs peuvent définir le volume qu'ils préfèrent ou d'arrêter entièrement le son. Ceci, et la vibration, sont également définis dans LocalStorage sur le navigateur de l'utilisateur ainsi que synchronisé avec le magasin Vuex. Cette approche nous permet de définir un paramètre «permanent» enregistré dans le navigateur, mais sans avoir besoin de le récupérer du navigateur à chaque fois qu'il est référencé. (Les sons, par exemple, vérifient le niveau de volume actuel à chaque fois que l'on est joué, et la latence d'attendre un appel local-storage à chaque fois que cela se produit pourrait être suffisante pour tuer l'expérience.)
Il s'avère que pour une raison quelconque, Safari est extrêmement retardé en ce qui concerne le son. Tous les clics, Boops et Dings prendraient un temps notable après l'événement qui les a déclenchés pour jouer en safari, en particulier sur iOS. C'était un rupture de transactions et un terrier de lapin, j'ai passé une bonne quantité d'heures à désespérer des tunnels.
Heureusement, j'ai trouvé une bibliothèque appelée Howler.js qui résout assez facilement les problèmes sonores (et qui a également un petit logo amusant). L'installation de Hurler comme dépendance et exécutant tous les sons de l'application à travers lui - essentiellement une ou deux lignes de code - a été suffisante pour résoudre le problème.
Si vous construisez une application JavaScript avec un son synchrone, je recommanderais fortement d'utiliser Hurler, car je n'ai aucune idée du problème de Safari ou de la façon dont Hurler le résout. Rien que j'ai essayé n'a fonctionné, donc je suis heureux de simplement résoudre le problème facilement avec très peu de frais généraux ou de modification de code.
Quina peut être un jeu difficile, surtout au début, donc il y a quelques façons d'ajuster la difficulté du jeu en fonction de votre préférence personnelle:
Ces paramètres permettent aux joueurs de diverses compétences, âge et / ou en anglais de jouer au jeu à leur propre niveau. (Un ensemble de mots de base avec des indices forts serait le plus simple; délicat ou aléatoire sans indice serait le plus difficile.)
Bien que simplement jouer à une série de jeux ponctuels avec des difficultés réglables puisse être assez agréable, cela ressemblerait plus à une application Web ou une démo standard qu'un vrai jeu à part entière. Ainsi, conformément à la poursuite de cette application native, Quina suit votre historique de jeu, montre vos statistiques de jeu de différentes manières et offre plusieurs «récompenses» pour diverses réalisations.
Sous le capot, chaque jeu est enregistré comme un objet qui ressemble à quelque chose comme ceci:
{ deviner: 3, Difficulté: «délicate», gagner: vrai, Astuce: «Aucun», }
L'application catalogue que vos jeux ont joué (encore une fois, via Vuex State synchronisé avec LocalStorage) sous la forme d'un tableau de jeu d'objets de jeu, que l'application utilise ensuite pour afficher vos statistiques - telles que votre rapport gagnant / perte, le nombre de jeux que vous avez joué et vos suppositions moyennes - ainsi que pour montrer votre progression vers les «récompenses» du jeu.
Tout cela se fait assez facilement avec divers Getters Vuex, chacun utilise des méthodes de tableau JavaScript, comme .Filter () et .reduce (), sur le tableau de jeu. Par exemple, c'est le Getter qui montre le nombre de jeux que l'utilisateur a gagné en jouant sur le paramètre «délicat»:
// Store / Getters.js TrickyGameswon (état) { return state.gamehistory.filter ( (jeu) => game.win && game.difficulty === 'Tricky' ).longueur },
Il existe de nombreux autres getters d'une complexité variable. (Celui pour déterminer la plus longue séquence de victoires de l'utilisateur a été particulièrement noueux.)
L'ajout de récompenses a été une question de créer un tableau d'objets de récompense, chacun lié à un Getter Vuex spécifique, et chacun avec une propriété exigence. Lethershold indiquant quand cette attribution a été déverrouillée (c'est-à-dire lorsque la valeur renvoyée par le Getter était suffisamment élevée). Voici un échantillon:
// Assets / JS / Awards.js Exporter par défaut [ { Titre: «début», exigence: { Getter: «TotalGamesplayed», Seuil: 1, Texte: «Jouez votre premier jeu de Quina», } }, { Titre: «Sharp», exigence: { Getter: «Trickygameswon», Seuil: 10, Texte: «Gagnez dix matchs au total sur Tricky», }, }, ]]
À partir de là, il s'agit d'une question assez simple de boucler sur les réalisations dans un fichier de modèle VUE pour obtenir la sortie finale, en utilisant sa propriété exigence.Text (bien qu'il y ait beaucoup de mathématiques et d'animation ajoutées pour remplir les jauges pour montrer les progrès de l'utilisateur vers la récompense):
Il y a 25 récompenses en tout (c'est 5 × 5, conformément au thème) pour diverses réalisations comme gagner un certain nombre de jeux, essayer tous les modes de jeu ou même gagner un jeu dans vos trois premières suppositions. (Celui qui est appelé «chanceux» - en tant que petit œuf de Pâques supplémentaire, le nom de chaque prix est également un mot de code potentiel, c'est-à-dire cinq lettres sans répétition.)
Le déverrouillage des prix ne fait rien, sauf vous donner des droits de vantardise, mais certains d'entre eux sont assez difficiles à réaliser. (Il m'a fallu quelques semaines après la sortie pour les obtenir tous!)
Il y a beaucoup à aimer dans la stratégie «Build une fois, déploie partout», mais il est également livré avec quelques inconvénients:
Il y a beaucoup de technologies qui font la promesse «Build for the Web, Release Everywhere» - React Native, Cordova, Ionic, Meteor et Nativescript, pour n'en nommer que quelques-uns.
Généralement, cela se résume à deux catégories:
La première approche peut sembler la plus désirable des deux (car à la fin de cela, vous vous retrouvez théoriquement avec une «vraie» application native), mais j'ai également trouvé que cela s'accompagne des plus gros obstacles. Chaque plate-forme ou produit vous oblige à apprendre sa façon de faire les choses, et de cette façon est forcément tout un écosystème et un cadre en soi. La promesse de «écrire ce que vous savez» est une surestimation assez forte dans mon expérience. Je suppose que dans un an ou deux, beaucoup de ces problèmes seront résolus, mais en ce moment, vous ressentez toujours un écart considérable entre la rédaction du code Web et l'expédition d'une application native.
D'un autre côté, la deuxième approche est viable à cause d'une chose appelée «TWA», ce qui permet de faire un site Web en un site Web en premier lieu.
TWA signifie une activité Web de confiance - et comme cette réponse ne sera probablement pas du tout utile, décomposons un peu plus, n'est-ce pas?
Une application TWA transforme essentiellement un site Web (ou une application Web, si vous souhaitez diviser les cheveux) en une application native, avec l'aide d'une petite supercherie d'interface utilisateur.
Vous pourriez penser à une application TWA comme un navigateur déguisé . Il s'agit d'une application Android sans internes, à l'exception d'un navigateur Web. L'application TWA est indiquée vers une URL Web spécifique, et chaque fois que l'application est démarrée, plutôt que de faire des trucs d'applications natifs normaux, il charge simplement ce site Web à la place - à l'écran complet, sans contrôle de navigateur, ce qui rend le site Web et se comporte comme s'il s'agissait d'une application native à part entière.
Il est facile de voir l'attrait de terminer un site Web dans une application native. Cependant, pas n'importe quel ancien site ou URL se qualifie; Afin de lancer votre site Web / application en tant qu'application native TWA, vous devrez cocher les cases suivantes:
Ce dernier point est l'endroit où se trouve la partie «de confiance»; Une application TWA vérifiera sa propre clé, puis vérifiera que la clé de votre application Web correspond, pour s'assurer qu'elle charge le bon site (vraisemblablement, pour empêcher le détournement malveillant des URL de l'application). Si la clé ne correspond pas ou n'est pas trouvée, l'application fonctionnera toujours, mais la fonctionnalité TWA sera partie; Il chargera simplement le site Web dans un navigateur ordinaire, Chrome et tout. La clé est donc extrêmement importante pour l'expérience de l'application. (On pourrait dire que c'est un élément clé . Désolé pas désolé.)
Le principal avantage d'une application TWA est qu'il ne vous oblige pas du tout à modifier votre code - pas de cadre ni de plate-forme pour apprendre; Vous créez simplement une application site Web / Web comme Normal, et une fois que vous l'avez fait, vous avez également fait le code d'application.
Le principal inconvénient , cependant, est que (malgré le fait d'aider à inaugurer l'ère moderne du Web et du JavaScript), Apple n'est pas en faveur des applications TWA; Vous ne pouvez pas les énumérer dans l'App Store d'Apple. Seul Google Play.
Cela peut ressembler à un discours, mais gardons quelques choses à l'esprit:
À ce stade, vous dites peut-être que cette entreprise TWA sonne bien, mais comment prendre mon site / application et le pousser dans une application Android?
La réponse se présente sous la forme d'un charmant petit outil CLI appelé Bubblewrap.
Vous pouvez considérer Bubblewrap comme un outil qui prend des entrées et des options de votre part, et génère une application Android (en particulier, un APK, l'un des formats de fichiers autorisés par le Google Play Store) hors de l'entrée.
L'installation de Bubblewrap est un peu délicate, et bien que l'utilisation ne soit pas tout à fait plug-and-play, il est certainement beaucoup plus à portée de main pour un développeur frontal moyen que toutes les autres options comparables que j'ai trouvées. Le fichier ReadMe sur la page NPM de Bubblewrap entre dans les détails, mais comme bref aperçu:
Installez Bubblewrap en exécutant NPM I -g @ bubblewrap / CLI (je suppose ici que vous connaissez NPM et en installant des packages via la ligne de commande). Cela vous permettra d'utiliser Bubblewrap n'importe où.
Une fois installé, vous courerez:
bubblewrap init --manifest https: //your-webapp-domain/manifest.json
Remarque: Le fichier manifeste.json est requis de tous les PWA, et Bubblewrap a besoin de l'URL de ce fichier, pas seulement de votre application. Soyez également averti: selon la façon dont votre fichier manifeste est généré, son nom peut être unique à chaque version. (Le module PWA de Nuxt ajoute un UUID unique au nom du fichier, par exemple.)
Notez également que par défaut, Bubblewrap validera que votre application Web est un PWA valide dans le cadre de ce processus. Pour une raison quelconque, lorsque je traversais ce processus, le chèque a continué de revenir négatif, malgré le phare confirmant qu'il s'agissait en fait d'une application Web progressive entièrement fonctionnelle. Heureusement, Bubblewrap vous permet de sauter ce chèque avec le drapeau - skippwavalidation.
Si c'est la première fois que vous utilisez Bubblewrap, il vous demandera alors si vous souhaitez qu'il installe le kit de développement Java (JDK) et le kit de développement de logiciels Android (SDK) pour vous. Ces deux sont les utilitaires en coulisses nécessaires pour générer une application Android. Si vous n'êtes pas sûr, frappez «Y» pour oui.
Remarque: Bubblewrap s'attend à ce que ces deux kits de développement existent dans des endroits très spécifiques et ne fonctionneront pas correctement s'ils ne sont pas là. Vous pouvez exécuter Bubblewrap Doctor pour vérifier, ou voir le Readme CLI Full Bubblewrap.
Une fois que tout a été installé - en supposant qu'il trouve votre fichier manifeste.json sur l'URL fournie - Bubblewrap posera quelques questions sur votre application.
Beaucoup de questions sont soit la préférence (comme la couleur principale de votre application) ou simplement confirmer les détails de base (comme le domaine et le point d'entrée de l'application), et la plupart seront pré-remplies à partir du fichier manifeste de votre site.
D'autres questions qui peuvent déjà être pré-remplies par votre manifeste comprennent où trouver les différentes icônes de votre application (à utiliser comme icône d'écran d'accueil, l'icône de la barre d'état, etc.), quelle couleur l'écran Splash devrait être pendant l'ouverture de l'application et l'orientation de l'écran de l'application, au cas où vous souhaitez forcer le portrait ou le paysage. Bubblewrap vous demandera également si vous souhaitez demander l'autorisation de la géolocalisation de votre utilisateur et si vous optez pour la facturation de jeu.
Cependant, il y a quelques questions importantes qui peuvent être un peu déroutantes, alors couvrons-les ici:
La dernière pièce du puzzle est la clé de signature. C'est la partie la plus importante . Cette clé est ce qui connecte votre application Web progressive à cette application Android. Si la clé que l'application attend ne correspond pas à ce qui se trouve dans votre PWA, encore une fois: votre application fonctionnera toujours, mais elle ne ressemblera pas à une application native lorsque l'utilisateur l'ouvre; Ce sera juste une fenêtre de navigateur normale.
Il y a deux approches ici qui sont un peu trop complexes pour entrer en détail, mais je vais essayer de donner quelques conseils:
Quelle que soit l'option que vous choisissez, il y a une documentation approfondie sur la signature des applications ici (écrite pour les applications Android, mais la plupart sont toujours pertinentes).
La partie où vous obtenez la clé sur votre site personnel est couverte dans ce guide pour vérifier les liens d'application Android. Pour résumer grossièrement: Google recherchera un fichier a /.well-known/assetlinks.json sur ce chemin exact sur votre site. Le fichier doit contenir votre hachage de clé unique ainsi que quelques autres détails:
[{ "relation": ["Delegate_permission / Common.handle_all_urls"], "Target": {"namespace": "android_app", "package_name": "your.app.id", "SHA256_CERT_FINGERPRINTS": ["votre: Unique: Hash: ici"]} }]
Avant de commencer, il y a aussi quelques obstacles à connaître du côté des choses:
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.
Not a lot. 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 tête() { retour { scénario: [ { 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 --> <template> <div v-if="isAndroid"> <slot></slot> </div> </template> <script> export default { computed: { isAndroid() { return this.$store.state.isAndroid }, }, } </script>
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.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!