拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Svelte 5 客户端运行时错误参考:错误码体系、触发机制与源码实现解析

Svelte 5 客户端运行时错误参考错误码体系、触发机制与源码实现解析【免费下载链接】svelteweb development for the rest of us项目地址: https://gitcode.com/GitHub_Trending/sv/svelte本篇基于 Svelte 仓库中由消息管道自动生成的客户端错误参考文档client-errors.md系统梳理 Svelte 5 客户端运行时抛出的全部 30 个错误码每个错误的准确消息模板、典型触发场景与修复方式。读完后你不仅能读懂Svelte error异常并快速定位原因还能理解这些错误背后的效果树effect tree、响应式派生值、hydration 等底层机制在源码中的真实校验位置。错误体系概览从 Markdown 消息源到运行时异常Svelte 的客户端错误文档不是手工维护的而是一条“单一事实来源 自动生成”的流水线消息源所有错误码及其消息模板、详细说明以 Markdown 形式集中定义在 messages/client-errors/errors.md。每个错误以## error_code开头紧跟一个或多个以引用的消息模板支持%component%、%key%、%a%等占位符其余段落即为本文档中的详细说明。代码生成scripts/process-messages/index.js 解析这些 Markdown一方面按错误码排序回写源文件另一方面生成各运行时的错误函数文件模板见 templates/client-errors.js最终产物为 src/internal/client/errors.js。文档生成同一个脚本把全部内容重排后写入 documentation/docs/98-reference/.generated/client-errors.md即本文所依据的参考页文件头明确标注 “This file is generated by scripts/process-messages/index.js. Do not edit!”。从生成模板可以看到每个错误函数的统一形态export function CODE(PARAMETER) { if (DEV) { const error new Error(${CODE}\n${MESSAGE}\nhttps://svelte.dev/e/${CODE}); error.name Svelte error; throw error; } else { throw new Error(https://svelte.dev/e/${CODE}); } }这带来两个实用结论开发模式DEV异常name为Svelte error消息包含三段——错误码、完整消息、https://svelte.dev/e/错误码参考链接可直接点击查阅该错误的详细说明。生产模式为控制包体大小错误消息被压缩为仅包含参考 URL即https://svelte.dev/e/错误码。因此即使线上环境只看到一个 URL 形式的报错也可以通过该错误码在本文档中查到完整说明。以下按主题分组完整覆盖文档中定义的全部 30 个错误码。带源码证据的条目均标注了校验逻辑在运行时代码中的位置。效果树Effect Tree相关错误Svelte 中存在两类响应——$derived和$effect。$derived惰性执行、无人引用时可被垃圾回收因此可以在任何位置创建而$effect会主动eagerly随依赖变化持续运行直到被销毁所以只能在其他 effect或 effect root例如组件首次挂载时创建的那个内部创建以便 Svelte 知道何时销毁它们。理解这一点是读懂下面大部分错误的前提。async_derived_orphanCannot create a $derived(...) with an await expression outside of an effect tree源码位置reactivity/deriveds.js#L118。当$derived中包含await表达式时Svelte 使用了一招“障眼法”如果等到读取{await getPromise()}时才去调用getPromise()就太迟了于是 Svelte 改用 effect 来提前调用它并在值可用时通知 Svelte。但既然用到了 effect异步派生值就只能在另一个 effect 内部创建——脱离效果树创建含await的$derived即触发此错误。effect_orphan%rune% can only be used inside an effect (e.g. during component initialisation)源码位置reactivity/effects.js#L57。Effect 只能在某个父 effect 正在运行时创建。因此它不能例如在事件处理器中创建也不能在await表达式之后创建——唯一的例外是await直接出现在组件script标签内而不是 async 函数里。在极罕见的情况下可以用$effect.root见$effect文档在正常组件生命周期之外创建 effect。effect_in_unowned_derivedEffect cannot be created inside a $derived value that was not itself created inside an effect源码位置reactivity/effects.js#L60。派生值本身如果不在效果树内则其中不能创建 effect——因为 Svelte 无法确定其销毁时机。effect_in_teardown%rune% cannot be used inside an effect cleanup function源码位置reactivity/effects.js#L64。effect 的清理函数运行在销毁路径上在其中使用响应式原语rune是不合法的。effect_pending_outside_reaction$effect.pending() can only be called inside an effect or derived源码证据dom/blocks/boundary.js#L557 处调用e.effect_pending_outside_reaction()。$effect.pending()用于查询 pending effect 数量必须在 effect 或 derived 的求值上下文中调用。get_abort_signal_outside_reactiongetAbortSignal() can only be called inside an effect or derivedgetAbortSignal()返回与当前响应作用域绑定的AbortSignal用于在异步任务如 fetch中响应 effect 销毁。与上一条同理它只能在 effect 或 derived 内部调用。derived_references_selfA derived value cannot reference itself recursively源码位置reactivity/deriveds.js#L363。派生值不能递归引用自身否则求值将永不完备。set_context_after_initsetContext must be called when a component first initializes, not in a subsequent effect or after an await expression源码位置client/context.js#L134。context 用于建立组件间层级关系只能在初始化阶段写入在后续 effect 中或await之后调用则失效。该限制仅在使用experimental.async选项时生效此选项将在 Svelte 6 中默认开启。状态与$state代理相关错误Svelte 5 的$state通过 Proxy 实现深度响应式对对象的某些元操作原型、属性描述符有严格约束。state_unsafe_mutationUpdating state inside $derived(...), $inspect(...) or a template expression is forbidden. If the value should not be reactive, declare it without $state源码位置reactivity/sources.js#L162。此错误发生在求值$derived期间更新了状态。常见场景是想“一次派生两个值”script let count $state(0); let even $state(true); let odd $derived.by(() { even count % 2 0; return !even; }); /script button onclick{() count}{count}/button p{count} is even: {even}/p p{count} is odd: {odd}/p这是被禁止的因为它引入了不稳定如果p{count} is even: {even}/p在odd重算之前更新even就是过期的。多数情况下解决方案是把一切改为派生值let count 0; let even $derived(count % 2 0); let odd $derived(!even);如果副作用不可避免请改用$effect。state_descriptors_fixedProperty descriptors defined on $state objects must contain value and always be enumerable, configurable and writable.源码位置client/proxy.js#L130。对$state对象自定义属性描述符时必须包含value且固定为可枚举、可配置、可写——这是代理转发响应性所依赖的不变式。state_prototype_fixedCannot set prototype of $state object源码位置client/proxy.js#L356。$state对象的原型不可替换否则会破坏代理链路。更新循环与批量更新Batch/Fork相关错误effect_update_depth_exceededMaximum update depth exceeded. This typically indicates that an effect reads and writes the same piece of state源码位置reactivity/batch.js#L1068。如果 effect 更新了它所依赖的状态它会重跑可能陷入死循环let count $state(0); $effect(() { // this both reads and writes count, // so will run in an infinite loop count 1; });Svelte 会在浏览器标签页崩溃前介入拦截。数组变异同理因为它同时读写了数组let array $state([hello]); $effect(() { array.push(goodbye); });注意effect 自我重跑本身没问题只要它会“收敛”settlelet array [a, b, c]; $effect(() { // this is okay, because sorting an already-sorted array // wont result in a mutation array.sort(); });遇到此问题时的常见原因是相关值根本不该是 state例如在 effect 中往logs数组 push就让logs为普通数组而非$state([])。极少数确实需要在 effect 中写状态的场景——应当避免——可用untrack读取状态避免将其加入依赖。fork_timingCannot create a fork inside an effect or when state changes are pending源码位置reactivity/batch.js#L1382。fork批量更新的“分叉/提交”单元不能创建在 effect 内部或当存在尚未刷新的状态变更时。fork_discardedCannot commit a fork that was already discarded源码位置reactivity/batch.js#L1402。一个 fork 被丢弃后不能再次提交。flush_sync_in_effectCannot use flushSync inside an effectflushSync()可同步冲刷所有 pending effect。它不能在 effect 正在被冲刷时使用——换句话说可以在状态变更之后调用它但不能在 effect 内部调用。此限制仅在使用experimental.async选项时生效该选项将在 Svelte 6 中默认开启。绑定Binding与 Props 相关错误bind_invalid_checkbox_valueUsing bind:value together with a checkbox input is not allowed. Use bind:checked instead源码位置dom/elements/bindings/input.js#L25 与 input.js#L84 两处调用。checkbox 的value是选项常量而非用户选择状态必须使用bind:checked绑定勾选状态。bind_not_bindableA component is attempting to bind to a non-bindable property %key% belonging to %component% (i.e. %name% bind:%key%{...}). To mark a property as bindable: let { %key% $bindable() } $props()组件的 prop 必须先用$bindable()声明外部才可以通过bind:key双向绑定。bind_invalid_exportComponent %component% has an export named %key% that a consumer component is trying to access using bind:%key%, which is disallowed. Instead, use bind:this (e.g. %name% bind:this{component} /) and then access the property on the bound component instance (e.g. component.%key%)这是针对 Svelte 4 风格export let组件的兼容提示Svelte 5 中不应再通过bind:key访问组件的 export而应使用bind:this获取实例后按属性访问。props_invalid_valueCannot do bind:%key%{undefined} when %key% has a fallback value源码位置reactivity/props.js#L329。当 prop 有回退默认值时父组件不能对其做bind:key{undefined}。props_rest_readonlyRest element properties of $props() such as %property% are readonly源码位置reactivity/props.js#L62。$props()中未显式解构、由 rest 收集到的属性是只读的不能直接写入。Keyed Each 块相关错误keyed each 块依赖 key 的稳定性来高效 diff因此对 key 表达式有两类严格校验。each_key_duplicateKeyed each block has duplicate key at indexes %a% and %b%Keyed each block has duplicate key %value% at indexes %a% and %b%源码位置dom/blocks/each.js#L360 与 each.js#L771。同一列表中两个元素使用相同 key 时抛出此时 Svelte 无法把 DOM 节点与数据项一一对应。each_key_volatileKeyed each block has key that is not idempotent — the key for item at index %index% was %a% but is now %b%. Keys must be the same each time for a given item源码位置dom/blocks/each.js#L310。key 表达式对同一元素多次调用必须返回相同值。像[item.a, item.b]这样的表达式每次都会创建新数组永远不会与自身相等。应使用原始值或构造稳定 key如item.a - item.b。HydrationSSR 水合相关错误hydration_failedFailed to hydrate the application源码位置client/render.js#L138。客户端渲染的 DOM 与服务端标记无法完成匹配时抛出说明 SSR 输出与客户端渲染结果存在结构性差异。hydratable_missing_but_requiredExpected to find a hydratable with key %key% during hydration, but did not.源码位置client/hydratable.js#L26。这通常发生在客户端渲染了一个服务端未渲染的 hydratable 时意味着它被迫回退为阻塞式运行其函数块性能代价高——会阻塞 hydration 直到异步工作完成script import { hydratable } from svelte; if (BROWSER) { // bad! nothing can become interactive until this asynchronous work is done await hydratable(foo, get_slow_random_number); } /scriptSnippet 与svelte:boundary相关错误invalid_snippetCould not {render} snippet due to the expression being null or undefined. Consider using optional chaining {render snippet?.()}源码位置dom/blocks/snippet.js#L34。{render}的表达式为null/undefined时抛出修复方式即消息中建议的可选链调用。svelte_boundary_reset_onerrorA svelte:boundary reset function cannot be called while an error is still being handled源码位置dom/blocks/boundary.js#L231。带onerror函数的svelte:boundary不能同步调用reset因为此时 boundary 仍处于损坏状态。通常reset()应在错误被解决之后再调用。如果需要在onerror回调中解决错误至少要等 boundary 稳定后再调用reset()例如借助ticksvelte:boundary onerror{async (error, reset) { fixTheError(); await tick(); reset(); }} /svelte:boundarySvelte 5 迁移与生命周期相关错误以下错误专门服务于从 Svelte 4 到 Svelte 5 的迁移详见 V5 迁移指南。component_api_changedCalling %method% on a component instance (of %component%) is no longer valid in Svelte 5源码位置dev/legacy.js#L17。Svelte 5 中组件不再是类class对实例调用旧版组件 API 方法如$$相关方法不再合法。component_api_invalid_newAttempted to instantiate %component% with new %name%, which is no longer valid in Svelte 5. If this component is not under your control, set the compatibility.componentApi compiler option to 4 to keep it working.源码位置dev/legacy.js#L8。同样源于“组件不再是类”的架构变更对不受你控制的第三方组件可通过compatibility.componentApi: 4编译器选项保持旧行为。lifecycle_legacy_only%name%(...) cannot be used in runes moderunes 模式script中启用$state等 rune下旧版生命周期钩子如onMount、beforeUpdate等参见 生命周期钩子文档不可用需改用$effect系列原语。其他错误rune_outside_svelteThe %rune% rune is only available inside .svelte and .svelte.js/ts filesrune如$state、$derived依赖编译期变换只能在.svelte及.svelte.js/.svelte.ts文件中使用在普通 JS/TS 模块中调用会触发此运行时错误。错误处理实践建议结合本文梳理调试 Svelte 5 运行时错误时可遵循以下路径以错误码为锚点开发模式下异常消息首行即错误码生产模式下异常消息本身就是一条svelte.dev/e/错误码链接。用错误码在 client-errors.md 或本文检索对应条目可获得完整消息模板与说明。区分“创建时机”类与“值合法性”类错误effect_orphan、effect_in_unowned_derived、effect_in_teardown、async_derived_orphan、set_context_after_init、effect_pending_outside_reaction、get_abort_signal_outside_reaction都指向同一根因——响应式原语必须在正确的 effect/derived 上下文中求值而each_key_duplicate、each_key_volatile、bind_invalid_checkbox_value、props_rest_readonly等则是对“值/用法合法性”的校验。关注循环依赖信号effect_update_depth_exceeded和derived_references_self分别提示 effect 与 derived 中的自我更新问题优先把“本不该是 state 的值”改回普通变量。SSR 项目重点排查 hydration 双错误hydration_failed说明服务端/客户端渲染结构性不一致hydratable_missing_but_required说明客户端多渲染了服务端没有的 hydratable导致阻塞式回退。迁移期项目component_api_changed、component_api_invalid_new、lifecycle_legacy_only是 Svelte 4 → 5 迁移的三类高频报错可结合 v5-migration-guide 与 legacy API 文档 逐项替换。关键文件索引文件作用documentation/docs/98-reference/.generated/client-errors.md本文依据自动生成的客户端错误参考页packages/svelte/messages/client-errors/errors.md错误码消息源单一事实来源packages/svelte/scripts/process-messages/index.js消息处理脚本生成 errors.js 与文档packages/svelte/scripts/process-messages/templates/client-errors.js错误函数模板DEV/生产双形态packages/svelte/src/internal/client/errors.js客户端错误函数运行时产物packages/svelte/src/internal/client/reactivity/effects.jseffect 创建上下文校验packages/svelte/src/internal/client/reactivity/deriveds.js派生值自引用、异步派生校验packages/svelte/src/internal/client/reactivity/batch.js更新深度、fork 时序校验packages/svelte/src/internal/client/dom/blocks/each.jskeyed each 的 key 校验packages/svelte/src/internal/client/proxy.js$state代理不变式校验packages/svelte/src/internal/client/dev/legacy.js旧版组件 API 兼容错误适用前提说明本文基于当前仓库快照其中flush_sync_in_effect与set_context_after_init的限制明确标注为仅在experimental.async选项下生效该选项将在 Svelte 6 默认开启各错误码的准确消息模板以 messages/client-errors/errors.md 为准。【免费下载链接】svelteweb development for the rest of us项目地址: https://gitcode.com/GitHub_Trending/sv/svelte创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门