svelte 学习章节

78 阅读6分钟

svelte 前端框架

官方网站

svelte.dev/docs/kit/in…

创建新的项目

npx sv create my-app
cd my-app
npm run dev

脚本隔离

svelte怎么实现脚本隔离的呢

<button onclick={() => count++}> clicks: {count}


## $state.raw

在您不希望对象和数组深度反应式的情况下,您可以使用 `$state.raw`。

```html
<script module>
  let person = $state.raw({
    name: "Jane",
    age: 18,
  });
  person.age+=1

person = {
	name: 'Heraclitus',
	age: 50
};

</script>

This reference only captures the initial value of person. Did you mean to reference it inside a closure instead?

当然会报错 是否在闭包中使用它

$state.snapshot

要对深度响应式的state代理进行静态快照,请使用state代理进行静态快照,请使用state.snapshot:

<script> 	let counter = $state({ count: 0 }); 
function onclick() 
{ 		// Will log `{ count: ... }` rather than `Proxy { ... }` 		console.log($state.snapshot(counter)); 	} 
  </script>

你想将一些状态传递给不期望代理的外部库或 API(例如 structuredClone)时,这会很方便。

![image-20251111153213801](/Users/wangyuan/Library/Application Support/typora-user-images/image-20251111153213801.png)

返回当前的代理对象 做浅克隆的时候非常有用

$derived 无副作用

理解双向绑定或者计算属性. 替代没有 $:

$derived (...) 内部的表达式应该没有副作用。Svelte 会禁止在派生表达式中进行状态更改(例如 count++)。

<script>
	let count = $state(0);
	let doubled = $derived(count * 2);
</script>

<button onclick={() => count++}>
	{doubled}
</button>

<p>{count} doubled is {doubled}</p>

测试一下?

image-20251111154333787转存失败,建议直接上传图片文件

如果没添加的话 count 的值改 变 doubled 没有任何变化

Svelte 组件中的代码仅在创建时执行一次。如果 没有 $derived符文,即使count发生变化,doubled也将保持其原始值。

$effect

效果使您的应用程序“执行操作”。当 Svelte 运行效果函数时,它会跟踪访问了哪些状态(和派生状态)(除非在 untrack 内部访问),并在该状态稍后更改时重新运行该函数。

Svelte 应用程序中的大多数效果都是由 Svelte 本身创建的——例如,当 name 更改时,它们是更新 <h1>hello {name}!</h1> 中文本的部分。

但是,您还可以使用 $effect 符文创建自己的效果,当您需要将外部系统(无论是库、<canvas> 元素还是网络上的某些内容)与 Svelte 应用程序内的状态同步时,这很有用。

[^]: 避免过度使用 $effect!当您在效果中执行太多工作时,代码通常会变得难以理解和维护。请参阅 何时不使用 $effect 以了解替代方法。

和react 的useeffect 方式差不多,都能修改本身的state 状态change

修改 状态的时候会自动执行函数,无需配置依赖项

<script>

  let size = $state(50);
  let color = $state("#ff3e00");
  let canvas;
  $effect(() => {
    const context = canvas.getContext("2d");
    context.clearRect(0, 0, canvas.width, canvas.height);
    console.log("执行了改变ui");

    // this will re-run whenever `color` or `size` change
    context.fillStyle = color;
    context.fillRect(0, 0, size, size);
  });
  color = "#00";
</script>

<button onclick="{()=>(color = '#ff3e00')}">change</button>
<canvas bind:this={canvas} width="100" height="100" />

$props

组件的输入称为 props,它是 properties 的缩写。您可以像传递属性给元素一样传递 props 给组件

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

<MyComponent adjective="cool" />

另一方面,在 MyComponent.svelte 内部,我们可以使用 $props 符文接收 props...

<script>
	let props = $props();
</script>

<p>this component is {props.adjective}</p>

...尽管更常见的是,您会解构您的 props

<script>
	let { adjective } = $props();
</script>

<p>this component is {adjective}</p>

回退值. (默认值 的方式好理解)

解构允许我们声明回退值,如果父组件未设置给定 prop,则使用这些值

let { adjective = 'happy' } = $props();

重命名 props

我们还可以使用解构赋值来重命名 props,如果它们是无效的标识符或 JavaScript 关键字(如 super),则需要这样做

let { super: trouper = 'lights are gonna find me' } = $props();

剩余 props

最后,我们可以使用 剩余属性 来获取,嗯,其余的 props

let { a, b, c, ...others } = $props();

更新 props

组件内部对 prop 的引用在 prop 本身更新时也会更新——当 App.svelte 中的 count 更改时,它也会在 Child.svelte 内部更改。但子组件能够临时覆盖 prop 值,这对于未保存的临时状态很有用 (演示)

app.svelte

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

	let count = $state(0);
</script>

<button onclick={() => (count += 1)}>
	clicks (parent): {count}
</button>

<Child {count} />';

	let count = $state(0);
</script>

<button onclick={() => (count += 1)}>
	clicks (parent): {count}
</button>

<Child {count} />

Child

<script>
	let { count } = $props();
</script>

<button onclick={() => (count += 1)}>
	clicks (child): {count}
</button>

单纯的基本数据类形是可以修改的,子组件修改props 也是可以发生变化,但是如果是是一个对象就不能修改;

子传父 (vue emit)

createEventDispatcher

  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
     dispatch('temperatureChange', { temperature: acTemperature }); //后面是参数

$bindable。(不建议)

常,props 单向流动,从父组件到子组件。这使得理解数据在应用程序中的流动变得很容易。

在 Svelte 中,组件 props 可以被 绑定,这意味着数据也可以从子组件 向上 流向父组件。您不应该经常这样做,但如果谨慎而适量地使用,它可以简化您的代码。

这也意味着状态代理可以在子组件中被 修改

!!!!对于普通 props 也可以进行修改,但强烈建议不要这样做 - 如果 Svelte 检测到组件正在修改它“不拥有”的状态,它会发出警告。

模板语法

@render

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

{#snippet sum(a, b)}
	<p>{a} + {b} = {a + b}</p>
{/snippet}

{@render sum(1, 2)}
{@render sum(3, 4)}
{@render sum(5, 6)}

当然可以这样

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

可选代码片段

如果代码片段可能未定义——例如,因为它是一个传入的 prop——那么您可以使用可选链来仅在它定义时渲染它

{@render children?.()}

bind 绑定

数据通常从上到下流动,从父组件到子组件。bind: 指令允许数据以相反的方式流动,从子组件到父组件。

双向绑定

<input bind:value={value} />
<input bind:value />

从 5.6.0 开始,如果 <input> 具有 defaultValue 并且是表单的一部分,则在表单重置时,它将恢复为该值而不是空字符串。请注意,对于初始渲染,绑定的值优先,除非它是 nullundefined

<script>
	let value = $state('');
</script>

<form>
	<input bind:value defaultValue="not the empty string">
	<input type="reset" value="Reset">
</form>

stores

stores是一个对象,它允许通过简单的商店契约以响应方式访问值。该svelte/store模块包含满足此契约的最小商店实现。

对带$前缀的变量的赋值要求该变量是可写商店,并且会导致调用商店的.set方法。

请注意,商店必须在组件的顶层声明 - 例如,不要在if块或函数内部声明。

<script>
	import { writable } from 'svelte/store';

	const count = writable(0);
	console.log($count); // logs 0

	count.set(1);
	console.log($count); // logs 1
//局部变量(不表示商店值)不能带有$前缀。
	$count = 2;
	console.log($count); // logs 2
</script>

何时使用stores

在 Svelte 5 之前,商店是创建跨组件响应式状态或提取逻辑的首选解决方案。使用符文,这些用例已大大减少。

  • 在提取逻辑时,最好利用符文的通用响应性:你可以在组件的顶层之外使用符文,甚至将它们放入 JavaScript 或 TypeScript 文件中(使用.svelte.js.svelte.ts文件结尾)
  • 在创建共享状态时,你可以创建一个包含所需值的$state对象,然后操作该状态
export const userState = $state({
	name: 'name',
	/* ... */
});

App

<script>
	import { userState } from './state.svelte';
</script>

<p>User name: {userState.name}</p>
<button onclick={() => {
	userState.name = 'new name';
}}>
	change name
</button>

上下文 context

大多数状态是组件级状态,其生命周期与组件相同。但是,也存在部分或应用程序范围的状态,也需要以某种方式进行处理。

最简单的方法是创建全局状态并导入它。

export const myGlobalState = $state({
	user: {
		/* ... */
	}
	/* ... */
});

sveltekit 路由

  • src/routes 是根路由

  • src/routes/about 创建了一个/about 路由

  • src/routes/blog/[slug] 创建了一个带有参数slug 的路由,该参数可用于在用户请求 /blog/hello-world 等页面时动态加载数据

每个路由目录包含一个或多个路由文件,这些文件可以通过其+ 前缀识别。+page.svelte

  • 所有文件都可以在服务器上运行
  • 除了+server 文件外,所有文件都在客户端运行
  • +layout+error 文件适用于子目录以及它们所在的目录

+page.svelte

一个+page.svelte 组件定义了应用程序的一个页面。默认情况下,页面会在服务器上(SSR)渲染初始请求,并在浏览器中(CSR)渲染后续导航。

src/routes/about/+page

<h1>About this site</h1>
<p>TODO...</p>
<a href="/">Home</a>

+layout

到目前为止,我们一直将页面视为完全独立的组件——导航时,现有的+page.svelte 组件将被销毁,一个新的组件将取代它。

但在许多应用程序中,有一些元素应该在每个页面上可见,例如顶级导航或页脚。与其在每个+page.svelte 中重复它们,不如将它们放在布局

{@render children()}

渲染子组件。 (react 中的 omit)

SvelteKit 怎么获取当前路由的实例?

SvelteKit 中,你通常不需要获取“路由实例”(因为 SvelteKit 没有像 Vue Router 或 React Router 那样的“router instance”概念),但你可以轻松获取当前路由的相关信息

<!-- src/routes/+page.svelte -->
<script>
  import { page } from '$app/stores';
</script>

<!-- 显示当前路径 -->
<p>当前路径: {$page.url.pathname}</p>

<!-- 显示查询参数 -->
<p>查询参数: {$page.url.searchParams.get('q')}</p>

<!-- 如果是动态路由,如 /blog/[slug] -->
{#if $page.params.slug}
  <p>文章 slug: {$page.params.slug}</p>
{/if}

✅ 方法二:在 load 函数中获取 urlparams

+page.js+layout.jsload 函数中,你可以直接访问路由信息:

// src/routes/blog/[slug]/+page.js
export async function load({ params, url, fetch }) {
  const { slug } = params; // ← 动态参数
  const searchQuery = url.searchParams.get('highlight'); // ← 查询参数

  const post = await fetch(`/api/posts/${slug}`).then(r => r.json());

  return {
    post,
    highlight: searchQuery
  };
}

然后在 +page.svelte 中通过 $page.data 使用:

<script>
  import { page } from '$app/stores';
</script>

<h1>{$page.data.post.title}</h1>
{#if $page.data.highlight}
  <mark>高亮关键词: {$page.data.highlight}</mark>
{/if}

✅ 方法三:编程式导航(类似“路由实例”的操作)

虽然没有 router 实例,但你可以使用 SvelteKit 提供的导航工具:

<script>
  import { goto, invalidate } from '$app/navigation';
  import { page } from '$app/stores';

  function goToProfile() {
    goto('/profile'); // 编程式跳转
  }

  function refreshData() {
    // 重新运行当前页面的 load 函数
    invalidate();
  }
</script>

<button on:click={goToProfile}>去个人页</button>
<button on:click={refreshData}>刷新数据</button>

<p>当前路径: {$page.url.pathname}</p>
  • goto(url):跳转页面(客户端导航)
  • invalidate():重新加载当前 load 数据
  • preloadData(url):预加载某个路由的数据

🚫 不要做的事

  • ❌ 不要尝试 import router from '...' —— SvelteKit 没有暴露 router 实例。
  • ❌ 不要手动解析 window.location —— 在 SSR(服务端渲染)时会出错。始终用 $pageload 上下文。

✅ 总结:如何“获取当前路由”

需求推荐方式
获取当前路径、参数、查询字符串使用 $page store(在 .svelte 文件中)
在数据加载时获取路由信息+page.jsload({ params, url })
页面跳转或刷新使用 goto()invalidate()$app/navigation 工具
全局监听路由变化订阅 $page store 即可自动响应变化

svelte:component(官方动态组件标签)

<script>
  import Home from './Home.svelte';
  import About from './About.svelte';

  let component = Home;

  function switchToAbout() {
    component = About;
  }
</script>

<button on:click={switchToAbout}>切换到 About</button>

<svelte:component this={component} name="Alice" />