CloudFlare propose un excellent produit appelé Workers KV, une couche de stockage à valeur clé à l'échelle mondiale. Il peut gérer des millions de clés, chacun étant accessible avec une latence extrêmement faible dans les scripts de travail, peu importe d'où vient la demande. Les travailleurs KV sont incroyables - il est également à un prix, y compris un niveau gratuit généreux.
Cependant, en tant qu'utilisateur à long terme de la gamme de produits CloudFlare, j'ai trouvé qu'une chose manquait: l'introspection locale . J'ai souvent des milliers, voire des centaines de milliers de clés dans mon application et je veux souvent avoir un moyen d'interroger toutes les données, de les trier ou simplement de voir ce qui existe réellement.
Récemment, j'ai eu l'honneur de rejoindre Cloudflare! Plus important encore, j'ai rejoint ce trimestre avant la «Wicl Win Week» (alias The Weekly Hackathon). Comme je n'ai pas accumulé suffisamment de backlog de travail (pas encore), croyez-moi, je saisirai cette occasion pour répondre à mes souhaits.
Retour au point, permettez-moi de vous dire comment j'ai construit les travailleurs KV GUI, une application de bureau multiplateforme construite à l'aide de Svelte, Redis et Rust.
En tant que développeur Web, il s'agit d'un lien familier. J'aimerais l'appeler la "partie simple", mais étant donné que vous pouvez utiliser tous les frameworks, bibliothèques ou modèles HTML, CSS et JavaScript, le choix de la paralysie est facile de se produire… il peut également être familier. Si vous avez une pile technologique frontale que vous aimez, c'est super, utilisez-le! Pour cette application, j'ai choisi d'utiliser Svelte parce que pour moi, cela rend les choses simples et les permet de les garder simples.
De plus, en tant que développeurs Web, nous voulons transporter tous les outils avec nous. Bien sûr, vous pouvez! Encore une fois, cette phase du projet n'est pas différente d'un cycle de développement des applications Web typique. Vous pouvez vous attendre à exécuter Yarn Dev (ou à une variante) comme votre commande principale et à vous sentir chez vous. Pour garder le thème "simple", j'ai choisi d'utiliser Sveltekit, le cadre officiel de Svelte et la boîte à outils pour la création d'applications. Il comprend un système de construction optimisé, une excellente expérience de développeur (y compris HMR!), Un routeur basé sur le système de fichiers et toutes les fonctionnalités que Svelte elle-même offre.
En tant que cadre, en particulier celui qui prend soin des outils moi-même, Sveltekit me permet de penser uniquement à mon application et à ses besoins. En fait, la seule chose que je dois faire en termes de configuration est de dire à Sveltekit que je souhaite créer une seule application (SPA) qui s'exécute uniquement sur le client. En d'autres termes, je dois explicitement retirer Sveltekit en supposant que je veux un serveur, ce qui est en fait une hypothèse raisonnable, car la plupart des applications peuvent bénéficier d'un rendu côté serveur. C'est aussi simple que de fixer le package @ sveltejs / adaptateur-statique, un préréglage de configuration créé spécifiquement à cet effet. Après l'installation, mon fichier de configuration entier ressemble à ceci:
<code>// svelte.config.js import preprocess from 'svelte-preprocess'; import adapter from '@sveltejs/adapter-static'; /** @type {import('@sveltejs/kit').Config} */ const config = { preprocess: preprocess(), kit: { adapter: adapter({ fallback: 'index.html' }), files: { template: 'src/index.html' } }, }; export default config;</code>
Les modifications à index.html sont ma préférence personnelle. Sveltekit utilise App.html comme modèle de base par défaut, mais les anciennes habitudes sont difficiles à modifier.
En quelques minutes seulement, ma chaîne d'outils savait déjà qu'elle construisait un spa, avait déjà un routeur prêt et un serveur de développement prêt à être disponible. De plus, la prise en charge de TypeScript, PostCSS et / ou SASS est également disponible en raison de Svelte-Preprocess si je veux (et je le veux). Prêt!
L'application nécessite deux vues:
Dans le monde Sveltekit, cela se traduit par deux "routeurs", que Sveltekit stipule que ces routes devraient exister sous le nom de SRC / Routes / index.svelte (page d'accueil) et SRC / Routes / Viewer.Svelte (page de visionneuse de données). Dans une application Web réelle, le deuxième itinéraire sera mappé vers l'URL / Viewer. Bien que ce soit toujours le cas, je sais que mon application de bureau n'aura pas de barre de navigation, ce qui signifie que l'URL sera invisible ... cela signifie que cela n'a pas d'importance comment je nomme cette voie, tant que cela a du sens pour moi.
Le contenu de ces fichiers est principalement hors de propos, du moins pour cet article. Pour ceux qui sont curieux, l'intégralité du projet est open source, et si vous recherchez des exemples Svelte ou Sveltekit, vous êtes invités à le vérifier. Au risque d'être comme un record battu, le point ici est que je construis une application Web normale.
À ce stade, je conçois simplement mes vues et jette de fausses données à code dur jusqu'à ce que j'obtienne quelque chose qui semble valide. Je suis resté ici pendant environ deux jours jusqu'à ce que tout soit beau et que toute l'interactivité (clics de bouton, soumissions de formulaires, etc.) a été perfectionnée. Je l'appellerais une application ou un modèle "géré".
À l'heure actuelle, un spa entièrement fonctionnel existe déjà. Il s'exécute dans un navigateur Web - et est développé dans un navigateur Web. Peut-être contraire à l'intuition, cela en fait un candidat parfait pour les applications de bureau! Mais comment le faire?
Vous avez peut-être entendu parler d'électron. Il s'agit de l'outil le plus célèbre pour construire des applications de bureau multiplateforme utilisant la technologie Web. Il y a beaucoup d'applications très populaires et réussies construites avec elle: Visual Studio Code, WhatsApp, Atom et Slack, pour n'en nommer que quelques-uns. Il fonctionne en regroupant vos ressources Web avec sa propre installation de chrome et son propre runtime Node.js. En d'autres termes, lorsque vous installez une application à base d'électrons, il est livré avec un navigateur chromé supplémentaire et un ensemble complet de langages de programmation (Node.js). Ceux-ci sont intégrés dans le contenu de l'application et ne peuvent pas être évités car ce sont des dépendances de l'application, garantissant qu'elle s'exécute de manière cohérente n'importe où. Comme vous pouvez l'imaginer, il y a des compromis pour cette approche - l'application est assez importante (c'est-à-dire plus de 100 Mo) et utilise beaucoup de ressources système pour s'exécuter. Pour utiliser l'application, l'arrière-plan exécute un chrome tout nouveau / séparé - ce qui n'est pas exactement la même chose que l'ouverture d'un nouvel onglet.
Heureusement, il existe des alternatives - j'ai évalué Svelte Nodegui et Tauri. Les deux options fournissent une taille et des économies d'utilisation importantes de l'application en s'appuyant sur les rendus indigènes fournis par le système d'exploitation plutôt que d'incorporer une copie de Chrome. Nodegui le fait en s'appuyant sur QT, un autre cadre d'application de bureau / GUI compilé en vues natives. Cependant, pour ce faire, Nodegui doit apporter quelques ajustements à votre code d'application afin qu'il puisse convertir vos composants en composants QT . Bien que je crois que cela fonctionnera certainement, je ne suis pas intéressé par cette solution car je veux utiliser exactement ce que je sais sans aucun ajustement à mes fichiers svelte. En revanche, Tauri réalise ses économies en enveloppez le webViewer natif du système d'exploitation - par exemple, Cocoa / WebKit sur MacOS, GTK-WEBKIT2 sur Linux et WebKit sur Edge sur Windows. Les revues Web sont en fait des navigateurs et Tauri les utilise car ils existent déjà sur votre système, ce qui signifie que nos applications peuvent rester purement des produits de développement Web.
Avec ces économies, la plus petite application Tauri est inférieure à 4 Mo et le poids moyen de l'application est inférieur à 20 Mo. Dans mes tests, la plus petite application Nodegui pèse environ 16 Mo. La plus petite application d'électrons peut facilement atteindre 120 Mo.
Inutile de dire que j'ai choisi Tauri. En suivant le guide d'intégration Tauri, j'ai ajouté le package @ tauri-apps / CLI dans DevDependces et j'ai initialisé le projet:
<code>yarn add --dev @tauri-apps/cli yarn tauri init</code>
Cela crée un répertoire SRC-Tauri à côté du répertoire SRC (où se trouve l'application Svelte). C'est là que se trouvent tous les fichiers spécifiques à Tauri, ce qui est idéal pour l'organisation.
Je n'ai jamais construit une application Tauri auparavant, mais après avoir examiné sa documentation de configuration, je suis en mesure de conserver la plupart des valeurs par défaut - à l'exception des éléments comme Package.ProductName et Windows.Title Values, bien sûr. En fait, le seul changement que je dois apporter est la configuration de build, qui doit être alignée sur Sveltekit pour les informations de développement et de sortie:
<code>// src-tauri/tauri.conf.json { "package": { "version": "0.0.0", "productName": "Workers KV" }, "build": { "distDir": "../build", "devPath": "http://localhost:3000", "beforeDevCommand": "yarn svelte-kit dev", "beforeBuildCommand": "yarn svelte-kit build" }, // ... }</code>
Distdir est lié à l'emplacement de l'actif prêt pour la production. Cette valeur est analysée à partir de l'emplacement du fichier tauri.conf.json, il a donc le préfixe ../.
DevPath est l'URL qui est procassée pendant le développement. Par défaut, Sveltekit génère un serveur de développement (configurable) sur le port 3000. J'ai accédé à l'adresse LocalHost: 3000 dans le navigateur pendant la première phase, donc ce n'est pas différent.
Enfin, Tauri a ses propres commandes Dev and Build. Pour éviter les tracas de gérer plusieurs commandes ou de construire des scripts, Tauri fournit les crochets BeforedCommand et BeforeBuildCommand, vous permettant d'exécuter n'importe quelle commande avant l'exécution de la commande Tauri. C'est une commodité subtile mais puissante!
La CLI Sveltekit est accessible via le nom binaire Svelte-Kit. Par exemple, l'écriture de Yarn Svelte-Kit Build dira que Yarn obtiendra son binaire Svelte-Kit local (installé via DevDependency), puis de dire à Sveltekit d'exécuter sa commande build.
Avec cela, mon package de niveau racine.json contient le script suivant:
<code>{ "private": true, "type": "module", "scripts": { "dev": "tauri dev", "build": "tauri build", "prebuild": "premove build", "preview": "svelte-kit preview", "tauri": "tauri" }, // ... "devDependencies": { "@sveltejs/adapter-static": "1.0.0-next.9", "@sveltejs/kit": "1.0.0-next.109", "@tauri-apps/api": "1.0.0-beta.1", "@tauri-apps/cli": "1.0.0-beta.2", "premove": "3.0.1", "svelte": "3.38.2", "svelte-preprocess": "4.7.3", "tslib": "2.2.0", "typescript": "4.2.4" } }</code>
Après l'intégration, ma commande de production est toujours une construction de fils, qui appelle Tauri Build pour regrouper l'application de bureau, mais seulement après la fin du Yarn Svelte-Kit se termine avec succès (via l'option avantBuildCommand). Ma commande de développement est toujours Yarn Dev, qui exécute les commandes Tauri Dev et Yarn Svelte-Kit Dev en parallèle. Le flux de travail de développement est entièrement à l'intérieur de l'application Tauri et maintenant il est proxyant localhost: 3000, me permettant d'obtenir toujours les avantages du serveur de développement HMR.
Important: Tauri est toujours en version bêta au moment de la rédaction. Cela dit, il semble très stable et bien planifié. Je n'ai aucune association avec le projet, mais il semble que Tauri 1.0 ira probablement bientôt dans une version stable. J'ai trouvé Tauri Discord très actif et serviable, y compris les réponses des mainteneurs de Tauri! Ils ont même répondu à certaines de mes questions de nouveaux débutants tout au long du processus. :)
À ce stade, c'était mercredi après-midi de la semaine de victoire rapide et j'ai honnêtement commencé à me sentir nerveux à l'idée de la terminer avant la démo de l'équipe du vendredi. Pourquoi? Depuis que je passe la moitié de la semaine, même si j'ai un beau spa dans une application de bureau à course, cela ne fait toujours rien . J'ai regardé les mêmes fausses données toute la semaine.
Vous pourriez penser que parce que j'ai accès au WebView, je peux utiliser fetch () pour faire des appels API de repos authentifiés pour les données KV des travailleurs que je veux et tout le tout dans une table localStorage ou indexée ... vous avez totalement raison! Cependant, ce n'est pas mon idée pour les cas d'utilisation d'application de bureau.
Il est totalement possible d'enregistrer toutes vos données dans une sorte de stockage de navigateur, mais il l'enregistre localement sur votre machine . Cela signifie que si les membres de votre équipe essaient de faire de même, tout le monde doit obtenir et enregistrer toutes les données sur sa propre machine . Idéalement, cette application KV des travailleurs devrait avoir la possibilité de se connecter et de se synchroniser avec une base de données externe. De cette façon, lorsque vous travaillez dans une configuration d'équipe, tout le monde peut s'adapter au même cache de base de données pour gagner du temps - et de l'argent. Cela commence à devenir important lorsqu'il s'agit de millions de clés qui, comme mentionné précédemment, n'est pas rare lors de l'utilisation des travailleurs KV.
Après y avoir réfléchi pendant un certain temps, j'ai décidé d'utiliser Redis comme stockage backend car il s'agit également d'un magasin de valeur clé. C'est génial car Redis traite déjà les clés comme des citoyens de première classe et fournit le comportement de tri et de filtrage que je veux (c'est-à-dire que je peux passer le travail au lieu de l'implémenter moi-même!). Ensuite, bien sûr, Redis est facile à installer et à exécuter localement ou dans des conteneurs, et si quelqu'un choisit de suivre cette voie, il y a beaucoup de Redis hébergé en tant que fournisseurs de services.
Mais, comment puis-je me connecter? Mon application est essentiellement une balise de navigateur exécutant svelte, non? Oui - mais c'est aussi bien plus que cela.
Comme vous pouvez le voir, une partie de la raison pour laquelle le succès d'Electron est que, oui, il garantit que les applications Web rendent bien sur chaque système d'exploitation, mais il apporte également le runtime Node.js. En tant que développeur Web, c'est un peu comme inclure une API backend directement dans mon client. Fondamentalement, le problème "... mais il fonctionne sur ma machine" disparaît parce que tous les utilisateurs (inconsciemment) exécutent exactement les mêmes paramètres locaux. Grâce à la couche Node.js, vous pouvez interagir avec le système de fichiers, exécuter le serveur sur plusieurs ports ou inclure un tas de node_modules vers-je parle simplement de conjurer avec désinvolture ici à l'instance redis. Des trucs puissants.
Nous ne perdrons pas cette superpuissance parce que nous utilisons Tauri! C'est la même chose, mais légèrement différente.
Au lieu d'inclure l'exécution Node.js, l'application Tauri est construite à l'aide de Rust, un langage système de bas niveau. C'est ainsi que Tauri lui-même interagit avec le système d'exploitation et "emprunte" son Viewer Web natif. Toutes les boîtes à outils Tauri sont compilées (via Rust), qui maintient les applications construites petites et efficaces. Cependant, cela signifie également que nous , développeurs d'applications, pouvons inclure toute autre caisse (l'équivalent "module NPM") dans l'application construite. Bien sûr, il existe également une caisse Redis bien nommée qui agit comme un pilote de client Redis qui permet aux travailleurs KV GUI de se connecter à toute instance redis.
Dans Rust, le fichier cargo.toml est similaire à notre fichier package.json. C'est là que les dépendances et les métadonnées sont définies. Dans la configuration de Tauri, il est situé dans SRC-Tauri / Cargo.toml car encore une fois, tout ce qui concerne Tauri est situé dans ce répertoire. La cargaison a également le concept de "drapeaux fonctionnels" définis au niveau de dépendance. (L'analogie la plus proche à laquelle je peux penser est d'utiliser le NPM pour accéder à la structure interne d'un module ou d'une importation sous-modules, bien qu'il ne soit toujours pas exactement le même, car dans Rust, les indicateurs de fonction affectent la façon dont le package est construit.)
<code># src-tauri/Cargo.toml [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } tauri = { version = "1.0.0-beta.1", features = ["api-all", "menu"] } redis = { version = "0.20", features = ["tokio-native-tls-comp"] }</code>
Ce qui précède définit la caisse redis comme une dépendance et sélectionne la fonction "tokio-native-tls-cal", qui, selon la documentation, est nécessaire pour le support TLS.
Ok, donc j'ai enfin tout ce dont j'ai besoin. Je dois faire parler à mon redis de mon svelte avant la fin de mercredi. Après avoir regardé autour de moi, j'ai remarqué que toutes les choses importantes semblent se produire dans le fichier SRC-Tauri / Main.rs. J'ai écrit la macro # [Command] et je sais que je l'ai déjà vu dans l'exemple Tauri de la journée, j'ai donc appris à copier les différentes parties du fichier exemple pour voir quels bogues apparaissent et disparaissent selon le compilateur de rouille.
Finalement, l'application Tauri a pu s'exécuter à nouveau, et j'ai appris que la macro # [Command] enveloppe en quelque sorte les fonctions sous-jacentes afin qu'elle puisse recevoir les valeurs "contextuelles" (si vous choisissez de les utiliser) et de recevoir les valeurs de paramètres pré-payées. De plus, en tant que langue, Rust effectue de nombreuses conversions de type. Par exemple:
<code>use tauri::{command}; #[command] fn greet(name: String, age: u8) { println!("Hello {}, {} year-old human!", name, age); }</code>
Cela crée une commande de salut, et lors de l'exécution, il s'attend à deux paramètres: nom et âge. Lorsqu'il est défini, la valeur du nom est une valeur de chaîne, et l'âge est le type de données U8 - c'est-à-dire un entier. Cependant, si les deux sont manquants, Tauri lance une erreur car la définition de la commande n'indique pas que tout peut être facultatif.
Afin de connecter réellement la commande Tauri à l'application, il doit être défini comme faisant partie de la combinaison Tauri :: Builder, située dans la fonction principale.
<code>use tauri::{command}; #[command] fn greet(name: String, age: u8) { println!("Hello {}, {} year-old human!", name, age); } fn main() { // start composing a new Builder chain tauri::Builder::default() // assign our generated "handler" to the chain .invoke_handler( // piece together application logic tauri::generate_handler![ greet, // attach the command ] ) // start/initialize the application .run( // put it all together tauri::generate_context!() ) // print<message> if error while running .expect("error while running tauri application"); }</message></code>
L'application Tauri compile et sait qu'il a une commande "Greet". Il contrôle également le WebView (nous en avons déjà discuté), mais ce faisant , il agit comme un pont entre l'extrémité actuelle (contenu WebView) et le backend, qui se compose de l'API Tauri et de tout autre code que nous écrivons (comme les commandes de salut). Tauri nous permet d'envoyer des messages entre le pont afin que les deux mondes puissent communiquer entre eux.
Le front-end peut accéder à ce "pont" en important des fonctionnalités de n'importe quel package (déjà inclus) @ tauri-apps, ou en s'appuyant sur une variable globale Window.Tauri (qui peut être utilisée pour l'ensemble de l'application client) . Plus précisément, nous sommes intéressés par la commande invoquée, qui accepte le nom de commande et un ensemble de paramètres. S'il y a des arguments, il doit être défini comme un objet où la clé correspond au nom du paramètre que notre fonction de rouille attend.
Dans la couche Svelte, cela signifie que nous pouvons effectuer ce qui suit pour appeler la commande de salut définie dans la couche de rouille:
<code>function onclick() { __TAURI__.invoke('greet', { name: 'Alice', age: 32 }); } Click Me</code>
Lorsque ce bouton est cliqué, notre fenêtre de terminal (où la commande Tauri Dev s'exécute) imprime:
<code>Hello Alice, 32 year-old human!</code>
Encore une fois, cela se produit parce que la fonction println! Il apparaît dans la fenêtre de la console du terminal - pas dans la console du navigateur - parce que ce code fonctionne toujours du côté rouille / système.
Il est également possible d'envoyer du contenu au client à partir de la commande Tauri, alors changeons rapidement le salut:
<code>use tauri::{command}; #[command] fn greet(name: String, age: u8) { // implicit return, because no semicolon! format!("Hello {}, {} year-old human!", name, age) } // OR #[command] fn greet(name: String, age: u8) { // explicit `return` statement, must have semicolon return format!("Hello {}, {} year-old human!", name, age); }</code>
Réalisant que je vais appeler Invoke plusieurs fois et être un peu paresseux, j'ai extrait un assistant client léger pour les intégrer:
<code>// @types/global.d.ts ///<reference types="@sveltejs/kit"></reference> type Dict<t> = Record<string t=""> ; declare const __TAURI__: { invoke: typeof import('@tauri-apps/api/tauri').invoke; } // src/lib/tauri.ts export function dispatch(command: string, args: Dict<string> ) { return __TAURI__.invoke(command, args); }</string></string></t></code>
Puis refactor le précédent greeter.svelte à:
<code>import { dispatch } from '$lib/tauri'; async function onclick() { let output = await dispatch('greet', { name: 'Alice', age: 32 }); console.log('~>', output); //=> "~> Hello Alice, 32 year-old human!" } Click Me</code>
merveilleux! C'est donc jeudi et je n'ai pas encore écrit de code redis, mais au moins je sais comment relier les deux moitiés du cerveau de l'application ensemble. Il est temps de peindre le code client et de remplacer tous les todos du gestionnaire d'événements et de le connecter au contenu réel.
Je vais omettre les détails ici car d'ici, c'est très spécifique à l'application - et surtout sur l'histoire du compilateur de rouille qui m'a fait un coup. De plus, explorer les détails est exactement la raison pour laquelle le projet est open source!
À un niveau élevé, une fois qu'une connexion redis est établie avec les détails donnés, le bouton Sync est accessible dans la route / la visionneuse. Lorsque ce bouton est cliqué (et seulement alors - en raison du coût), une fonction JavaScript est appelée, qui est responsable de la connexion à l'API CloudFlare REST et de la distribution de la commande "redis_set" pour chaque touche. Cette commande redis_set est définie dans la couche de rouille - comme sont toutes des commandes basées sur Redis - et est responsable de l'écriture de paires de valeurs clés vers Redis.
La lecture des données de Redis est un processus très similaire, juste l'inverse. Par exemple, lorsque / la visionneuse démarre, toutes les clés doivent être répertoriées et prêtes. Dans la terminologie Svelte, cela signifie que je dois planifier la commande Tauri lorsque le composant / Viewer est installé. Cela se produit presque mot pour mot ici. De plus, cliquer sur le nom de la clé dans la barre latérale affiche plus de "détails" sur la clé, y compris son temps d'expiration (le cas échéant), ses métadonnées (le cas échéant) et sa valeur réelle (si connue). Pour optimiser le coût et la charge du réseau, nous avons décidé que nous ne devrions obtenir la valeur de la clé que nécessaire. Cela introduit le bouton Rafraîchissement qui, lorsqu'il est cliqué, interagit à nouveau avec l'API REST, puis planifie une commande afin que le client Redis puisse mettre à jour la clé individuellement.
Je n'essaie pas de finir à la hâte, mais une fois que vous voyez une interaction réussie entre JavaScript et le code de rouille, vous voyez toutes les interactions! Le reste de mes jeudis et vendredi matin ne fait que définir de nouvelles paires de demandes, ce qui ressemble beaucoup à vous envoyer un ping et des messages pong.
Pour moi - je pense que pour de nombreux autres développeurs JavaScript - le défi cette semaine est d'apprendre la rouille. Je crois que vous l'avez déjà entendu, et vous l'entendrez certainement à nouveau à l'avenir. Les règles de propriété, les chèques d'emprunt et la signification des marqueurs de syntaxe à un seul caractère (ces marqueurs ne sont pas faciles à rechercher d'ailleurs) ne sont que quelques obstacles que j'ai rencontrés. Merci encore Tauri Discord pour son aide et sa gentillesse!
Cela signifie également que l'utilisation de Tauri n'est pas un défi - c'est un énorme soulagement. Je suis sûr que je prévois d'utiliser à nouveau Tauri à l'avenir, surtout si je sais que je peux simplement utiliser WebViewer si je le souhaite. Creuser et / ou l'ajout de sections de rouille est un "matériau supplémentaire" et n'en a besoin que si mon application l'exige.
Pour ceux qui veulent savoir, car je ne trouve pas un autre endroit pour le mentionner: sur MacOS, l'application KV GUI des travailleurs pèse moins de 13 Mo. Je suis très excité par ce résultat!
Bien sûr, Sveltekit rend également ce calendrier possible. Non seulement cela m'a sauvé une demi-journée de configuration de la ceinture d'outils, mais le serveur de développement HMR instantané pourrait également me faire économiser plusieurs heures de rafraîchissement manuellement le navigateur - puis le spectateur Tauri.
Si vous l'avez vu ici, c'est impressionnant! Merci beaucoup pour votre temps et votre attention. Pour rappel, le projet est disponible sur GitHub, et les derniers binaires précompilés sont toujours disponibles via sa page de publication.
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!