Svelte中的动态渲染和事件绑定

作者:renzp94
时间:2021-03-22 06:30:46

动态渲染

Svelte中支持:条件渲染(if)列表渲染(each)异步判断渲染(await)

条件渲染(if)

Svelte中使用条件渲染主要有以下几个方式:

  • if:{#if 条件} ... {/if}
  • if-else:{#if 条件} ... {:else} ... {/if}
  • if-else if:{#if 条件} ... {:else if 条件} ... {/if}

在线 REPL

<script>
    let role = 'admin';
</script>

<select bind:value={role}>
    <option value="normal">普通用户</option>
    <option value="admin">管理员</option>
    <option value="root">超级管理员</option>
</select>
{#if role === 'normal'}
    <div>普通用户</div>
{:else if role === 'admin'}
    <div>管理员</div>
{:else if role === 'root'}
    <div>超级管理员</div>
{:else}
    <div>未知身份</div>
{/if}

列表渲染(each)

Svelte中列表渲染通过{#each 列表 as 列表元素,列表下标 (key)} ... {/each},支持数组类似数组的对象(有length属性),列表元素支持解构。其中,key是为了保证列表数据更新后视图渲染正确,一般需要具有唯一性。

在线 REPL

<script>
    const books = [
        { id: 1, text: 'Javascript' },
        { id: 2, text: 'Svelte' },
        { id: 3, text: 'React' },
        { id: 4, text: 'Vue' }
    ];
</script>

<ul>
    <!--
        {#each books as item,index (item.id)}
            <li>{index + 1}. {item.text}</li>
        {/each}
    -->
    {#each books as { id, text }, index (id)}
        <li>{index + 1}. {text}</li>
    {/each}
</ul>

异步判断渲染(await)

当列表需要请求中状态时,就可以用异步判断渲染,主要有以下几个方式。

  • 请求中、成功、错误:{#await expression}...{:then data}...{:catch error}...{/await}
  • 请求中、成功:{#await expression}...{:then data}...{/await}
  • 请求成功:{#await expression then data}...{/await}
  • 请求错误:{#await expression catch error}...{/await}

在线 REPL

<script>
    let list = [];

    function mock() {
        return new Promise((resolve, reject) => {
            const list = [
                { id: 1, text: 'Javascript' },
                { id: 2, text: 'Svelte' },
                { id: 3, text: 'React' },
                { id: 4, text: 'Vue' }
            ];

            setTimeout(() => {
                resolve(list);
                //                 reject()
            }, 3000);
        });
    }

    async function init() {
        const data = await mock();
        list = data;
    }

    let promise = init();
</script>

{#await promise}
    <span>loading...</span>
{:then}
    <ul>
        {#each list as { id, text } (id)}
            <li>{text}</li>
        {/each}
    </ul>
{:catch}
    <span>请求失败,请稍后重试</span>
{/await}

注意:如果不需要请求或失败后的数据可以省略为:{:then data}{:then}{:catch error}{:catch}

事件绑定

DOM 事件绑定

Svelte中使用on:来绑定事件,支持inline handlers(内联函数)

在线 REPL

<script>
    let count = 0;
    const onClick = () => count++;
</script>

<!-- 内联样式 -->
<!-- <button on:click={() => count++}>点击了{count}次</button> -->
<button on:click={onClick}>点击了{count}</button>

Svelte同时也提供了可以更改其行为的事件修饰符,用|使用,修饰符可以多个使用。

  • preventDefaultevent.preventDefault()阻止默认事件
  • stopPropagationevent.stopPropagation()阻止事件传递
  • passive:改善了触摸/滚轮事件的滚动性能(Svelte 会在安全的地方自动添加滚动条)
  • nonpassive:明确设置passive: false
  • capture:将事件触发时机改为捕获阶段,默认为冒泡阶段MDN Docs
  • once:只触发一次
  • self:仅当event.target是元素本身时才会触发事件
<script>
    let count = 0;
    let blockCount = 0;
    const onClick = () => count++;
    const onBlockClick = () => blockCount++;
</script>

<div class="block" on:click|self={onBlockClick}>
    block被点击了{blockCount}次
    <button on:click|once={onClick}>点击了{count}次</button>
</div>

<style>
    .block {
        background-color: red;
    }
</style>

组件中传递事件

如果想监听组件的事件,则需要在组件内容触发事件,可通过createEventDispatcher创建一个触发器来触发。

注意:通过createEventDispatcher触发的事件在回调中的参数是一个自定义事件对象CustomEvent,需要通过e.detail来获取触发时传入的数据。

在线 REPL

Select.svelte

<script>
    import { createEventDispatcher } from 'svelte';

    export let value;

    const dispatch = createEventDispatcher();
    const onChange = () => dispatch('change', value);
</script>

<select bind:value on:blur on:change={onChange}>
    <slot />
</select>

App.svelte

<script>
    import Select from './Select.svelte';
    let value = '1';

    const onChange = (e) => {
        console.log(e.detail);
    };
</script>

<Select bind:value on:change={onChange}>
    <option value="1">选项1</option>
    <option value="2">选项2</option>
    <option value="3">选项3</option>
</Select>

组件事件不会冒泡,若需要监听某个深度嵌套的组件,则需要在中间组件做转发,若中间组件只是转发事件,不对事件传递的数据做处理,则可以通过on:事件名直接转发(DOM 元素也适用),无需通过createEventDispatcher来触发。

在线 REPL

Level2.svelte

<script>
    import { createEventDispatcher } from 'svelte';

    const dispatch = createEventDispatcher();

    const onClick = () => dispatch('click', 'click');
</script>

<button on:click={onClick}>
    <slot />
</button>

Level1.svelte

<script>
    import { createEventDispatcher } from 'svelte';
    import Level2 from './Level2.svelte';

    const dispatch = createEventDispatcher();

    const onClick = (e) => dispatch('click', `${e.detail} Level1处理了`);
</script>

<Level2>
    Level1未做转发
    <slot />
</Level2>
<Level2 on:click={onClick}>
    Level1处理
    <slot />
</Level2>
<Level2 on:click>
    <slot />
</Level2>

App.svelte

<script>
    import Level1 from './Level1.svelte';

    const onClick = (e) => console.log(e.detail);
</script>

<Level1 on:click={onClick}>点击</Level1>