Svelte系列 --- 模板逻辑

773 阅读2分钟

除去脚本(数据处理),组件的另一个核心点是 UI.

但是用于描述UI的html是一种标记语言,所以这也就决定了html本身无法表达逻辑

因此,Svelte 为模板增加了一些逻辑支持,譬如判断、遍历等,以增强UI 的逻辑表达能力。

特点

  1. Svelte 的编译器将会编译这些逻辑,通过将 HTML 编译成使用原生 JS 创建的形式,也即这些 HTML 最终将使用 createElement 等原生的 DOM API 来创建。
  2. 逻辑标识符,一般使用{}进行包裹,{#xxx}表示某一个逻辑块的开始,{:xxx}表示某一个逻辑块的继续或中转,{/xxx}表示某一个逻辑块的结束

条件判断

if

{#if user.loggedIn}
  <button on:click={toggle}>Log out</button>
{/if}

if ... else ...

{#if user.loggedIn}
  <button on:click={toggle}>Log out</button>
{:else}
  <button on:click={toggle}>Log in</button>
{/if}

if ... else if ...else...

{#if x > 10}
  <p>{x} 大于 10</p>
{:else if 5 > x}
  <p>{x} 小于 5</p>
{:else}
  <p>{x} 在 5 和 10 之间</p>
{/if}

循环

没有key的each块

<!-- each array as value, index -->
{#each cats as cat, i} <!-- 也可以使用解构 {#each cats as { id, name }, i} -->
  <li>
    <a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
      {i + 1}:{cat.name}
    </a>
  </li>
{/each}

提供key的each块

  1. svlete中没有了virtual dom的概念,所以svelte中的key不会参与到DOM Diff
  2. svelte不添加key并不会报错或警告,可以不加
  3. 默认情况下,当你修改 each 块的值时,它将该块的 结束 位置添加和删除项,并更新所有已更改的值。不过那可能不是你想要的。
  4. svelte中的key也是给组件一个唯一标识ID,便于svelte更好的识别各个组件对象,并进行更新
<script>
    import Thing from './Thing.svelte';

    let things = [
	{ id: 1, name: 'apple' },
	{ id: 2, name: 'banana' },
	{ id: 3, name: 'carrot' },
	{ id: 4, name: 'doughnut' },
	{ id: 5, name: 'egg' },
    ];

    function handleClick() {
        things = things.slice(1);
	console.log(things); 
        /* => 数组正常发生了更改,但是没有key的时候,UI的更新可能会出现问题
            [
    		{ id: 2, name: 'banana' },
    		{ id: 3, name: 'carrot' },
    		{ id: 4, name: 'doughnut' },
		{ id: 5, name: 'egg' }
            ]
	*/
    }
</script>

<button on:click={handleClick}>
    Remove first thing
</button>

<!-- each array as value, key (id) -->
{#each things as thing (thing.id)}
    <Thing name={thing.name}/>
{/each}

<!--
  1. 在svelte内部使用的是`map`结构在实现key值的绑定
  2. 所以原则上,可以使用任意的对象来作为svelte中的key
  3. 但是推荐使用字符串或者数字作为key,因为字符串或数字通常会更安全,因为这可以保证key的唯一不变性
     然而对象毕竟是引用数据类型,作为key的只是引用,无法保证所引用的那个对象中的属性和值不会发生改变
     这也就是为什么不推荐使用index(索引)作为key的原因
     因为此时如果中间插入了一个元素,那么后边所有元素的key都会发生改变,无法保证key的唯一不变性
  4. key 目的是便于svlete计算出哪些是没有变化的元素,这样可以不需要重新创建它,以便减少 DOM 渲染的工作量
-->

渲染对象长度为零

<script>
    let cats = []
</script>

<!-- 
    如果需要渲染的对象中没有内容,即length为0,那么对应的位置将不会渲染任何的内容。 
    所以此时界面上空空如也,并没有渲染出任何的内容
-->
{#each cats as cat, i} 
  <li>
    {i} --- {cat}
  </li>
{/each}

await块

这是Svelte中特有的逻辑块

以便于 Svelte程序必须在某个时候处理异步数据

<script>
  // getRandomNumber是一个异步请求,返回的是一个promise
  let getNumberPromise = getRandomNumber();
</script>

<!-- number是一个异步操作返回的promise -->
{#await getNumberPromise}
    <!-- 状态为pending时候进行的操作  -->
    <p>loading ...</p>
{:then number}
    <!-- 状态为resolve时候进行的操作,后面的number是成功后返回的结果 -->
    <p>{number}</p>
{:catch error}
    <!-- 状态为reject时候进行的操作, error是返回的错误对象 -->
    <p>{error.message}</p>
{/await}
<!--
    很多情况下,我们可能并不需要处理状态为pending或reject的情况
    所以此时可以对await块进行简写操作
-->

<!-- 省略状态为pending时候的操作 -->
{#await getNumberPromise then number}
    <p>{number}</p>
{:catch error}
    <p>{error.message}</p>
{/await}

<!-- 同时省略状态为pending和reject的时候对promise结果的处理 -->
{#await getNumberPromise then number}
    <p>{number}</p>
{/await}

key

<script>
    let num = 0;
    let opacity = 1
	
    function addNumber() {
	opacity = 0
		
        // 淡出后。修改num的值,并执行淡入效果
	setTimeout(() => {
            num = num + 2
            opacity = 1
	}, 500)
    }
</script>

<!-- 使用transition 实现在切换num值的时候的 淡出和淡入效果 -->
<p style="transition: opacity 0.5s ease; opacity: {opacity};">{ num }</p>
<button on:click="{addNumber}">+2</button>
<!--
    如果按照上述例子的写法,显然是比较繁琐的
    为此,svlete将一些简单的动画进行了封装
-->
<script>
    import { fade } from 'svelte/transition';
    let num = 0;
</script>

<!-- 
    fade只有在创建元素的时候才会生效,所以此时需要为p元素包裹key块,不包裹的时候是不会产生渐入渐出效果
    key块的作用和each块中key的作用是一致,当num发生改变的时候,svelte会自动销毁并重新创建p标签
-->
{#key num}
    <p in:fade>{ num }</p>
{/key}
<button on:click="{() => num += 2}">+2</button>