从jQuery到Svelte,

大家好。

这是文章“ 摆脱jQuery到Svelte,不费吹灰之力 ”的继续

下面我将谈论我遇到的困难,其中没有很多,只有一个如此根本的问题,如果没有社区支持我将无法应对。

介绍


我打算分块重写前端,这并不是完全不能解决的事情,也不能完全解决问题-我不得不分批重写。

首先,因为jQuery方法是必须的,所以Svelte方法是声明性的。

其次,由于使用jQuery,我们的范围始终是全局的,因此我们可以从任何代码行访问网页的所有元素,我们可以通过ID或CSS选择器来访问它们,而Svelte建议使用组件以及在我们看到的组件内部仅组件本身,我们没有外部或内部元素,因此我们无法直接访问它们。

使用Svelte,我们可以得到真正的面向对象操作:我们不能自己进行更改,只能告诉组件更改的必要性。如何进行这些更改,只知道组件内部的代码。

太好了:)

Svelte有两种与组件通信的机制。

绑定变量(绑定,映射)


我们声明一个变量并将其映射到component属性:

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

我们做了什么?


我们声明了两个局部变量“ task”和“ isModal”,“ task”是要在组件中显示的信息,仅显示数据,不会更改,“ isModal”是组件可见性的标志,如果用户单击组件上的叉号,则组件应消失,十字符号属于该组件,因此我们对点击一无所知,但是我们了解到“ isModal”变量的值已更改,并且由于反应性,我们将计算出这个新值。

如果需要双向绑定,则可以编写“ bind:”,该组件内部值的更改将报告给“父”组件。

如果只需要告诉组件值,则可以使用缩写形式;如果组件属性名称与变量名称匹配,则可以编写“ {task}”或使用析构函数“ {... task}”。

方便。

但是,如果我们在一个组件中嵌入了另一个组件,并且还有第三个组件,那么当然会有一个bollerplate用于向上和向下滚动嵌套层次结构的值。

事件冒泡


我可能会误认为术语,不要踢太多。

父组件可以处理子组件的事件,但只能处理子组件报告的那些事件。

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

这里发生了什么 ?


在父组件中,“ applySample”函数由子“ SearchForm”组件的“ on:search”事件执行,该函数从事件对象接收参数(event.detail)并对其进行处理。

组件中会发生什么?


输入元素的“值”属性映射到“样本”变量;通过(“表单”元素的)“上:提交”事件,执行“搜索”功能,该函数创建“搜索”事件并将{样本:样本“对象”写入“详细信息”属性}-即搜索字符串的值。

因此,搜索字符串的值将传递给父组件,由他决定如何处理该值。

该组件仅负责显示输入表单并传输输入的值,该组件不执行搜索和显示结果,因此我们承担责任。

美丽!

从命令式到声明式的过渡


不幸的是,在这里无法清楚地显示出差异。换句话说,这听起来像是:如果使用jQuery时,我创建了html标记,然后将其插入到正确的位置,然后使用Svelte我生成具有组件属性的数组,然后在循环中添加具有预先计算的属性的组件:

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

怎么运行的 ?


创建分页组件时,我们形成一个“元素”数组以转到某些页面-“ pagingPlaces”,然后循环浏览所有元素并插入一个组件以显示一个分页位置-“ OrdinalPlace”。

同样,采用声明式方法,我们不会自己形成每个职位,而是告诉组件我们需要显示具有此类属性的职位。

在这里,我们看到了一个事件出现的混乱情况。要转到搜索结果页面,用户单击“ OrdinalPlace”组件,该组件无法加载页面,因此它将创建带有“ page index”参数的“ move”事件,并且该事件将拾取父组件“ Paging”,因此也无法加载页面它引发'move'事件,并且下一个父组件已经将其拾取并以某种方式进行处理。

Svelte和组件方法促使我们分担责任并遵循SOLID。

最大的伏击


上面的示例显示了一个基本问题的解决方案,如果没有提示,我将无法解决该问题。Svelte会缓存所有组件,您需要帮助他跟踪这些组件中的更改。

这是有问题的代码:

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

为了显示分页中的页面列表,我们遍历了数组,Svelte为组件分配了每个组件的数组索引,现在,Svelte决定基于此索引重绘组件,如果您在遍历数组元素时未指定索引,则不了解什么,我试图了解一天,然后我在大厅里寻求帮助,在大厅里,我没有立即找到一个熟悉这种耙子的人,但是由于这些家伙,他们帮助了我。

使用数组时,请记住这一点:数组的任何传递都必须再次使用索引:

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

“将PagingPlaces作为位置(place.index)”-确保使用。

当然,如果您以前使用过React / Vue,那么您可能已经熟悉此功能。

视觉效果


我的应用程序使用模式窗口。jQuery为此设置了标记要求,没有它,jQuery.modal()方法将无法工作。

Svelte使这更容易:

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

具体来说,“过渡:淡入淡出”是导致页面上元素消失/外观的原因。
没有人指示我们应该拥有什么标记。

很好。

除了该动画之外,Svelte还有其他一些内容:教程中的链接中的示例fly and tweened

其他


变量名


变量/参数/属性命名的麻烦在于,您必须使用一个词来调用对象的属性和在此写的变量,当您需要在电话上谈论代码时,手机规则就可以了,这样您就不必为此感到困惑并了解所有内容,例如重复的名称违反。

阿贾克斯


这不适用于Svelte,但涉及拒绝使用jQuery,可以用fetch()替换jQuery.ajax,我做了这样的替换,您可以在存储库中看到它。

结论


从使用jQuery到使用Svelte的过程将需要重写用于创建标记的逻辑,但这并不困难,而且只要看起来似乎就没有困难,尤其是在您的代码没有犯错的情况下。

Svelte简化了标记并缩短了JS代码,使用Svelte使您的代码更可重用,并且可以抵抗随机错误。

使用Svelte,它将对您和您的客户都有好处!

参考文献


Svelte
Repository的官方网站,以及从使用jQuery到使用
俄语的Svelte in Telegram社区的Svelte Channel 的过渡,

谢谢您的阅读。

PS:我不知道为什么Habr从与社区频道的链接中排除冒号,正确的链接是:tg:// resolve?Domain = sveltejs

All Articles