Aléjese de jQuery a Svelte, por así decirlo

Hola a todos.

Esta es una continuación del artículo " Aléjese de jQuery a Svelte, sin dolor ".

A continuación hablaré sobre las dificultades que encontré, no había muchas de ellas, y solo una era tan fundamental donde no podía hacer frente sin el apoyo de la comunidad .

Introducción


Planeaba reescribir el front-end en pedazos, no es algo que no funcionaría en absoluto, no funcionó por completo, tuve que reescribirlo en pedazos grandes.

Primero, debido a que el enfoque jQuery es imperativo, el enfoque Svelte es declarativo.

En segundo lugar, debido a que usando jQuery, nuestro alcance es siempre global, podemos acceder a todos los elementos de una página web desde cualquier línea de código, accedemos a ellos por ID o selector CSS, mientras que Svelte recomienda usar componentes y dentro del componente que vemos solo el componente en sí, no tenemos elementos externos o internos, y no podemos acceder a ellos directamente.

Con Svelte, obtenemos OOP real: no podemos hacer cambios nosotros mismos, solo podemos decirle al componente sobre la necesidad de cambios. Cómo se realizarán estos cambios, solo conoce el código dentro del componente.

Y eso es genial :)

Svelte tiene dos mecanismos para comunicarse con los componentes.

Variables de enlace (enlaces, mapeo)


Declaramos una variable y la asignamos al atributo del componente:

<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é hemos hecho?


Declaramos dos variables locales "tarea" e "isModal", "tarea" es información que se mostrará en el componente, los datos solo se mostrarán, no se cambiarán, "isModal" es el indicador de visibilidad del componente, si el usuario hizo clic en la cruz en el componente, entonces el componente debería desaparecer , la cruz pertenece al componente, por lo que no sabemos nada sobre el clic, pero sabemos que el valor de la variable "isModal" ha cambiado y, gracias a la reactividad, calcularemos este nuevo valor.

Si necesitamos un enlace bidireccional, entonces escribimos "enlace:", un cambio en el valor dentro del componente se informará al componente "padre".

Podemos usar una forma abreviada si solo necesitamos decirle al componente los valores, y si el nombre del atributo del componente coincide con el nombre de la variable, podemos escribir "{task}" o usar el destructor "{... task}".

Convenientemente

Pero si tenemos otro componente incrustado en un componente, y también hay un tercero, entonces, por supuesto, hay una placa de relleno para desplazar los valores hacia arriba y hacia abajo en la jerarquía de anidación.

Evento burbujeante


Puedo confundirme con la terminología, no patear mucho.

El componente primario puede manejar los eventos del componente secundario, pero solo aquellos eventos que informa el componente secundario.

<!-- 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 está pasando aqui ?


En el componente principal, la función "applySample" se ejecuta mediante el evento "on: search" del componente secundario "SearchForm", esta función recibe los parámetros (event.detail) del objeto de evento y los procesa.

¿Qué pasa en un componente?


El atributo "valor" del elemento de entrada se asigna a la variable "muestra", mediante el evento "on: submit" (del elemento "form") se ejecuta la función "search", que eleva el evento 'search' y escribe el objeto {sample: sample en la propiedad "detail" }, es decir, el valor de la cadena de búsqueda.

Por lo tanto, el valor de la cadena de búsqueda se pasa al componente principal y es él quien decide qué hacer con este valor.

El componente solo es responsable de mostrar el formulario de entrada y transmitir el valor ingresado, el componente no implementa la búsqueda y muestra los resultados, por lo que compartimos la responsabilidad.

¡La belleza!

La transición de lo imperativo a lo declarativo


Aquí, desafortunadamente, no funciona mostrar la diferencia con la misma claridad. En palabras, suena así: si, al usar jQuery, creé el marcado html y luego lo inserté en el lugar correcto, luego con Svelte genero una matriz con los atributos de los componentes y luego en el bucle agrego los componentes con atributos precalculados:

