Récemment, la tendance à la
«disparition des frameworks» gagne en popularité, dont la locomotive, sans aucun doute, peut être considérée comme
SvelteJS - un framework de buildtime et un compilateur javascript vanille.
Malgré le fait que Svelte soit conceptuellement très simple et encore plus facile à utiliser, de nombreux développeurs se demandent quelle est la caractéristique la plus meurtrière de ce cadre, et toute l'approche? Pourquoi n'est-ce pas «encore un autre framework javascript»?
Dans cet article, je parlerai de l'une des nombreuses superpuissances de Svelte qui peuvent sérieusement vous faciliter la vie.
Voyons cela, mais je vais d'abord vous raconter une légende ...
La légende du cadre du pouvoir
Certains frameworks ont été créés par les gars de Google et Facebook, d'autres - des mecs sympas, mais tous sous la "grande attention" de Rich Harris .
Neuf cadres ont été créés pour les humains, sept semblent être pour les nains. Trois autres cadres (react, vue, angular) étaient pour les elfes.
Après avoir créé les cadres et les avoir mis en œuvre dans des milliers de projets, Rich Harris a personnellement et secrètement créé un cadre ...
Un cadre pour les gouverner tous,
Un cadre pour les trouver,
Un cadre pour tous les amener
Et les lier ensemble.
- Le Seigneur des Cadres
Le problème
Je suis sûr que beaucoup d'entre vous qui ont été sérieusement et longtemps engagés dans le développement front-end ont été confrontés à plusieurs reprises au problème du choix des outils pour votre projet actuel et / ou suivant.
La variété de toutes sortes de packages, utilitaires, baleines, frameworks, bibliothèques et autres solutions est hors échelle comme jamais auparavant. Et surtout, tout ce mouvement continue de s'accélérer.
Tout cela, d'une manière ou d'une autre, s'applique au choix du cadre. Je ne me trompe probablement pas si je suppose que seules quelques équipes et entreprises modernes commencent de nouveaux projets sans utiliser de framework js. Bien sûr, en ce qui concerne les applications Web modernes, pas seulement les sites Web. Et tout irait bien si tant de choses dans votre projet n'en dépendaient pas.
Jugez par vous-même, la composition et les caractéristiques de l'équipe dépendent en grande partie du cadre que vous choisissez, et tout le processus de chasse en dépend certainement. Parfois, le budget et les délais en dépendent même. En bref brrr.
Mais les vrais problèmes commencent, si quelque part au milieu du projet, vous vous rendez compte que vous avez fait un mauvais choix. Quelque chose n'a pas grandi ensemble et n'a pas tourné. Le framework a nécessité un peu plus de temps à maîtriser, une équipe un peu plus grande, s'est avérée un peu moins rapide, un peu inadaptée à vos objectifs ou à votre style de développement, etc. Et plus important encore, maintenant votre projet est lié à 100% à ce cadre et vous ne pouvez pas simplement le prendre et le réécrire sur autre chose.
Encore plus offensant, quand malgré tout avoir terminé le projet avec succès, vous comprenez qu'en général, vous n'êtes pas très heureux. Et probablement, nous ne voudrions pas écrire le prochain projet sur le même framework. Ainsi, toutes ces solutions «réutilisées» que nous recherchons peuvent être jetées dans le tuyau.
En fait, l'enfer avec, avec un code d'entreprise qui implémente une tâche métier spécifique, fonctionne très bien. Mais vous avez écrit un super "% insérer votre% avec le blackjack et les filles à faible responsabilité sociale", et vous vouliez l'utiliser dans votre prochain projet, mais cette infection est étroitement liée au cadre actuel à partir duquel vous regardez déjà un type.
Une autre variante du même problème - imaginez que vous êtes une grande entreprise, comme Yandex. Vous avez une multitude de projets, dont certains ne sont connus que de certains employés, et chaque projet a déjà fait l'expérience de tout ce que j'ai décrit ci-dessus. Le problème est que tous ces projets reposent et détestent les différents cadres qu'ils ont initialement sélectionnés.
Et voici votre formidable leadership, décidé à concurrencer Google Material Design et à vous envoyer en croisade vers les interfaces variées de vos projets afin de les amener à un dénominateur commun. Des concepteurs astucieux dessinent déjà de nouveaux boutons et sélecteurs et griffonnent des milliers de pages de directives pour le nouveau kit d'interface utilisateur unique de vos composants. Vive camarades!
Pas la vie, mais un conte de fées, non? Il ne reste plus qu'à trouver comment extraire tous ces nouveaux composants sur tous ces projets que vous avez déjà réussi à écrire sur tous les frameworks possibles. S'il y a vraiment beaucoup de temps et d'argent et qu'il y a un désir esthétique, et surtout la conviction que "tout doit être unifié", alors vous pouvez mettre une douzaine d'équipes pour réécrire tout cela, par exemple, sur React. C'est exact, car la merde terne sur laquelle vous avez écrit les 2-3 dernières années est déjà moralement obsolète, mais React le sera pour toujours. Bien, bien)
Il y a une autre manière. Vous pouvez écrire un merveilleux nouveau kit d'interface utilisateur sur un seul framework, créer une bibliothèque de composants réutilisables, pour ainsi dire, puis utiliser simplement ce kit d'interface utilisateur dans tous vos projets. Ça a l'air cool? Bien sûr, mais il reste un problème - l'exécution.
Si votre projet est écrit en angulaire (~ 500 Ko), et que vous avez décidé d'écrire un kit d'interface utilisateur dans React (~ 98 Ko), puis faites glisser chaque projet sur un framework, un autre framework, et même avec un tas de dépendances, le kit d'interface utilisateur lui-même est direct disons que cela ne semble pas optimal.
Solution
Pour nous aider à venir les frameworks très "disparus", sans runtime. La condition principale ici est qu'ils soient aussi isolés que possible en termes de leur écosystème et disposent de mécanismes d'intégration externes et des API correspondantes.
Un bon exemple d'un tel cadre est SvelteJS, sur lequel pas mal d'
articles ont déjà été écrits
sur Habré .
Imaginez donc la situation dans laquelle nous avons une application sur React. Peut-être que nous en avons assez et que nous voulons nous en débarrasser, mais tout réécrire à la fois est un luxe inadmissible. Ou peut-être que certaines parties de l'application nécessitent une amélioration ou une refactorisation. Eh bien, ou nous avons décidé de créer une bibliothèque de composants unique, et maintenant nous allons écrire tous les composants dans Svelte et les utiliser dans tous les projets. Présenté? Oui, bien sûr que non, personne n'a un tel fantasme. Jetons un coup d'œil à un exemple réel.

