MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Svelte的模板语法详解

2024-07-283.2k 阅读

变量声明与绑定

在Svelte中,变量声明非常简单,就像在JavaScript中一样。例如:

<script>
    let name = 'John';
</script>

<p>{name}</p>

这里,我们声明了一个名为name的变量,并在<p>标签中使用花括号将其插入到模板中。

双向绑定

双向绑定是Svelte模板语法的一个强大功能。它允许我们在组件的状态和DOM元素之间建立双向连接。例如,对于输入框:

<script>
    let message = 'Hello';
</script>

<input type="text" bind:value={message}>
<p>{message}</p>

在这个例子中,输入框的值与message变量双向绑定。当我们在输入框中输入内容时,message变量会更新,同时<p>标签中的内容也会随之更新。

条件绑定

条件绑定允许我们根据某个条件来决定是否绑定一个属性。比如,我们有一个按钮,只有在某个条件为真时才禁用它:

<script>
    let isDisabled = true;
</script>

<button {disabled:isDisabled}>Click me</button>

这里,disabled属性会根据isDisabled的值来决定是否应用到按钮上。

指令

if指令

if指令用于根据条件渲染或不渲染DOM元素。例如:

<script>
    let showMessage = true;
</script>

{#if showMessage}
    <p>This is a message</p>
{/if}

如果showMessagetrue,则<p>标签会被渲染到DOM中;如果为false,则不会渲染。

我们还可以使用else ifelse子句:

<script>
    let status = 'pending';
</script>

{#if status === 'completed'}
    <p>Task completed</p>
{:else if status === 'pending'}
    <p>Task is pending</p>
{:else}
    <p>Unknown status</p>
{/if}

each指令

each指令用于迭代数组并为每个元素渲染一个模板。假设我们有一个数组:

<script>
    let numbers = [1, 2, 3, 4, 5];
</script>

<ul>
    {#each numbers as number}
        <li>{number}</li>
    {/each}
</ul>

在这个例子中,each指令遍历numbers数组,为每个元素渲染一个<li>标签。

我们还可以获取当前元素的索引:

<script>
    let fruits = ['apple', 'banana', 'cherry'];
</script>

<ul>
    {#each fruits as fruit, index}
        <li>{index + 1}. {fruit}</li>
    {/each}
</ul>

这里,index表示当前元素在数组中的索引。

await指令

在处理异步操作时,await指令非常有用。假设我们有一个异步函数来获取数据:

<script>
    async function fetchData() {
        let response = await fetch('https://example.com/api/data');
        return response.json();
    }
    let promise = fetchData();
</script>

{#await promise}
    <p>Loading...</p>
{:then data}
    <pre>{JSON.stringify(data, null, 2)}</pre>
{:catch error}
    <p>Error: {error.message}</p>
{/await}

这里,await指令等待promise解决。在等待过程中,会显示“Loading...”。如果promise成功解决,会显示数据;如果promise被拒绝,会显示错误信息。

组件间通信

父传子

在Svelte中,父组件向子组件传递数据非常直观。假设我们有一个父组件App.svelte和一个子组件Child.svelte

Child.svelte中定义一个接收数据的属性:

<script>
    export let message;
</script>

<p>{message}</p>

App.svelte中使用子组件并传递数据:

<script>
    import Child from './Child.svelte';
    let data = 'Hello from parent';
</script>

<Child message={data} />

这里,App.svelte通过message属性将data传递给Child.svelte

子传父

子组件向父组件传递数据通常通过事件机制。在Child.svelte中:

<script>
    import { createEventDispatcher } from'svelte';
    const dispatch = createEventDispatcher();
    function sendData() {
        dispatch('customEvent', { data: 'Hello from child' });
    }
</script>

<button on:click={sendData}>Send data to parent</button>

App.svelte中监听子组件发出的事件:

<script>
    import Child from './Child.svelte';
    function handleCustomEvent(event) {
        console.log(event.detail.data);
    }
</script>

<Child on:customEvent={handleCustomEvent} />

这里,Child.svelte通过createEventDispatcher创建一个事件分发器,然后在按钮点击时发出customEvent事件,并携带数据。App.svelte监听这个事件并处理数据。

样式

组件内样式

Svelte允许我们在组件内部定义样式,这些样式只作用于当前组件。例如:

<script>
    let textColor = 'blue';
</script>

<style>
    p {
        color: {textColor};
    }
</style>

<p>This text has a dynamic color</p>

在这个例子中,<style>标签内的样式只应用于当前组件的<p>标签,并且颜色是根据textColor变量动态变化的。

全局样式

如果我们想定义全局样式,可以在项目的根目录下创建一个global.css文件,并在main.js中引入:

import './global.css';

global.css中定义的样式将应用于整个应用程序。

模板表达式

算术表达式

我们可以在模板中使用算术表达式。例如:

<script>
    let num1 = 5;
    let num2 = 3;
</script>

<p>{num1 + num2}</p>
<p>{num1 - num2}</p>
<p>{num1 * num2}</p>
<p>{num1 / num2}</p>

这里,分别展示了加法、减法、乘法和除法的算术表达式。

逻辑表达式

逻辑表达式也可以在模板中使用。比如:

<script>
    let isTrue = true;
    let isFalse = false;
</script>

<p>{isTrue && 'This is true'}</p>
<p>{isFalse || 'This is false'}</p>
<p>{!isFalse}</p>

这里,展示了逻辑与、逻辑或和逻辑非的使用。

三元表达式

三元表达式在模板中用于根据条件返回不同的值:

<script>
    let age = 18;
</script>

<p>{age >= 18? 'Adult' : 'Minor'}</p>

如果age大于等于18,会显示“Adult”,否则显示“Minor”。

绑定HTML和SVG

绑定HTML

有时候我们需要动态渲染HTML内容。Svelte提供了{@html}指令来实现这一点。例如:

<script>
    let htmlContent = '<strong>Bold text</strong>';
</script>

{@html htmlContent}

这里,htmlContent中的HTML标签会被解析并渲染为实际的HTML元素。但要注意,使用{@html}时要小心,因为它可能会导致跨站脚本(XSS)攻击,如果内容来自不可信的源。

绑定SVG

在Svelte中使用SVG也很方便。我们可以直接在模板中编写SVG代码,并且可以绑定变量到SVG属性上。例如:

<script>
    let circleRadius = 50;
</script>

<svg width="200" height="200">
    <circle cx="100" cy="100" {r:circleRadius} fill="red" />
</svg>

这里,circle元素的半径r是根据circleRadius变量动态变化的。

事件处理

常见DOM事件

Svelte可以很容易地处理常见的DOM事件,如点击、鼠标移动等。例如,处理按钮点击事件:

<script>
    function handleClick() {
        console.log('Button clicked');
    }
</script>

<button on:click={handleClick}>Click me</button>

这里,当按钮被点击时,handleClick函数会被调用。

处理鼠标移动事件:

<script>
    function handleMouseMove(event) {
        console.log(`Mouse position: ${event.clientX}, ${event.clientY}`);
    }
</script>

<div on:mousemove={handleMouseMove}>Move your mouse here</div>

当鼠标在<div>元素内移动时,handleMouseMove函数会被调用,并传入event对象,我们可以从中获取鼠标的位置等信息。

自定义事件

除了处理DOM事件,我们还可以创建和处理自定义事件。在一个组件中创建自定义事件:

<script>
    import { createEventDispatcher } from'svelte';
    const dispatch = createEventDispatcher();
    function sendCustomEvent() {
        dispatch('myCustomEvent', { data: 'Some data' });
    }
</script>

<button on:click={sendCustomEvent}>Send custom event</button>

在父组件中监听这个自定义事件:

<script>
    import MyComponent from './MyComponent.svelte';
    function handleCustomEvent(event) {
        console.log(event.detail.data);
    }
</script>

<MyComponent on:myCustomEvent={handleCustomEvent} />

当在子组件中点击按钮时,会发出myCustomEvent自定义事件,父组件可以监听到并处理事件携带的数据。

过渡与动画

过渡效果

Svelte提供了内置的过渡效果,可以轻松地为元素添加过渡动画。例如,淡入过渡:

<script>
    import { fade } from'svelte/transition';
    let showElement = true;
</script>

<button on:click={() => showElement =!showElement}>Toggle element</button>

{#if showElement}
    <div transition:fade>
        This element has a fade transition
    </div>
{/if}

这里,fade过渡效果会在元素显示和隐藏时添加淡入淡出的动画。

动画效果

动画效果允许我们创建更复杂的动态效果。例如,创建一个旋转动画:

<script>
    import { animate } from'svelte/animate';
    let rotation = 0;
    function startAnimation() {
        animate(
            $: rotation,
            360,
            {
                duration: 2000,
                easing: 'linear',
                repeat: Infinity
            }
        );
    }
</script>

<button on:click={startAnimation}>Start animation</button>

<div style="transform: rotate({rotation}deg)">
    This div is rotating
</div>

在这个例子中,点击按钮后,div元素会在2秒内以线性方式旋转360度,并无限重复。

插槽(Slots)

匿名插槽

插槽允许我们在组件中插入自定义内容。匿名插槽是最基本的形式。例如,在一个Card.svelte组件中:

<div class="card">
    <slot></slot>
</div>

在父组件中使用Card.svelte

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

<Card>
    <h2>Card title</h2>
    <p>Card content</p>
</Card>

这里,<Card>标签内的内容会被插入到Card.svelte组件的<slot>位置。

具名插槽

具名插槽允许我们在组件中定义多个插槽,并在父组件中指定内容插入到哪个插槽。例如,在Layout.svelte组件中:

<div class="layout">
    <slot name="header"></slot>
    <slot></slot>
    <slot name="footer"></slot>
</div>

在父组件中使用Layout.svelte

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

<Layout>
    <h1 slot="header">Page header</h1>
    <p>Main content</p>
    <p slot="footer">Page footer</p>
</Layout>

这里,h1标签会插入到名为header的插槽,p标签(没有指定插槽名)会插入到默认插槽,另一个p标签会插入到名为footer的插槽。

响应式声明

自动响应式

Svelte会自动追踪变量的变化,并在变量改变时更新相关的DOM。例如:

<script>
    let count = 0;
    function increment() {
        count++;
    }
</script>

<p>{count}</p>
<button on:click={increment}>Increment</button>

当点击按钮时,count变量增加,<p>标签中的内容会自动更新。

手动响应式声明

有时候我们需要手动控制响应式行为。可以使用$:语法。例如:

<script>
    let num1 = 5;
    let num2 = 3;
    $: sum = num1 + num2;
</script>

<p>{sum}</p>
<button on:click={() => num1++}>Increment num1</button>
<button on:click={() => num2++}>Increment num2</button>

这里,当num1num2改变时,sum会根据$:声明的表达式重新计算,并且<p>标签中的内容会更新。

生命周期钩子

onMount

onMount钩子在组件被插入到DOM后立即执行。例如:

<script>
    import { onMount } from'svelte';
    onMount(() => {
        console.log('Component mounted');
    });
</script>

<p>This is a component</p>

当组件被渲染到DOM中时,会在控制台输出“Component mounted”。

beforeUpdate

beforeUpdate钩子在组件的状态发生变化,且DOM即将更新之前执行。例如:

<script>
    import { beforeUpdate } from'svelte';
    let count = 0;
    function increment() {
        count++;
    }
    beforeUpdate(() => {
        console.log('Before update, count is', count);
    });
</script>

<p>{count}</p>
<button on:click={increment}>Increment</button>

当点击按钮增加count时,在DOM更新之前,会在控制台输出“Before update, count is”以及当前count的值。

afterUpdate

afterUpdate钩子在组件的状态发生变化,且DOM已经更新之后执行。例如:

<script>
    import { afterUpdate } from'svelte';
    let count = 0;
    function increment() {
        count++;
    }
    afterUpdate(() => {
        console.log('After update, count is', count);
    });
</script>

<p>{count}</p>
<button on:click={increment}>Increment</button>

当点击按钮增加count,DOM更新完成后,会在控制台输出“After update, count is”以及当前count的值。

onDestroy

onDestroy钩子在组件从DOM中移除之前执行。例如:

<script>
    import { onDestroy } from'svelte';
    onDestroy(() => {
        console.log('Component is being destroyed');
    });
</script>

<p>This is a component</p>

当组件从DOM中移除时,会在控制台输出“Component is being destroyed”。

总结

Svelte的模板语法提供了丰富而强大的功能,从基本的变量绑定、指令,到组件间通信、样式处理、过渡动画等。通过深入理解和熟练运用这些语法,开发者可以高效地构建出交互式、动态且美观的前端应用程序。无论是小型项目还是大型企业级应用,Svelte的模板语法都能满足开发需求,帮助开发者实现各种复杂的功能和设计。希望通过本文的详细介绍,读者对Svelte的模板语法有了更全面、深入的理解,能够在实际开发中灵活运用,创造出优秀的前端作品。