<!-- 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>

Cómo funciona ?


Al crear el componente de paginación, formamos una matriz de "elementos" para ir a ciertas páginas - "pagingPlaces", luego recorremos todos los elementos e insertamos un componente para mostrar una posición de paginación - "OrdinalPlace".

Una vez más, un enfoque declarativo, no formamos cada posición nosotros mismos, le decimos al componente que necesitamos mostrar una posición con tales atributos.

Aquí vemos un caso confuso de la aparición de un evento. Para ir a la página de resultados de búsqueda, el usuario hace clic en el componente "OrdinalPlace", este componente no puede cargar la página, por lo tanto, crea un evento "mover" con el parámetro de índice de la página y este evento recoge el componente principal - "Paginación", que tampoco puede cargar la página. provoca el evento 'mover', y el siguiente componente principal ya lo recoge y de alguna manera lo procesa.

Svelte y el enfoque de componentes nos empujan a compartir la responsabilidad y seguir SOLID.

Mayor emboscada


El ejemplo anterior muestra una solución a un problema fundamental que no podría manejar sin una pista. Svelte almacena en caché todos los componentes y debe ayudarlo a rastrear los cambios en estos componentes.

Aquí está el código en cuestión:

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

Para mostrar la lista de páginas en la paginación, revisamos la matriz y Svelte asignó un índice de matriz a cada componente, ahora Svelte toma la decisión de volver a dibujar el componente en función de este índice, si no especifica el índice mientras recorre los elementos de la matriz, entonces no comprende qué Traté de entender por un día, luego pedí ayuda en el pasillo y en el pasillo no encontré de inmediato a una persona que conociera bien este rastrillo, pero me ayudaron, gracias a los chicos.

Cuando trabaje con matrices, tenga esto en cuenta: cualquier paso a través de la matriz debe usar un índice, nuevamente:

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

"PagingPlaces as place (place.index)" - asegúrese de usar.

Por supuesto, si anteriormente trabajó con React / Vue, entonces probablemente ya esté familiarizado con esta función.

Efectos visuales


Mi aplicación usaba ventanas modales. jQuery para esto establece los requisitos de marcado, sin él, el método jQuery.modal () no funcionará.

Svelte hace esto más fácil:

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

Específicamente, "transición: desvanecimiento" es responsable de la desaparición / aparición de elementos en la página.
Nadie nos dicta qué margen de beneficio deberíamos tener.

Es bueno.

Además de esta animación, Svelte tiene un par más: volar y tuitear , ejemplos de los enlaces en el tutorial.

Otro


Nombres de variables


El problema con el nombramiento de variables / parámetros / atributos, tiene que usar una palabra para llamar tanto a la propiedad del objeto como a la variable que escribe allí, la regla del teléfono cuando necesita hablar sobre el código en el teléfono, para que no se confunda en ese extremo y entienda todo, tales nombres repetidos violar.

Ajax


Esto no se aplica a Svelte, pero se refiere al rechazo del uso de jQuery, jQuery.ajax se puede reemplazar con fetch (), hice un reemplazo, puede verlo en el repositorio.

Conclusión


Pasar de usar jQuery a usar Svelte requerirá reescribir la lógica para crear marcado, pero no es tan difícil y tan largo como puede parecer, especialmente si su código no peca con esto.

Svelte simplifica su marcado y acorta el código JS, el uso de Svelte hace que su código sea más reutilizable y resistente a errores aleatorios.

Utilice Svelte, ¡será bueno para usted y sus clientes!

Referencias


Sitio oficial de Svelte
Repository con la transición de usar jQuery a usar Svelte
Channel de la comunidad de habla rusa Svelte en Telegram

Gracias por leer.

PD: No sé por qué Habr excluye los dos puntos del enlace al canal de la comunidad, la línea de enlace correcta es: tg: // resolve? Domain = sveltejs

All Articles