Clause de non-responsabilitéJe veux immédiatement attirer votre attention sur le fait que je ne suis pas développeur React et la dernière fois que j'ai "ressenti" React en 2015. Par conséquent, je suppose que la façon dont j'ai écrit une partie de l'exemple React peut blesser les sentiments des réactifs croyants . Je suis désolé de ne pas juger strictement, d'autant plus que le sens de l'article ne change pas.
Ainsi, la tâche consiste à implémenter le composant Svelte prêt à l'emploi dans l'application React, sans modifier le composant lui-même et sans envelopper le runtime supplémentaire dans l'application. Par exemple, je prendrai le composant qui recherche les utilisateurs de GitHub, que j'ai écrit pour l'article précédent
"Comment rechercher des utilisateurs sur GitHub sans React + RxJS 6 + Recomposer" .
Le code de ce composant peut être affiché dans le
REPL et l'exemple de code de cet article dans le
référentiel .
Créer une application de réaction
Tout d'abord, créez un nouveau projet React à l'aide de l'outil standard de facto -
create-react-app :
npx create-react-app my-app cd my-app npm start
D'accord, si vous allez au 3000ème port, cela semble fonctionner.
Personnalisez Svelte
Si vous ne savez rien de Svelte, alors je dirai ceci, dans le cadre de la tâche Svelte, ce n'est qu'une étape de plus de votre collecteur (webpack / rollup / gupl / grunt / etc), qui vous permettra d'écrire des composants au format SFC et de les compiler en vanille javascript.
Dans la communauté Svelte, Rollup est plus préféré, ce qui n'est pas surprenant, car ils ont un auteur - Rich Harris. Cependant, puisque l'ARC utilise le webpack, nous allons configurer Svelte à travers lui. Pour ce faire, vous devez d'abord transférer les configurations webpack des scripts de réaction vers le projet afin que nous puissions les modifier. Cela se fait à l'aide de la commande intégrée:
npm run eject
Pour autant que je sache, ce n'est pas une approche casher, mais par exemple, c'est l'option la plus pratique.
Maintenant que les configurations du webpack sont à la racine du projet, vous pouvez installer Svelte:
npm i --save-dev svelte svelte-loader
Faites attention au drapeau
--save-dev , rappelez-vous oui, il n'y a pas d'exécution.))))
La touche finale, vous devez connecter le chargeur approprié dans les configurations:
{ test: /\.svelte$/, use: { loader: 'svelte-loader', } },
En général, il est courant dans la communauté Svelte d'écrire des fichiers de composants avec l'extension
.html , car le composant Svelte est un fichier HTML valide. Cependant, pour éviter les collisions potentielles, dans certains cas, il est préférable d'utiliser le
format de fichier
.svelte personnalisé.
C'est ce que nous avons fait, maintenant tous les fichiers
.svelte inclus dans le projet seront interceptés par ce chargeur et compilés par Svelte.
Écrire un composant Svelte
Tout d'abord, il est préférable de configurer l'éditeur de code, par exemple, afin qu'il applique la coloration syntaxique html aux fichiers avec l'extension correspondante. Quelque chose comme ça se fait dans VS Code:
"files.associations": { "*.svelte": "html" }
Créez maintenant un dossier ./src/svelte_components/ et là le dossier du composant lui-même. Après cela, nous transférons simplement tous les fichiers de l'
exemple REPL dans ce dossier, en leur donnant simultanément une nouvelle extension
.svelte , et appelons le fichier App.html Widget.svelte.
Le résultat devrait être quelque chose comme ceci:
Au même endroit, nous créons le fichier index.js dans lequel nous aurons le code d'intégration Svelte et React.
Intégrer
Peut-être que maintenant vous voulez savoir ce qu'est la magie? La magie est que nous avons déjà fait toute la magie. Magiquement, n'est-ce pas?
Sérieusement, nous pouvons maintenant utiliser les composants Svelte dans notre application React en tant que constructeurs JS complètement ordinaires, ce qui signifie que le code d'intégration avec Svelte ne sera pas différent de l'intégration avec un autre autonome. La documentation React contient même une section dédiée à cela:
Intégration avec d'autres bibliothèques .
Le code d'intégration peut ressembler à ceci:
import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { const { username } = this.props; this.widget = new Widget({ target: this.el, data: { username } }); } componentWillUnmount() { this.widget.destroy(); } render() { return ( <div ref={el => this.el = el}></div> ); } }
Au total, nous avons simplement enveloppé le code de notre composant Svelte complexe dans un composant React très simple, qui crée simplement une nouvelle instance du composant Svelte, transférant l'élément de montage et les données des accessoires lors de sa création. De plus, nous n'oublions pas de désinstaller le composant Svelte dans le hook
componentWillUnmount .
La seule chose que nous n'avons pas encore faite est qu'ils n'ont pas synchronisé les valeurs de l'état du composant. Autrement dit, si le composant parent a jeté d'autres accessoires dans le composant wrapper, ils doivent alors s'appliquer au composant Svelte. Inversement, si les données ont été modifiées à l'intérieur du composant Svelte, elles doivent être annulées.
Pour ce faire, nous supposons que le composant React de niveau supérieur transmettra le rappel onChange, que nous devrions extraire lorsque des modifications se produisent à l'intérieur, et nous nous attendrons à des changements dans les accessoires du
composant wrapper dans le crochet
componentWillReceiveProps . Faisons-le:
componentDidMount() { ... this.widget.on('state', ({ current: { username }, changed }) => { if (changed.username) { this.props.onChange({ username }); } }); } componentWillReceiveProps({ username }) { this.widget.set({ username }); }
Ici, nous avons utilisé l'événement d'
état intégré, qui se déclenche chaque fois que l'
état du composant Svelte change. Un objet contenant l'état actuel du composant (
actuel ), l'état
précédent (
précédent ) et la liste des propriétés
modifiées (
modifiées ) est transféré vers le rappel. En conséquence, nous vérifions simplement si le nom d'utilisateur a été modifié et appelons le
rappel onChange, le cas échéant.
Dans le hook
componentWillReceiveProps , nous définissons le nouveau
nom d'utilisateur à l'aide de la méthode
set () intégrée.
Outre les composants intégrés, les composants Svelte peuvent implémenter des événements et des méthodes personnalisés. Ce sont ces fonctionnalités intéressantes qui vous permettent de décrire l'interface du composant et il est assez pratique d'organiser la communication avec le "monde extérieur".
Utiliser
Essayons maintenant d'utiliser notre widget directement dans l'application React. Pour ce faire, modifiez le fichier App.js généré par le démarreur:
import React, { Component } from 'react'; import './App.css'; import GithubWidget from './svelte_components/GithubWidget'; class App extends Component { constructor() { super(); this.state = { username: '' }; } handleChange = (state) => { this.setState({ ...state }); } render() { return ( <div className="App"> <header className="App-header"> <h1>Github Widget for: {this.state.username}</h1> <GithubWidget onChange={this.handleChange} username={this.state.username} /> </header> </div> ); } } export default App;
En bref, nous l'utilisons comme un composant React régulier. Et en conséquence, nous obtenons:
Déjà pas mal, non?) Veuillez noter que la valeur du nom d'utilisateur que nous entrons dans le champ de texte du widget est immédiatement transmise à l'application React.
Nous finaliserons
Apprenons maintenant à notre widget à rechercher et à afficher non seulement la carte d'utilisateur GitHub, mais également la carte de référentiel.
Tout d'abord, vous devez créer un nouveau composant Repo.svelte, qui dessinera la carte de référentiel. Par souci de simplicité, je viens de copier le modèle et les styles de User.svelte et de l'adapter aux données du référentiel. Cependant, théoriquement, il s'agit d'un composant distinct.
Ensuite, vous devez apprendre au composant de contrôle Widget.svelte à commuter ces deux types de cartes à la volée. En outre, vous devez lui apprendre à tirer différentes demandes pour l'utilisateur et le référentiel.
Nous utiliserons un champ pour la saisie et déterminerons le type de données par la présence de "/" dans la valeur. Autrement dit, si vous devez rechercher l'utilisateur, entrez son nom d'utilisateur, et si le référentiel, puis entrez le nom d'utilisateur de l'utilisateur, puis "/" et le nom du référentiel.
À première vue, cela semble plutôt confus, mais sur Svelte, la solution prendra littéralement 5-6 lignes de code. Tout d'abord, connectons un nouveau composant et une nouvelle méthode API, que nous encapsulons en anti-rebond:
... import Repo from './Repo.svelte'; ... import { getUserCard, getRepoCard } from './api.js'; ... const getRepo = debounce(getRepoCard, 1000);
Ensuite, créez une propriété calculée qui déterminera quel type de carte nous devons montrer:
computed: { ... repo: ({ username }) => username.includes('/'), ... }
Ajoutez maintenant le commutateur de requêtes à l'API:
computed: { ... card: ({ username, repo }) => username && (repo ? getRepo : getUser)(username), ... }
Et enfin, changer les composants de la carte en fonction du type:
computed: { ... Card: ({ repo }) => repo ? Repo : User, ... }
De plus, afin de remplacer dynamiquement les composants, nous devons utiliser une balise Svelte spéciale, qui dessine le composant dont la valeur est passée à l'attribut
this :
<svelte:component this={Card} {...card} />
Ça marche. L'avez-vous remarqué? Nous écrivons déjà sur Svelte dans l'application React! )))
Maintenant, apprenons à notre widget à masquer le champ de saisie et essayez de saisir le nom d'utilisateur non pas dans le widget, mais dans l'application React. Est-il important que notre logique métier obtienne cette valeur?
Nous introduisons une nouvelle propriété de
recherche dont la valeur par défaut est false. Selon cette propriété, le champ de saisie sera affiché ou non affiché. Par défaut, en conséquence, il n'y aura pas de champ.
{#if search} <input bind:value=username placeholder="username or username/repo"> {/if} ... <script> export default { ... data() { return { username: '', search: false }; }, ... }; </script>
Maintenant, dans App.js, nous allons créer un champ d'entrée du côté React de l'application et écrire le traitement correspondant pour l'événement d'entrée:
... handleUsername = (e) => { this.setState({ username: e.target.value }); } ... <h1>Github Widget for: {this.state.username}</h1> <input value={this.state.username} onChange={this.handleUsername} className="Username" placeholder="username or username/repo" />
Et copiez-le également dans le dossier avec le widget voici un tel fileur svg sur Svelte:
<svg height={size} width={size} style="animation-duration:{speed}ms;" class="svelte-spinner" viewbox="0 0 32 32" > <circle role="presentation" cx="16" cy="16" r={radius} stroke={color} fill="none" stroke-width={thickness} stroke-dasharray="{dash},100" stroke-linecap="round" /> </svg> <script> export default { data() { return { size: 25, speed: 750, color: 'rgba(0,0,0,0.4)', thickness: 2, gap: 40, radius: 10 }; }, computed: { dash: ({radius, gap}) => 2 * Math.PI * radius * (100 - gap) / 100 } }; </script> <style> .svelte-spinner { transition-property: transform; animation-name: svelte-spinner_infinite-spin; animation-iteration-count: infinite; animation-timing-function: linear; } @keyframes svelte-spinner_infinite-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } </style>
Et nous l'appliquerons dans le widget pour qu'il soit très beau:
... {#await card} <Spinner size="50" speed="750" color="#38b0ee" thickness="2" gap="40" /> {:then card} ...
À mon avis, cela s'est avéré pas très mal:
Le chapeau haut de forme avec un fond noir et un champ de saisie est une application React, le bloc blanc ci-dessous est un widget Svelte. Ce sont les tartes. )))
→
RéférentielConclusions
Svelte est un excellent outil pour développer des applications Web modernes basées sur des composants. De plus, vous pouvez rapidement et facilement y écrire des composants d'interface utilisateur autonomes réutilisables et des widgets, qui peuvent être utilisés dans toutes les applications Web, même en conjonction avec d'autres cadres. Il est également idéal pour les
microfronts .
Svelte est parfait pour vous si:
- Vous souhaitez démarrer un nouveau projet et ne savez pas quel framework choisir pour cela.
- Vous avez un projet, ça marche et mieux vaut ne pas le toucher. De nouveaux composants et modules, vous pouvez écrire dans Svelte et intégrer de manière transparente dans le code existant.
- Vous avez déjà un projet, mais il est partiellement ou complètement obsolète et / ou nécessite une refonte sérieuse, jusqu'à une réécriture complète. Vous pouvez commencer à le réécrire en plusieurs parties. Vous n'avez pas besoin de proposer des configurations complexes. Il vous suffit de prendre un composant, de le réécrire dans Svelte et d'envelopper le nouveau composant avec l'ancien. Cependant, le reste de l'application n'est même pas au courant des changements.
- Vous avez plusieurs projets sur différentes bases de code et en même temps, vous souhaitez avoir un seul kit d'interface utilisateur et l'utiliser dans l'un de ces projets. Écrivez un kit d'interface utilisateur sur Svelte et utilisez-le n'importe où. C'est sympa.
Vous voulez découvrir des cas plus intéressants? Rejoignez notre
chaîne Telegram !
MISE À JOUR: merci
justboris pour la bonne
question . Poursuivant l'exemple:
import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { ... this.widget = new Widget({ target: this.el, data: { username }, slots: { default: this.slot } }); ... } ... render() { return ( <div ref={el => this.el = el}> <div ref={el => this.slot = el}> {this.props.children} </div> </div> ); } }
<GithubWidget onChange={this.handleChange} username={this.state.username}> <p>Hello world</p> </GithubWidget>