Éloignez-vous de jQuery à Svelte, pour ainsi dire

Bonjour à tous.

Il s'agit d'une continuation de l'article " Évadez-vous de jQuery à Svelte, pas de douleur ."

Ci-dessous, je parlerai des difficultés que j'ai rencontrées, il n'y en avait pas beaucoup, et une seule était si fondamentale où je ne pouvais pas faire face sans le soutien de la communauté .

introduction


J'avais prévu de réécrire le front-end en morceaux, ce n'est pas quelque chose qui ne fonctionnerait pas du tout, cela n'a pas fonctionné complètement - j'ai dû le réécrire en gros morceaux.

Tout d'abord, parce que l'approche jQuery est impérative, l'approche Svelte est déclarative.

Deuxièmement, parce qu'en utilisant jQuery, notre portée est toujours globale, nous pouvons accéder à tous les éléments d'une page Web à partir de n'importe quelle ligne de code, nous y accédons par ID ou sélecteur CSS, tandis que Svelte recommande d'utiliser des composants et à l'intérieur du composant que nous voyons seulement le composant lui-même, nous n'avons aucun élément externe ou interne, et nous ne pouvons pas y accéder directement.

Avec Svelte, nous obtenons une véritable POO: nous ne pouvons pas apporter de modifications nous-mêmes, nous pouvons seulement informer le composant de la nécessité de modifications. Comment ces modifications seront apportées, ne connaît que le code à l'intérieur du composant.

Et c'est super :)

Svelte dispose de deux mécanismes pour communiquer avec les composants.

Variables de liaison (liaisons, mappage)


Nous déclarons une variable et la mappons à l'attribut du composant:

<script>
    import DetailTaskView from './DetailTaskView.svelte';
    let task = {};
    let isModal = false;
    function renderTask(data) {
        const isSuccess = data.hasOwnProperty(0);
        if (isSuccess) {
            isModal = true;
            task = data[0];
        }
    }
    import {fade} from 'svelte/transition';
