跳至主要内容

其他

Svelte 4 迁移指南

在 GitHub 上编辑此页面

此迁移指南概述了如何从 Svelte 版本 3 迁移到版本 4。有关每个更改的更多详细信息,请参阅链接的 PR。使用迁移脚本自动迁移其中一些:npx svelte-migrate@latest svelte-4

如果您是库作者,请考虑仅支持 Svelte 4 或是否也可以支持 Svelte 3。由于大多数重大更改不会影响很多人,因此这可能很容易实现。此外,请记住在 peerDependencies 中更新版本范围。

最低版本要求

  • 升级到 Node 16 或更高版本。不再支持较早版本。(#8566)
  • 如果您正在使用 SvelteKit,请升级到 1.20.4 或更高版本 (sveltejs/kit#10172)
  • 如果您在不使用 SvelteKit 的情况下使用 Vite,请升级到 vite-plugin-svelte 2.4.1 或更高版本 (#8516)
  • 如果您正在使用 webpack,请升级到 webpack 5 或更高版本以及 svelte-loader 3.1.8 或更高版本。不再支持较早版本。(#8515, 198dbcf)
  • 如果您正在使用 Rollup,请升级到 rollup-plugin-svelte 7.1.5 或更高版本 (198dbcf)
  • 如果您正在使用 TypeScript,请升级到 TypeScript 5 或更高版本。较低版本可能仍然有效,但对此不做任何保证。(#8488)

捆绑器的浏览器条件

捆绑器现在必须在为浏览器构建前端捆绑包时指定 browser 条件。SvelteKit 和 Vite 将自动为您处理此问题。如果您使用任何其他捆绑器,您可能会观察到生命周期回调(例如 onMount)未被调用,并且您需要更新模块解析配置。

  • 对于 Rollup,这是通过在 @rollup/plugin-node-resolve 插件中设置 browser: true 来完成的。有关更多详细信息,请参阅 rollup-plugin-svelte 文档
  • 对于 webpack,这是通过将 "browser" 添加到 conditionNames 数组来完成的。如果您已设置别名配置,您可能还必须更新它。有关更多详细信息,请参阅 svelte-loader 文档

(#8516)

Svelte 不再支持编译器输出的 CommonJS (CJS) 格式,并且还移除了 svelte/register 钩子和 CJS 运行时版本。如果您需要保留 CJS 输出格式,请考虑使用打包器在构建后步骤中将 Svelte 的 ESM 输出转换为 CJS。(#8613

Svelte 函数的类型更严格

现在 createEventDispatcherActionActionReturnonMount 的类型更严格了

  • createEventDispatcher 现在支持指定有效负载是可选的、必需的还是不存在的,并且会相应地检查调用点(#7224
ts
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher<{
optional: number | null;
required: string;
noArgument: null;
}>();
Expected 2-3 arguments, but got 1.2554Expected 2-3 arguments, but got 1.
Argument of type '"surprise"' is not assignable to parameter of type 'null | undefined'.2345Argument of type '"surprise"' is not assignable to parameter of type 'null | undefined'.
// Svelte version 3:
dispatch('optional');
dispatch('required'); // I can still omit the detail argument
dispatch('noArgument', 'surprise'); // I can still add a detail argument
Expected 2-3 arguments, but got 1.2554Expected 2-3 arguments, but got 1.
Argument of type '"surprise"' is not assignable to parameter of type 'null | undefined'.2345Argument of type '"surprise"' is not assignable to parameter of type 'null | undefined'.
// Svelte version 4 using TypeScript strict mode:
dispatch('optional');
dispatch('required'); // error, missing argument
dispatch('noArgument', 'surprise'); // error, cannot pass an argument
  • ActionActionReturn 现在具有默认参数类型 undefined,这意味着如果您想指定此操作接收参数,则需要键入泛型。迁移脚本将自动迁移此项(#7442
const action: Action = (node, params) => { .. } // this is now an error if you use params in any way
const action: Action<HTMLElement, string> = (node, params) => { .. } // params is of type string
  • 如果您异步地从 onMount 返回一个函数,现在将显示一个类型错误,因为这可能是您代码中的一个错误,您希望在销毁时调用回调,而它只会对同步返回的函数执行此操作(#8136
// Example where this change reveals an actual bug
onMount(
 // someCleanup() not called because function handed to onMount is async
 async () => {
   const something = await foo();
 // someCleanup() is called because function handed to onMount is sync
 () => {
  foo().then(something =>  ..
   // ..
   return () => someCleanup();
}
);

使用 Svelte 的自定义元素

使用 Svelte 创建自定义元素已经过全面检修并得到显著改进。tag 选项已弃用,取而代之的是新的 customElement 选项

<svelte:options tag="my-component" />
<svelte:options customElement="my-component" />

进行此更改是为了允许 更多可配置性 以适应高级用例。迁移脚本将自动调整您的代码。属性的更新时机也略有改变。(#8457

SvelteComponentTyped 已弃用

SvelteComponentTyped 已弃用,因为 SvelteComponent 现在具有其所有类型化功能。用 SvelteComponent 替换 SvelteComponentTyped 的所有实例。

 import { SvelteComponentTyped } from 'svelte';
 import { SvelteComponent } from 'svelte';

 export class Foo extends SvelteComponentTyped<{ aProp: string }> {}
 export class Foo extends SvelteComponent<{ aProp: string }> {}

如果您之前已将 SvelteComponent 用作组件实例类型,现在您可能会看到一个有点不透明的类型错误,可以通过将 : typeof SvelteComponent 更改为 : typeof SvelteComponent<any> 来解决此问题。

<script>
  import ComponentA from './ComponentA.svelte';
  import ComponentB from './ComponentB.svelte';
  import { SvelteComponent } from 'svelte';

  let component: typeof SvelteComponent;
  let component: typeof SvelteComponent<any>;

  function choseRandomly() {
	component = Math.random() > 0.5 ? ComponentA : ComponentB;
  }
</script>

<button on:click={choseRandomly}>random</button>
<svelte:element this={component} />

迁移脚本会自动为您执行这两项操作。(#8512

过渡默认是局部的

过渡现在默认是局部的,以避免页面导航时的混乱。“局部”意味着如果过渡在嵌套控制流块(each/if/await/key)内,而不是直接父块,而是上面创建/销毁的块,则不会播放。在以下示例中,slide 入场动画仅在 successfalse 变为 true 时播放,但当 showfalse 变为 true不会播放

{#if show}
	...
	{#if success}
		<p in:slide>Success</p>
	{/each}
{/if}

要使过渡成为全局,请添加 |global 修饰符 - 然后当上面任何控制流块被创建/销毁时,它们将播放。迁移脚本会自动为您执行此操作。(#6686

默认插槽绑定

默认插槽绑定不再公开给命名插槽,反之亦然

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

<Nested let:count>
	<p>
		count in default slot - is available: {count}
	</p>
	<p slot="bar">
		count in bar slot - is not available: {count}
	</p>
</Nested>

这使得插槽绑定更加一致,因为当例如默认插槽来自列表而命名插槽不是时,行为是未定义的。(#6049

预处理器

应用预处理器的顺序已更改。现在,预处理器按顺序执行,并且在一个组中,顺序是标记、脚本、样式。

ts
import { preprocess } from 'svelte/compiler';
const { code } = await preprocess(
source,
[
{
markup: () => {
console.log('markup-1');
},
script: () => {
console.log('script-1');
},
style: () => {
console.log('style-1');
}
},
{
markup: () => {
console.log('markup-2');
},
script: () => {
console.log('script-2');
},
style: () => {
console.log('style-2');
}
}
],
{
filename: 'App.svelte'
}
);
// Svelte 3 logs:
// markup-1
// markup-2
// script-1
// script-2
// style-1
// style-2
// Svelte 4 logs:
// markup-1
// script-1
// style-1
// markup-2
// script-2
// style-2

例如,如果您正在使用 MDsveX,这可能会影响您 - 在这种情况下,您应该确保它在任何脚本或样式预处理器之前。

preprocess: [
	vitePreprocess(),
	mdsvex(mdsvexConfig)
	mdsvex(mdsvexConfig),
	vitePreprocess()
]

每个预处理器还必须有一个名称。(#8618

新的 eslint 包

eslint-plugin-svelte3 已弃用。它可能仍然适用于 Svelte 4,但我们对此不作任何保证。我们建议切换到我们的新包 eslint-plugin-svelte。请参阅 此 Github 帖子,了解如何迁移的说明。或者,您可以使用 npm create svelte@latest 创建一个新项目,选择 eslint(可能还有 TypeScript)选项,然后将相关文件复制到您现有的项目中。

其他重大更改

  • inert 属性现在应用于输出元素,以使其对辅助技术不可见并防止交互。(#8628
  • 运行时现在使用 classList.toggle(name, boolean),这可能无法在非常旧的浏览器中使用。如果您需要支持这些浏览器,请考虑使用 polyfill。(#8629
  • 运行时现在使用 CustomEvent 构造函数,这可能无法在非常旧的浏览器中使用。如果您需要支持这些浏览器,请考虑使用 polyfill。(#8775
  • 使用 StartStopNotifier 接口(传递给 writable 等的创建函数)从头开始实现自己的存储的人员现在需要传递更新函数以及设置函数。这对使用存储或使用现有 Svelte 存储创建存储的人员没有任何影响。(#6750
  • derived 现在会对传递给它的假值引发错误,而不是存储。(#7947
  • svelte/internal 的类型定义已被删除,以进一步阻止使用那些不是公共 API 的内部方法。其中大部分可能会在 Svelte 5 中更改
  • 现在对 DOM 节点进行批量删除,这会稍微改变其顺序,可能会影响在这些元素上使用 MutationObserver 时触发的事件顺序(#8763
  • 如果您之前通过 svelte.JSX 命名空间增强了全局类型,则需要迁移到使用 svelteHTML 命名空间。类似地,如果您使用 svelte.JSX 命名空间来使用其中的类型定义,则需要迁移到使用 svelte/elements 中的类型。您可以在 此处 找到有关如何操作的更多信息
上一个 TypeScript
下一个 svelte/register