Skip to main content

{@attach ...}

附件是在元素挂载到 DOM 或 state 读取函数内部更新时在 effect 中运行的函数。

¥Attachments are functions that run in an effect when an element is mounted to the DOM or when state read inside the function updates.

它们可以可选地返回一个函数,该函数在附件重新运行之前调用,或者在元素稍后从 DOM 中移除之后调用。

¥Optionally, they can return a function that is called before the attachment re-runs, or after the element is later removed from the DOM.

附件在 Svelte 5.29 及更高版本中可用。

App
<script>
	/** @type {import('svelte/attachments').Attachment} */
	function myAttachment(element) {
		console.log(element.nodeName); // 'DIV'

		return () => {
			console.log('cleaning up');
		};
	}
</script>

<div {@attach myAttachment}>...</div>
<script lang="ts">
	import type { Attachment } from 'svelte/attachments';

	const myAttachment: Attachment = (element) => {
		console.log(element.nodeName); // 'DIV'

		return () => {
			console.log('cleaning up');
		};
	};
</script>

<div {@attach myAttachment}>...</div>

一个元素可以包含任意数量的附件。

¥An element can have any number of attachments.

附件工厂(Attachment factories)

¥Attachment factories

一个有用的模式是让函数(例如本例中的 tooltip)返回一个附件 (demo):

¥A useful pattern is for a function, such as tooltip in this example, to return an attachment (demo):

App
<script>
	import tippy from 'tippy.js';

	let content = $state('Hello!');

	/**

	 * @param {string} content

	 * @returns {import('svelte/attachments').Attachment}
	 */
	function tooltip(content) {
		return (element) => {
			const tooltip = tippy(element, { content });
			return tooltip.destroy;
		};
	}
</script>

<input bind:value={content} />

<button {@attach tooltip(content)}>
	Hover me
</button>
<script lang="ts">
	import tippy from 'tippy.js';
	import type { Attachment } from 'svelte/attachments';

	let content = $state('Hello!');

	function tooltip(content: string): Attachment {
		return (element) => {
			const tooltip = tippy(element, { content });
			return tooltip.destroy;
		};
	}
</script>

<input bind:value={content} />

<button {@attach tooltip(content)}>
	Hover me
</button>

由于 tooltip(content) 表达式在 effect 中运行,因此每当 content 发生更改时,附件都会被销毁并重新创建。在附件函数首次运行时,读取的任何状态都会发生相同的情况。(如果你不希望如此,请参阅 控制附件重新运行的时间。)

¥Since the tooltip(content) expression runs inside an effect, the attachment will be destroyed and recreated whenever content changes. The same thing would happen for any state read inside the attachment function when it first runs. (If this isn’t what you want, see Controlling when attachments re-run.)

内联附件(Inline attachments)

¥Inline attachments

附件也可以内联创建 (demo):

¥Attachments can also be created inline (demo):

App
<canvas
	width={32}
	height={32}
	{@attach (canvas) => {
		const context = canvas.getContext('2d');

		$effect(() => {
			context.fillStyle = color;
			context.fillRect(0, 0, canvas.width, canvas.height);
		});
	}}
></canvas>

嵌套效果会在 color 发生变化时运行,而外部效果(调用 canvas.getContext(...) 的地方)仅运行一次,因为它不读取任何响应式状态。

将附件传递给组件(Passing attachments to components)

¥Passing attachments to components

当在组件上使用时,{@attach ...} 将创建一个键为 Symbol 的 prop。如果组件将 spreads 属性添加到元素上,则该元素将接收这些附件。

¥When used on a component, {@attach ...} will create a prop whose key is a Symbol. If the component then spreads props onto an element, the element will receive those attachments.

这允许你创建用于扩充元素(demo)的封装器组件:

¥This allows you to create wrapper components that augment elements (demo):

Button
<script>
	/** @type {import('svelte/elements').HTMLButtonAttributes} */
	let { children, ...props } = $props();
</script>

<!-- `props` includes attachments -->
<button {...props}>
	{@render children?.()}
</button>
<script lang="ts">
	import type { HTMLButtonAttributes } from 'svelte/elements';

	let { children, ...props }: HTMLButtonAttributes = $props();
</script>

<!-- `props` includes attachments -->
<button {...props}>
	{@render children?.()}
</button>
App
<script>
	import tippy from 'tippy.js';
	import Button from './Button.svelte';

	let content = $state('Hello!');

	/**

	 * @param {string} content

	 * @returns {import('svelte/attachments').Attachment}
	 */
	function tooltip(content) {
		return (element) => {
			const tooltip = tippy(element, { content });
			return tooltip.destroy;
		};
	}
</script>

<input bind:value={content} />

<Button {@attach tooltip(content)}>
	Hover me
</Button>
<script lang="ts">
	import tippy from 'tippy.js';
	import Button from './Button.svelte';
	import type { Attachment } from 'svelte/attachments';

	let content = $state('Hello!');

	function tooltip(content: string): Attachment {
		return (element) => {
			const tooltip = tippy(element, { content });
			return tooltip.destroy;
		};
	}
</script>

<input bind:value={content} />

<Button {@attach tooltip(content)}>
	Hover me
</Button>

控制附件重新运行的时间(Controlling when attachments re-run)

¥Controlling when attachments re-run

actions 不同,附件是完全响应式的:{@attach foo(bar)} 会在 foobar 发生更改(或读取 foo 中的任何状态)时重新运行:

¥Attachments, unlike actions, are fully reactive: {@attach foo(bar)} will re-run on changes to foo or bar (or any state read inside foo):

function function foo(bar: any): (node: any) => voidfoo(bar) {
	return (node) => {
		veryExpensiveSetupWork(node: anynode);
		update(node: anynode, bar: anybar);
	};
}

在极少数情况下,如果出现问题(例如,如果 foo 执行了昂贵且不可避免的设置工作),请考虑将数据传递到函数内部,并在子效果中读取:

¥In the rare case that this is a problem (for example, if foo does expensive and unavoidable setup work) consider passing the data inside a function and reading it in a child effect:

function function foo(getBar: any): (node: any) => voidfoo(getBar) {
	return (node) => {
		veryExpensiveSetupWork(node: anynode);

		
function $effect(fn: () => void | (() => void)): void
namespace $effect

Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values. The timing of the execution is after the DOM has been updated.

Example:

$effect(() => console.log('The count is now ' + count));

If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.

Does not run during server side rendering.

https://svelte.dev/docs/svelte/$effect

@paramfn The function to execute
$effect
(() => {
update(node: anynode, getBar: anygetBar()); }); } }

以编程方式创建附件(Creating attachments programmatically)

¥Creating attachments programmatically

要将附件添加到将展开到组件或元素的对象,请使用 createAttachmentKey

¥To add attachments to an object that will be spread onto a component or element, use createAttachmentKey.

将操作转换为附件(Converting actions to attachments)

¥Converting actions to attachments

如果你使用的库仅提供操作,则可以使用 fromAction 将它们转换为附件,以便(例如)与组件一起使用它们。

¥If you’re using a library that only provides actions, you can convert them to attachments with fromAction, allowing you to (for example) use them with components.

上一页 下一页