Svelte 最新中文文档翻译(7)—— snippet 与 @render

news/2025/2/13 20:03:37/文章来源:https://www.cnblogs.com/yayujs/p/18714337

前言

Svelte,一个非常“有趣”、用起来“很爽”的前端框架。从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1

image.png

Svelte 以其独特的编译时优化机制著称,具有轻量级高性能易上手等特性,非常适合构建轻量级 Web 项目,也是我做个人项目的首选技术栈。

目前 Svelte 基于 Svelte 5 发布了最新的官方文档,但却缺少对应的中文文档。为了帮助大家学习 Svelte,为爱发电翻译了官方文档。

我同时搭建了 Svelte 最新的中文文档站点:https://svelte.yayujs.com ,如果需要辅助学习,也可以入手我的小册《Svelte 开发指南》,语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

虽说是翻译,但个人并不喜欢严格遵守原文,为了保证中文阅读流畅,会删减部分语句,对难懂的部分也会另做补充解释,希望能给大家带来一个好的中文学习体验。

欢迎围观我的“朋友圈”、加入“低调务实优秀中国好青年”前端社群,分享技术,带你成长。

snippet

{#snippet name()}...{/snippet}
{#snippet name(param1, param2, paramN)}...{/snippet}

代码片段和 渲染标签 是在组件内部创建可复用标记块的一种方法。与其编写这样的重复代码...

{#each images as image}{#if image.href}<a href={image.href}><figure><img src={image.src} alt={image.caption} width={image.width} height={image.height} /><figcaption>{image.caption}</figcaption></figure></a>{:else}<figure><img src={image.src} alt={image.caption} width={image.width} height={image.height} /><figcaption>{image.caption}</figcaption></figure>{/if}
{/each}

...你可以这样写:

{#snippet figure(image)}<figure><img src={image.src} alt={image.caption} width={image.width} height={image.height} /><figcaption>{image.caption}</figcaption></figure>
{/snippet}{#each images as image}{#if image.href}<a href={image.href}>{@render figure(image)}</a>{:else}{@render figure(image)}{/if}
{/each}

像函数声明一样,代码片段可以有任意数量的参数,这些参数可以有默认值,并且你可以对每个参数进行解构。然而,你不能使用剩余参数。

代码片段作用域

代码片段可以在组件的任何地方声明。它们可以引用在自身之外声明的值,例如在 <script> 标签或 {#each ...} 块中 (demo)...

<script>let { message = `很高兴见到你!` } = $props();
</script>{#snippet hello(name)}<p>你好 {name}! {message}!</p>
{/snippet}{@render hello('alice')}
{@render hello('bob')}

...并且它们对同一词法作用域中的所有内容都是"可见的"(即兄弟节点和这些兄弟节点的子节点):

<div>{#snippet x()}{#snippet y()}...{/snippet}<!-- 这很好 -->{@render y()}{/snippet}<!-- 这将出错,因为 `y` 不在作用域中 -->{@render y()}
</div><!-- 这也将出错,因为 `x` 不在作用域中 -->
{@render x()}

代码片段可以引用自身和其他片段 (demo):

{#snippet blastoff()}<span>🚀</span>
{/snippet}{#snippet countdown(n)}{#if n > 0}<span>{n}...</span>{@render countdown(n - 1)}{:else}{@render blastoff()}{/if}
{/snippet}{@render countdown(10)}

将代码片段传递给组件

在模板中,代码片段和其他值一样。这样,它们可以作为 props 传递给组件 (demo):

<script>import Table from './Table.svelte';const fruits = [{ name: '苹果', qty: 5, price: 2 },{ name: '香蕉', qty: 10, price: 1 },{ name: '樱桃', qty: 20, price: 0.5 }];
</script>{#snippet header()}<th>水果</th><th>数量</th><th>价格</th><th>总计</th>
{/snippet}{#snippet row(d)}<td>{d.name}</td><td>{d.qty}</td><td>{d.price}</td><td>{d.qty * d.price}</td>
{/snippet}<Table data={fruits} {header} {row} />

把它想象成向组件传递内容而非数据。这个概念类似于 Web 组件中的插槽。

为了方便,直接在组件内部声明的代码片段会隐式成为组件的 props (demo):

<!-- 这在语义上与上面的相同 -->
<Table data={fruits}>{#snippet header()}<th>水果</th><th>数量</th><th>价格</th><th>总计</th>{/snippet}{#snippet row(d)}<td>{d.name}</td><td>{d.qty}</td><td>{d.price}</td><td>{d.qty * d.price}</td>{/snippet}
</Table>

组件标签内的任何不是代码片段声明的内容都将隐式成为 children 代码片段的一部分 (demo):

<!--- file: App.svelte --->
<Button>点击我</Button>
<!--- file: Button.svelte --->
<script>let { children } = $props();
</script><!-- 结果将是 <button>点击我</button> -->
<button>{@render children()}</button>

[!NOTE] 请注意,如果组件内部还有内容,你不能有名为 children 的 prop — 基于这个原因,应该避免使用这个名称作为 prop

你可以将代码片段 props 声明为可选的。你可以使用可选链,当代码片段未设置时不渲染任何内容...

<script>let { children } = $props();
</script>{@render children?.()}

...或者使用 #if 块来渲染后备内容:

<script>let { children } = $props();
</script>{#if children}{@render children()}
{:else}后备内容
{/if}

代码片段类型

代码片段实现了从 'svelte' 导入的 Snippet 接口:

<script lang="ts">import type { Snippet } from 'svelte';interface Props {data: any[];children: Snippet;row: Snippet<[any]>;}let { data, children, row }: Props = $props();
</script>

通过这项改动,如果你尝试在没有提供 data prop 和 row 代码片段的情况下使用该组件,则会出现红色波浪线。请注意,提供给 Snippet 的类型参数是一个元组,因为代码片段可以有多个参数。

我们可以通过声明泛型来进一步收窄类型,以便 datarow 引用相同的类型:

<script lang="ts" generics="T">import type { Snippet } from 'svelte';let {data,children,row}: {data: T[];children: Snippet;row: Snippet<[T]>;} = $props();
</script>

导出代码片段

.svelte 文件顶层声明的代码片段可以从 <script module> 导出以供其他组件使用,前提是它们不引用非模块 <script> 中的任何声明(无论是直接引用还是通过其他代码片段间接引用) (demo):

<script module>export { add };
</script>{#snippet add(a, b)}{a} + {b} = {a + b}
{/snippet}

[!NOTE]
这需要 Svelte 5.5.0 或更新版本

程序化代码片段

代码片段可以通过 createRawSnippet API 以编程方式创建。这适用于高级用例。

代码片段和插槽

在 Svelte 4 中,可以使用 插槽 将内容传递给组件。代码片段更强大、更灵活,因此在 Svelte 5 中插槽已被弃用。

要渲染一个代码片段,请使用 {@render ...} 标签。

{#snippet sum(a, b)}<p>{a} + {b} = {a + b}</p>
{/snippet}{@render sum(1, 2)}
{@render sum(3, 4)}
{@render sum(5, 6)}

表达式可以是像 sum 这样的标识符,也可以是任意的 JavaScript 表达式:

{@render (cool ? coolSnippet : lameSnippet)()}

可选代码片段

如果代码片段可能未定义 — 例如,因为它是一个传入的 prop — 那么你可以使用可选链操作符,只在代码片段确实存在时才渲染它:

{@render children?.()}

或者,使用{#if ...} 块配合 :else 子句来渲染后备内容:

{#if children}{@render children()}
{:else}<p>后备内容</p>
{/if}

Svelte 中文文档

本篇已收录在掘金专栏 《Svelte 中文文档》,该系列预计 40 篇。

系统学习 Svelte,欢迎入手小册《Svelte 开发指南》。语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

此外我还写过 JavaScript 系列、TypeScript 系列、React 系列、Next.js 系列、冴羽答读者问等 14 个系列文章, 全系列文章目录:https://github.com/mqyqingfeng/Blog

通过文字建立交流本身就是一种缘分,欢迎围观我的“朋友圈”、加入“低调务实优秀中国好青年”前端社群,分享技术,带你成长。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/883377.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基环树 DP:学习笔记

总述 定义 基环树,是一个 \(N\) 个点和 \(N\) 条边的连通图,特征是图中有且仅有一个环。特别的,如果不连通且每个连通块的点数和边数都相等,那么这就是一个基环树森林。 基环树 DP,顾名思义,就是在一个基环树上 DP,或是 DP 的结构类似基环树。相对于正常的树型 DP,一般…

数字孪生如何让GIS场景视效瞬间高大上?带你了解鲸孪生中的GIS系统

GIS与数字孪生的结合非常紧密,而山海鲸可视化作为一个数字孪生平台,也将GIS系统整合在了鲸孪生功能之中。 GIS中包含了大量的数据,例如遥感数据、地形数据、倾斜摄影数据等,能够为数字孪生系统提供非常好的补充。同时,传统的GIS系统整体视觉效果相对较差,与其他模型和数据…

2 分支 多个思路

利用分支,你就可以在同一个代码基础上同时处理多个完全没有关联、相互独立的工作。考虑以下场景。 假设你正在改一个 Bug-A,此时已经产生了大量的代码修改,并且离修复完成还有很长一段时间(起码得明天)。此时,有一个着急但简单的 Bug-B 需要你立即完成,并在一个小时内同…

记录一种DAG计数方法与一个配套技巧

记录一种DAG计数方法与一个配套技巧 定义 \(f_S\) 表示集合 \(S\) 中的点构成的合法 DAG 子图的方案数。假设找到 DAG 中一个入度为 \(0\) 的节点 \(x\),那么很明显 \(f_S=\sum_{x}f_{S\setminus \{x\}}\),这明显要算重因为 \(S\setminus \{x\}\) 中也有入度为 \(0\) 的点。 …

野鸡题手写题解整合

浴谷正在蒸蒸日上,专栏区怕是马上要倒闭了。 CF2026F 题 题。题外话:这场有点水平,E 题让我重拾了最大权闭合子图的记忆。 首先考虑没有这个可持久化(只有 \(2,3,4\) 操作)怎么做。\(0/1\) 背包问题,动态维护当前的 dp 数组 \(f_i\) 表示总体积 \(\sum p\) 不超过 \(i\) …

鸿蒙开发:了解@Builder装饰器

@Builder装饰是鸿蒙UI开发中,非常重要的一个装饰器,在实际的开发中,合理且正确的使用,能够让我们的代码更加的简洁前言本文代码案例基于Api13,温馨提示:内容相对来说比较简单,如果您已掌握,略过即可。如果说一个页面中组件有很多,我们都统一写到build函数中,显而易见…

P1020 [NOIP 1999 提高组] 导弹拦截(dilworth)

这道题真的做的我鬼火冒,尤其是这个第二问要用到dilworth但是我看讲解完全不知道他们在讲什么,我看了好久才理解,一个数组至少可以由几个不增子序列覆盖就等于严格单调递增的最长子序列的长度,如果是至少可以由几个严格递减子序列覆盖就等于最长单调不减子序列的长度,然后…

Linux系统介绍

1. Linux介绍 Linux和windows一样也是一个操作系统,但是与windows不同的是,Linux是一套开放源码的代码程序、并且可以自由传播的类unix操作系统软件。 Linux系统主要被应用于服务端、嵌入式开发和个人PC桌面3大领域,一般的WEB项目都是部署在Linux操作系统上。 Linux是一个基…

Linux驱动---按键

文章简述了Input子系统架构,讲解了输入设备驱动开发流程,探讨了按键消抖方法及按键驱动开发要点,为嵌入式开发提供参考。目录一、Input子系统1.1、简介1.2、Input子系统构成1.3、input_dev结构体二、输入设备驱动开发流程2.1、分配和初始化输入设备2.2、注册设备2.3、事件上…

Android studio:flutter开发环境配置

Make Android apps | Flutter 下载flutter,并配置环境变量 右击 此电脑->属性->高级系统设置->环境变量在 用户变量 新建两个变量 变量名:FLUTTER_STORAGE_BASE_URL 变量值:https://storage.flutter-io.cn 变量名:PUB_HOSTED_URL 变量值:https://pub.flutter-io.c…