</script>
{#if isModal}
    <div transition:fade>
        <DetailTaskView bind:isModal="{isModal}" {...task}/>
    </div>
{/if}

Qu'avons-nous fait?


Nous avons déclaré deux variables locales «tâche» et «isModal», «tâche» est une information à afficher dans le composant, les données sont uniquement affichées, ne seront pas modifiées, «isModal» est le drapeau de la visibilité du composant, si l'utilisateur a cliqué sur la croix du composant, alors le composant devrait disparaître , la croix appartient au composant, donc nous ne savons rien du clic, mais nous apprenons que la valeur de la variable "isModal" a changé et grâce à la réactivité, nous allons travailler sur cette nouvelle valeur.

Si nous avons besoin d'une liaison bidirectionnelle, alors nous écrivons «bind:», une modification de la valeur à l'intérieur du composant sera signalée au composant «parent».

Nous pouvons utiliser une forme abrégée si nous avons seulement besoin de dire au composant les valeurs, et si le nom d'attribut du composant correspond au nom de la variable, nous pouvons écrire "{task}" ou utiliser le destructeur "{... task}".

Idéalement.

Mais si nous avons un autre composant incorporé dans un composant, et qu'il y en a aussi un troisième, alors bien sûr il y a une plaque pour faire défiler les valeurs vers le haut et vers le bas dans la hiérarchie d'imbrication.

Bulle d'événement


Je peux me tromper avec la terminologie, ne donne pas beaucoup de coups.

Le composant parent peut gérer les événements du composant enfant, mais uniquement les événements signalés par le composant enfant.

<!-- App.svelte   -->
<script>
    import SearchForm from './SearchForm.svelte';
    async function applySample(event) {
        const sample = event.detail.sample;
        if(sample){
            search(sample);
        }
        if(!sample){
            renderPage();
        }
    }
</script>
<div class="container">
    <h1>  </h1>
    <SearchForm on:search={applySample}/>
</div>

<!-- SearchForm.svelte    -->
<script>
	import { createEventDispatcher } from 'svelte';
	const dispatch = createEventDispatcher();
	let sample = '';
    function search(event) {
        event.preventDefault();
        dispatch('search', {
            sample: sample
        });
    }
</script>
<form class="form-horizontal" id="search"
on:submit={search}>
    <div class="form-group">
        <label class="control-label col-sm-2" for="sample">
        
        </label>
        <div class="col-sm-10">
            <input id="sample" class="form-control" type="search"
             placeholder="  "
             autocomplete="on" bind:value={sample}
             />
        </div>
    </div>
</form>

Que se passe t-il ici ?


Dans le composant parent, la fonction «applySample» est exécutée par l'événement «on: search» du composant enfant «SearchForm», cette fonction reçoit les paramètres (event.detail) de l'objet événement et les traite.

Que se passe-t-il dans un composant?


L'attribut "value" de l'élément d'entrée est mappé à la variable "sample", par l'événement "on: submit" (de l'élément "form") la fonction "search" est exécutée, ce qui déclenche l'événement 'search' et écrit l'objet {sample: sample dans la propriété "detail" } - c'est-à-dire la valeur de la chaîne de recherche.

Ainsi, la valeur de la chaîne de recherche est transmise au composant parent et c'est lui qui décide quoi faire de cette valeur.

Le composant est uniquement responsable de l'affichage du formulaire de saisie et de la transmission de la valeur saisie, le composant ne met pas en œuvre la recherche et l'affichage des résultats, nous partageons donc la responsabilité.

La beauté!

Le passage de l'impératif au déclaratif


Ici, malheureusement, cela ne fonctionne pas pour montrer la différence tout aussi clairement. En termes, cela ressemble à ceci: si, lors de l'utilisation de jQuery, j'ai créé un balisage html puis l'ai inséré au bon endroit, puis avec Svelte je génère un tableau avec les attributs des composants puis dans la boucle j'ajoute les composants avec des attributs pré-calculés:

<!-- Paging.svelte        -->
<script>
    import {createEventDispatcher} from 'svelte';
    import OrdinalPlace from './OrdinalPlace.svelte';
    let pagingPlaces;
    //         pagingPlaces
    function addPlace(
        paging = [], index = Number.NEGATIVE_INFINITY,
        text = "", css = "") {
        paging.push({index:index,text:text,css:css});

        return paging;
    }
    const dispatch = createEventDispatcher();
    function browsePage(event) {
        const pageIndex = event.detail.index;
        dispatch('move', {
            index: pageIndex
        });
    }
</script>

{#if pagingPlaces.length}
    <table class = "table table-hover-cells table-bordered">
        <tbody>
            <tr>
            {#each pagingPlaces as place (place.index)}
                <OrdinalPlace on:move="{browsePage}"
                {...place}>
                </OrdinalPlace>
            {/each}
            </tr>
        </tbody>
    </table>
{/if}

<!-- OrdinalPlace.svelte     ""      -->
<script>
    export let index = -1;
    export let text = "";
    export let css = "";

    let number = index +1;
    function skip() {
        return !(text === "");
    }

    let letSkip = skip();
    let noSkip = !letSkip;

    import { createEventDispatcher } from "svelte";
    const dispatch = createEventDispatcher();
    function moveTo() {
        if(noSkip){
            dispatch("move", {index:index});
        }
    }
</script>
<td class="{css}" on:click="{moveTo}">
    {#if letSkip}
        {text}
    {/if}
    {#if noSkip}
        {number}
    {/if}
</td>

Comment ça fonctionne ?


Lors de la création du composant de pagination, nous formons un tableau «d'éléments» pour accéder à certaines pages - «pagingPlaces», puis parcourons tous les éléments et insérons un composant pour afficher une position de pagination - «OrdinalPlace».

Encore une fois, une approche déclarative, nous ne formons pas chaque position nous-mêmes, nous disons au composant que nous devons afficher une position avec de tels attributs.

Nous voyons ici un cas confus d'émergence d'un événement. Pour accéder à la page des résultats de la recherche, l'utilisateur clique sur le composant «OrdinalPlace», ce composant ne peut pas charger la page, il crée donc un événement «move» avec le paramètre d'index de page et cet événement récupère le composant parent - «Paging», qui ne peut pas non plus charger la page par conséquent il déclenche l'événement 'move', et le composant parent suivant le récupère déjà et le traite d'une manière ou d'une autre.

Svelte et l'approche par composants nous poussent à partager les responsabilités et à suivre SOLID.

Plus grande embuscade


L'exemple ci-dessus montre une solution à un problème fondamental que je ne pourrais pas gérer sans un indice. Svelte met en cache tous les composants et vous devez l'aider à suivre les modifications de ces composants.

Voici le code en question:

            {#each pagingPlaces as place (place.index)}
                <OrdinalPlace on:move="{browsePage}"
                {...place}>
                </OrdinalPlace>
            {/each}

Pour afficher la liste des pages dans la pagination, nous avons parcouru le tableau et Svelte a affecté un composant un index de tableau à chaque composant, maintenant Svelte prend la décision de redessiner le composant en fonction de cet index, si vous ne spécifiez pas l'index lors de l'itération à travers les éléments du tableau, alors ne comprenez pas ce que , J'ai essayé de comprendre pendant une journée, puis j'ai demandé de l'aide à la salle et dans la salle, je n'ai pas trouvé immédiatement une personne qui connaissait bien ce râteau, mais ils m'ont aidé, grâce aux gars.

Lorsque vous travaillez avec des tableaux, gardez cela à l'esprit: tout passage à travers le tableau doit utiliser un index, encore une fois:

            {#each pagingPlaces as place (place.index)}

"PagingPlaces as place (place.index)" - assurez-vous d'utiliser.

Bien sûr, si vous avez déjà travaillé avec React / Vue, vous connaissez probablement déjà cette fonctionnalité.

Effets visuels


Mon application utilisait des fenêtres modales. jQuery pour cela définit les exigences de balisage, sans cela, la méthode jQuery.modal () ne fonctionnera pas.

Svelte facilite cela:

{#if isModal}
    <div transition:fade>
        <DetailTaskView bind:isModal="{isModal}" {...task}/>
    </div>
{/if}

Plus précisément, "transition: fondu" est responsable de la disparition / apparition d'éléments sur la page.
Personne ne nous dicte quel balisage nous devrions avoir.

C'est bon.

En plus de cette animation, Svelte en a quelques autres: voler et interpoler , exemples des liens dans le tutoriel.

Autre


Noms de variables


Le problème avec la dénomination des variables / paramètres / attributs, vous devez utiliser un mot pour appeler à la fois la propriété de l'objet et la variable que vous y écrivez, la règle du combiné lorsque vous devez parler du code au téléphone, afin de ne pas vous embrouiller à cette fin et de tout comprendre, tels que les noms répétés violer.

Ajax


Cela ne s'applique pas à Svelte, mais concerne le rejet de l'utilisation de jQuery, jQuery.ajax peut être remplacé par fetch (), j'ai fait un tel remplacement, vous pouvez le voir dans le référentiel.

Conclusion


Passer de l'utilisation de jQuery à l'utilisation de Svelte nécessitera une réécriture de la logique de création du balisage, mais ce n'est pas aussi difficile et aussi longtemps que cela puisse paraître, surtout si votre code ne pèche pas avec cela.

Svelte simplifie votre balisage et raccourcit le code JS, l'utilisation de Svelte rend votre code plus réutilisable et résistant aux erreurs aléatoires.

Utilisez Svelte, ce sera bon pour vous et vos clients!

Références


Site officiel de Svelte
Repository avec la transition de l'utilisation de jQuery à l'utilisation de Svelte
Channel de la communauté russophone Svelte in Telegram

Merci d'avoir lu.

PS: Je ne sais pas pourquoi Habr exclut les deux-points du lien vers le canal communautaire, la ligne de lien correcte est: tg: // résoudre? Domain = sveltejs

All Articles