跳到主要内容

模板语法

元素指令

在 GitHub 上编辑此页面

除了属性之外,元素还可以有指令,它们以某种方式控制元素的行为。

on:eventname

on:eventname={handler}
on:eventname|modifiers={handler}

使用 on: 指令来监听 DOM 事件。

App.svelte
<script>
	let count = 0;

	/** @param {MouseEvent} event */
	function handleClick(event) {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	count: {count}
</button>
App.svelte
<script lang="ts">
	let count = 0;
	
	function handleClick(event: MouseEvent) {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	count: {count}
</button>

处理程序可以内联声明,而不会造成性能损失。与属性一样,为了语法高亮,指令值可以加引号。

<button on:click={() => (count += 1)}>
	count: {count}
</button>

使用 | 字符将修饰符添加到 DOM 事件。

<form on:submit|preventDefault={handleSubmit}>
	<!-- the `submit` event's default is prevented,
		 so the page won't reload -->
</form>

可以使用以下修饰符

  • preventDefault — 在运行处理程序之前调用 event.preventDefault()
  • stopPropagation — 调用 event.stopPropagation(),防止事件到达下一个元素
  • stopImmediatePropagation - 调用 event.stopImmediatePropagation(),防止触发同一事件的其他侦听器。
  • passive — 提高触控/滚轮事件的滚动性能(Svelte 会在安全的情况下自动添加它)
  • nonpassive — 明确设置 passive: false
  • capture — 在捕获阶段而不是冒泡阶段触发处理程序
  • once — 在首次运行后移除处理程序
  • self — 仅当event.target是元素本身时才触发处理程序
  • trusted — 仅当event.isTrustedtrue时才触发处理程序。即,如果事件是由用户操作触发的。

修饰符可以链接在一起,例如on:click|once|capture={...}

如果on:指令在没有值的情况下使用,组件将转发事件,这意味着组件的使用者可以监听它。

<button on:click> The component itself will emit the click event </button>

可以为同一事件有多个事件监听器

<script>
	let counter = 0;
	function increment() {
		counter = counter + 1;
	}

	/** @param {MouseEvent} event */
	function track(event) {
		trackEvent(event);
	}
</script>

<button on:click={increment} on:click={track}>Click me!</button>

bind:property

bind:property={variable}

数据通常从父级向下流向子级。bind:指令允许数据从子级向上流向父级。大多数绑定特定于特定元素。

最简单的绑定反映属性的值,例如input.value

<input bind:value={name} />
<textarea bind:value={text} />

<input type="checkbox" bind:checked={yes} />

如果名称与值匹配,可以使用简写。

<input bind:value />
<!-- equivalent to
<input bind:value={value} />
-->

数字输入值被强制转换;即使input.value在 DOM 中是字符串,Svelte 也会将其视为数字。如果输入为空或无效(在type="number"的情况下),则值为null

<input type="number" bind:value={num} />
<input type="range" bind:value={num} />

在具有type="file"<input>元素上,可以使用bind:files获取所选文件的FileList。它是只读的。

<label for="avatar">Upload a picture:</label>
<input accept="image/png, image/jpeg" bind:files id="avatar" name="avatar" type="file" />

如果将bind:指令与on:指令一起使用,则它们在定义中的顺序会影响调用事件处理程序时绑定的变量的值。

<script>
	let value = 'Hello World';
</script>

<input
	on:input={() => console.log('Old value:', value)}
	bind:value
	on:input={() => console.log('New value:', value)}
/>

在这里,我们绑定到文本输入的值,它使用input事件。其他元素上的绑定可能使用不同的事件,例如change

绑定 <select> 值

<select>值绑定对应于所选<option>上的value属性,该属性可以是任何值(不仅仅是字符串,如 DOM 中通常的情况)。

<select bind:value={selected}>
	<option value={a}>a</option>
	<option value={b}>b</option>
	<option value={c}>c</option>
</select>

<select multiple>元素的行为类似于复选框组。绑定的变量是一个数组,其中每个条目对应于每个所选<option>value属性。

<select multiple bind:value={fillings}>
	<option value="Rice">Rice</option>
	<option value="Beans">Beans</option>
	<option value="Cheese">Cheese</option>
	<option value="Guac (extra)">Guac (extra)</option>
</select>

<option>的值与其文本内容匹配时,可以省略该属性。

<select multiple bind:value={fillings}>
	<option>Rice</option>
	<option>Beans</option>
	<option>Cheese</option>
	<option>Guac (extra)</option>
</select>

具有contenteditable属性的元素支持以下绑定

它们之间有细微的差别,可以在这里了解更多信息

<div contenteditable="true" bind:innerHTML={html} />

<details>元素支持绑定到open属性。

<details bind:open={isOpen}>
	<summary>Details</summary>
	<p>Something small enough to escape casual notice.</p>
</details>

媒体元素绑定

媒体元素(<audio><video>)有自己的一组绑定——七个只读绑定...

  • duration(只读)——视频的总时长,单位为秒
  • buffered(只读)——{start, end} 对象的数组
  • played(只读)——同上
  • seekable(只读)——同上
  • seeking(只读)——布尔值
  • ended(只读)——布尔值
  • readyState(只读)——0 到 4(包括 0 和 4)之间的数字

...和五个双向绑定

  • currentTime——视频中当前的播放时间,单位为秒
  • playbackRate——播放视频的速度,其中 1 为“正常”
  • paused——这个应该不言自明
  • volume——0 到 1 之间的值
  • muted——表示播放器是否静音的布尔值

视频还具有只读的 videoWidthvideoHeight 绑定。

<video
	src={clip}
	bind:duration
	bind:buffered
	bind:played
	bind:seekable
	bind:seeking
	bind:ended
	bind:readyState
	bind:currentTime
	bind:playbackRate
	bind:paused
	bind:volume
	bind:muted
	bind:videoWidth
	bind:videoHeight
/>

图像元素绑定

图像元素(<img>)有两个只读绑定

  • naturalWidth(只读)——图像的原始宽度,在图像加载后可用
  • naturalHeight(只读)——图像的原始高度,在图像加载后可用
<img
	bind:naturalWidth
	bind:naturalHeight
></img>

块级元素绑定

块级元素有 4 个只读绑定,使用类似于 此方法 进行测量

  • clientWidth
  • clientHeight
  • offsetWidth
  • offsetHeight
<div bind:offsetWidth={width} bind:offsetHeight={height}>
	<Chart {width} {height} />
</div>

bind:group

bind:group={variable}

一起工作的输入可以使用 bind:group

<script>
	let tortilla = 'Plain';

	/** @type {Array<string>} */
	let fillings = [];
</script>

<!-- grouped radio inputs are mutually exclusive -->
<input type="radio" bind:group={tortilla} value="Plain" />
<input type="radio" bind:group={tortilla} value="Whole wheat" />
<input type="radio" bind:group={tortilla} value="Spinach" />

<!-- grouped checkbox inputs populate an array -->
<input type="checkbox" bind:group={fillings} value="Rice" />
<input type="checkbox" bind:group={fillings} value="Beans" />
<input type="checkbox" bind:group={fillings} value="Cheese" />
<input type="checkbox" bind:group={fillings} value="Guac (extra)" />

bind:group 仅在输入位于同一 Svelte 组件中时才有效。

bind:this

bind:this={dom_node}

要获取对 DOM 节点的引用,请使用 bind:this

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

	/** @type {HTMLCanvasElement} */
	let canvasElement;

	onMount(() => {
		const ctx = canvasElement.getContext('2d');
		drawStuff(ctx);
	});
</script>

<canvas bind:this={canvasElement} />

class:name

class:name={value}
class:name

class: 指令提供了一种更简便的方法来切换元素上的类。

<!-- These are equivalent -->
<div class={isActive ? 'active' : ''}>...</div>
<div class:active={isActive}>...</div>

<!-- Shorthand, for when name and value match -->
<div class:active>...</div>

<!-- Multiple class toggles can be included -->
<div class:active class:inactive={!active} class:isAdmin>...</div>

style:property

style:property={value}
style:property="value"
style:property

style: 指令提供了一种简写,用于在元素上设置多个样式。

<!-- These are equivalent -->
<div style:color="red">...</div>
<div style="color: red;">...</div>

<!-- Variables can be used -->
<div style:color={myColor}>...</div>

<!-- Shorthand, for when property and variable name match -->
<div style:color>...</div>

<!-- Multiple styles can be included -->
<div style:color style:width="12rem" style:background-color={darkMode ? 'black' : 'white'}>...</div>

<!-- Styles can be marked as important -->
<div style:color|important="red">...</div>

style: 指令与 style 属性结合使用时,指令将优先。

<div style="color: blue;" style:color="red">This will be red</div>

use:action

use:action
use:action={parameters}
ts
action = (node: HTMLElement, parameters: any) => {
update?: (parameters: any) => void,
destroy?: () => void
}

动作是在创建元素时调用的函数。它们可以返回一个带有 destroy 方法的对象,该方法在卸载元素后调用

<script>
	/** @type {import('svelte/action').Action}  */
	function foo(node) {
		// the node has been mounted in the DOM

		return {
			destroy() {
				// the node has been removed from the DOM
			}
		};
	}
</script>

<div use:foo />

动作可以有一个参数。如果返回值有 update 方法,则每当该参数更改时都会调用该方法,即 Svelte 将更新应用到标记后立即调用。

不要担心我们为每个组件实例重新声明 foo 函数——Svelte 会将不依赖于本地状态的任何函数提升到组件定义之外。

<script>
	export let bar;

	/** @type {import('svelte/action').Action}  */
	function foo(node, bar) {
		// the node has been mounted in the DOM

		return {
			update(bar) {
				// the value of `bar` has changed
			},

			destroy() {
				// the node has been removed from the DOM
			}
		};
	}
</script>

<div use:foo={bar} />

svelte/action 页面中阅读更多内容。

transition:fn

transition:fn
transition:fn={params}
transition:fn|global
transition:fn|global={params}
transition:fn|local
transition:fn|local={params}
ts
transition = (node: HTMLElement, params: any, options: { direction: 'in' | 'out' | 'both' }) => {
delay?: number,
duration?: number,
easing?: (t: number) => number,
css?: (t: number, u: number) => string,
tick?: (t: number, u: number) => void
}

当元素进入或离开 DOM(由于状态更改)时,会触发过渡。

当一个块正在过渡时,块内的所有元素(包括那些没有自己的过渡的元素)都会保留在 DOM 中,直到该块中的所有过渡都已完成。

transition: 指令表示一个双向过渡,这意味着它可以在过渡进行时平滑地逆转。

{#if visible}
	<div transition:fade>fades in and out</div>
{/if}

默认情况下,过渡是本地的(在 Svelte 3 中,它们默认是全局的)。本地过渡仅在它们所属的块被创建或销毁时播放,而不是在父块被创建或销毁时播放。

{#if x}
	{#if y}
		<!-- Svelte 3: <p transition:fade|local> -->
		<p transition:fade>fades in and out only when y changes</p>

		<!-- Svelte 3: <p transition:fade> -->
		<p transition:fade|global>fades in and out when x or y change</p>
	{/if}
{/if}

默认情况下,首次渲染时不会播放引入过渡。你可以通过在 创建组件 时设置 intro: true 并将过渡标记为 global 来修改此行为。

过渡参数

与动作一样,过渡也可以有参数。

(双重 {{大括号}} 不是特殊语法;这是一个表达式标记内的对象字面量。)

{#if visible}
	<div transition:fade={{ duration: 2000 }}>fades in and out over two seconds</div>
{/if}

自定义过渡函数

过渡可以使用自定义函数。如果返回的对象具有 css 函数,Svelte 将创建一个在元素上播放的 CSS 动画。

传递给 csst 参数是在应用 easing 函数后介于 01 之间的值。进入 过渡从 0 运行到 1退出 过渡从 1 运行到 0 — 换句话说,1 是元素的自然状态,就好像没有应用过渡一样。u 参数等于 1 - t

该函数在过渡开始之前被重复调用,带有不同的 tu 参数。

<script>
	import { elasticOut } from 'svelte/easing';

	/** @type {boolean} */
	export let visible;

	/**
	 * @param {HTMLElement} node
	 * @param {{ delay?: number, duration?: number, easing?: (t: number) => number }} params
	 */
	function whoosh(node, params) {
		const existingTransform = getComputedStyle(node).transform.replace('none', '');

		return {
			delay: params.delay || 0,
			duration: params.duration || 400,
			easing: params.easing || elasticOut,
			css: (t, u) => `transform: ${existingTransform} scale(${t})`
		};
	}
</script>

{#if visible}
	<div in:whoosh>whooshes in</div>
{/if}

自定义过渡函数还可以返回一个 tick 函数,该函数在过渡期间使用相同的 tu 参数调用。

如果可以使用 css 代替 tick,请这样做 — CSS 动画可以在主线程之外运行,防止在较慢的设备上出现卡顿。

App.svelte
<script>
	export let visible = false;

	/**
	 * @param {HTMLElement} node
	 * @param {{ speed?: number }} params
	 */
	function typewriter(node, { speed = 1 }) {
		const valid = node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE;

		if (!valid) {
			throw new Error(`This transition only works on elements with a single text node child`);
		}

		const text = node.textContent;
		const duration = text.length / (speed * 0.01);

		return {
			duration,
			tick: (t) => {
				const i = ~~(text.length * t);
				node.textContent = text.slice(0, i);
			}
		};
	}
</script>

{#if visible}
	<p in:typewriter={{ speed: 1 }}>The quick brown fox jumps over the lazy dog</p>
{/if}
App.svelte
<script lang="ts">
	export let visible = false;
	
	function typewriter(node: HTMLElement, { speed = 1 }: { speed?: number }) {
		const valid = node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE;
	
		if (!valid) {
			throw new Error(`This transition only works on elements with a single text node child`);
		}
	
		const text = node.textContent;
		const duration = text.length / (speed * 0.01);
	
		return {
			duration,
			tick: (t) => {
				const i = ~~(text.length * t);
				node.textContent = text.slice(0, i);
			},
		};
	}
</script>

{#if visible}
	<p in:typewriter={{ speed: 1 }}>The quick brown fox jumps over the lazy dog</p>
{/if}

如果一个过渡返回一个函数而不是一个过渡对象,那么该函数将在下一个微任务中被调用。这允许多个过渡进行协调,从而实现交叉淡化效果

过渡函数还接收第三个参数options,其中包含有关过渡的信息。

options对象中可用的值是

  • direction - inoutboth之一,具体取决于过渡的类型

过渡事件

除了任何标准 DOM 事件外,具有过渡的元素还将分派以下事件

  • introstart
  • introend
  • outrostart
  • outroend
{#if visible}
	<p
		transition:fly={{ y: 200, duration: 2000 }}
		on:introstart={() => (status = 'intro started')}
		on:outrostart={() => (status = 'outro started')}
		on:introend={() => (status = 'intro ended')}
		on:outroend={() => (status = 'outro ended')}
	>
		Flies in and out
	</p>
{/if}

in:fn/out:fn

in:fn
in:fn={params}
in:fn|global
in:fn|global={params}
in:fn|local
in:fn|local={params}
out:fn
out:fn={params}
out:fn|global
out:fn|global={params}
out:fn|local
out:fn|local={params}

类似于transition:,但仅适用于进入 (in:) 或离开 (out:) DOM 的元素。

transition:不同,使用in:out:应用的过渡不是双向的——如果在过渡进行中 outro 了块,则进入过渡将继续与退出过渡“一起播放”,而不是反向。如果中止了退出过渡,过渡将从头开始重新启动。

{#if visible}
	<div in:fly out:fade>flies in, fades out</div>
{/if}

animate:fn

animate:name
animate:name={params}
ts
animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => {
delay?: number,
duration?: number,
easing?: (t: number) => number,
css?: (t: number, u: number) => string,
tick?: (t: number, u: number) => void
}
ts
DOMRect {
bottom: number,
height: number,
​​left: number,
right: number,
top: number,
width: number,
x: number,
y: number
}

键控 each 块的内容重新排序时,将触发动画。当添加或删除元素时,动画不会运行,仅当 each 块中现有数据项的索引更改时才会运行。Animate 指令必须位于键控 each 块的直接子元素上。

动画可与 Svelte 的内置动画函数自定义动画函数一起使用。

<!-- When `list` is reordered the animation will run-->
{#each list as item, index (item)}
	<li animate:flip>{item}</li>
{/each}

动画参数

与动作和过渡一样,动画也可以有参数。

(双重 {{大括号}} 不是特殊语法;这是一个表达式标记内的对象字面量。)

{#each list as item, index (item)}
	<li animate:flip={{ delay: 500 }}>{item}</li>
{/each}

自定义动画函数

动画可以使用自定义函数,这些函数提供nodeanimation对象和任何parameters作为参数。animation参数是一个包含fromto属性的对象,每个属性都包含一个DOMRect,描述了元素在其startend位置的几何形状。from属性是在其起始位置的元素的 DOMRect,而to属性是在列表重新排序并更新 DOM 后的最终位置的元素的 DOMRect。

如果返回的对象具有 css 方法,Svelte 将创建一个在元素上播放的 CSS 动画。

传递给 csst 参数是一个值,在应用 easing 函数后从 01 开始。u 参数等于 1 - t

该函数在动画开始之前重复调用,并带有不同的 tu 参数。

<script>
	import { cubicOut } from 'svelte/easing';

	/**
	 * @param {HTMLElement} node
	 * @param {{ from: DOMRect; to: DOMRect }} states
	 * @param {any} params
	 */
	function whizz(node, { from, to }, params) {
		const dx = from.left - to.left;
		const dy = from.top - to.top;

		const d = Math.sqrt(dx * dx + dy * dy);

		return {
			delay: 0,
			duration: Math.sqrt(d) * 120,
			easing: cubicOut,
			css: (t, u) => `transform: translate(${u * dx}px, ${u * dy}px) rotate(${t * 360}deg);`
		};
	}
</script>

{#each list as item, index (item)}
	<div animate:whizz>{item}</div>
{/each}

自定义动画函数还可以返回一个 tick 函数,该函数在动画期间使用相同的 tu 参数调用。

如果可以使用 css 代替 tick,请这样做 — CSS 动画可以在主线程之外运行,防止在较慢的设备上出现卡顿。

<script>
	import { cubicOut } from 'svelte/easing';

	/**
	 * @param {HTMLElement} node
	 * @param {{ from: DOMRect; to: DOMRect }} states
	 * @param {any} params
	 */
	function whizz(node, { from, to }, params) {
		const dx = from.left - to.left;
		const dy = from.top - to.top;

		const d = Math.sqrt(dx * dx + dy * dy);

		return {
			delay: 0,
			duration: Math.sqrt(d) * 120,
			easing: cubicOut,
			tick: (t, u) => Object.assign(node.style, { color: t > 0.5 ? 'Pink' : 'Blue' })
		};
	}
</script>

{#each list as item, index (item)}
	<div animate:whizz>{item}</div>
{/each}
上一个 特殊标记
下一个 组件指令