首页
/ Svelte 编译器警告完全参考:读懂 Svelte 编译期 Warning 体系与源码生成管线

Svelte 编译器警告完全参考:读懂 Svelte 编译期 Warning 体系与源码生成管线

2026-09-06 22:11:08作者:温玫谨Lighthearted

导读

本文围绕 Svelte 官方文档中的编译器警告(compile warnings)参考页展开:完整收录 Svelte 编译器当前支持的全部警告码及其触发条件,讲解每一条警告背后的可访问性、响应式或 SSR 正确性考量,并深入仓库源码,还原警告消息从 packages/svelte/messages 下的 Markdown 源文件经 scripts/process-messages 管线自动生成 warnings.js生成版参考文档 的完整流程。读完本文,你既能把警告码当作「查表手册」使用,也能理解警告的产生、过滤与 svelte-ignore 抑制机制的底层实现。

什么是编译器警告

Svelte 在编译阶段(而非运行时)检查潜在错误——最典型的就是写出不可访问(inaccessible)的标记、误用属性、把非响应式变量当成状态使用等。这类问题不会导致编译失败,但会生成警告(warning),提示开发者在构建时就修正它们。所有编译警告的码表见 30-compiler-warnings.md 所内嵌的 .generated/compile-warnings.md(该文件由脚本自动生成,禁止手改)。

警告消息中普遍使用 %name%%element%%role% 这类占位符,实际触发时会被替换为具体值。例如 component_name_lowercase 的模板是「<%name%> will be treated as an HTML element unless it begins with a capital letter」,触发时会显示成具体的组件名。

警告码全景目录

以下目录完整覆盖当前仓库生成的警告码清单,按消息模板归纳。前缀约定:a11y_* 为可访问性规则(源文件 a11y.md),其余分布在 miscoptionsscriptstyletemplate 等源文件中。

a11y 可访问性类

警告码 消息模板
a11y_accesskey Avoid using accesskey
a11y_aria_activedescendant_has_tabindex An element with an aria-activedescendant attribute should have a tabindex value
a11y_aria_attributes <%name%> should not have aria-* attributes
a11y_autocomplete_valid '%value%' is an invalid value for 'autocomplete' on <input type="%type%">
a11y_autofocus Avoid using autofocus
a11y_click_events_have_key_events Visible, non-interactive element <%element%> with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as <button type="button"> or <a> might be more appropriate
a11y_consider_explicit_label Buttons and links should either contain text or have an aria-label, aria-labelledby or title attribute
a11y_distracting_elements Avoid <%name%> elements
a11y_figcaption_index <figcaption> must be first or last child of <figure>
a11y_figcaption_parent <figcaption> must be an immediate child of <figure>
a11y_hidden <%name%> element should not be hidden
a11y_img_redundant_alt Screenreaders already announce <img> elements as an image
a11y_incorrect_aria_attribute_type The value of '%attribute%' must be a %type%
a11y_incorrect_aria_attribute_type_boolean The value of '%attribute%' must be either 'true' or 'false'. It cannot be empty
a11y_incorrect_aria_attribute_type_id The value of '%attribute%' must be a string that represents a DOM element ID
a11y_incorrect_aria_attribute_type_idlist The value of '%attribute%' must be a space-separated list of strings that represent DOM element IDs
a11y_incorrect_aria_attribute_type_integer The value of '%attribute%' must be an integer
a11y_incorrect_aria_attribute_type_token The value of '%attribute%' must be exactly one of %values%
a11y_incorrect_aria_attribute_type_tokenlist The value of '%attribute%' must be a space-separated list of one or more of %values%
a11y_incorrect_aria_attribute_type_tristate The value of '%attribute%' must be exactly one of true, false, or mixed
a11y_interactive_supports_focus Elements with the '%role%' interactive role must have a tabindex value
a11y_invalid_attribute '%href_value%' is not a valid %href_attribute% attribute
a11y_label_has_associated_control A form label must be associated with a control
a11y_media_has_caption <video> elements must have a <track kind="captions">
a11y_misplaced_role <%name%> should not have role attribute
a11y_misplaced_scope The scope attribute should only be used with <th> elements
a11y_missing_attribute <%name%> element should have %article% %sequence% attribute
a11y_missing_content <%name%> element should contain text
a11y_mouse_events_have_key_events '%event%' event must be accompanied by '%accompanied_by%' event
a11y_no_abstract_role Abstract role '%role%' is forbidden
a11y_no_interactive_element_to_noninteractive_role <%element%> cannot have role '%role%'
a11y_no_noninteractive_element_interactions Non-interactive element <%element%> should not be assigned mouse or keyboard event listeners
a11y_no_noninteractive_element_to_interactive_role Non-interactive element <%element%> cannot have interactive role '%role%'
a11y_no_noninteractive_tabindex noninteractive element cannot have nonnegative tabIndex value
a11y_no_redundant_roles Redundant role '%role%'
a11y_no_static_element_interactions <%element%> with a %handler% handler must have an ARIA role
a11y_positive_tabindex Avoid tabindex values above zero
a11y_role_has_required_aria_props Elements with the ARIA role "%role%" must have the following attributes defined: %props%
a11y_role_supports_aria_props The attribute '%attribute%' is not supported by the role '%role%'
a11y_role_supports_aria_props_implicit The attribute '%attribute%' is not supported by the role '%role%'. This role is implicit on the element <%name%>
a11y_unknown_aria_attribute Unknown aria attribute 'aria-%attribute%'. Did you mean '%suggestion%'?
a11y_unknown_role Unknown role '%role%'. Did you mean '%suggestion%'?

属性 / 元素 / 结构类

警告码 消息模板
attribute_avoid_is The "is" attribute is not supported cross-browser and should be avoided
attribute_global_event_reference You are referencing globalThis.%name%. Did you forget to declare a variable with that name?
attribute_illegal_colon Attributes should not contain ':' characters to prevent ambiguity with Svelte directives
attribute_invalid_property_name '%wrong%' is not a valid HTML attribute. Did you mean '%right%'?
attribute_quoted Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes
bidirectional_control_characters A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences
bind_invalid_each_rest The rest operator (...) will create a new object and binding '%name%' with the original object will not work
block_empty Empty block
component_name_lowercase <%name%> will be treated as an HTML element unless it begins with a capital letter
css_unused_selector Unused CSS selector "%name%"
element_implicitly_closed This element is implicitly closed by the following %tag%, which can cause an unexpected DOM structure. Add an explicit %closing% to avoid surprises.
element_invalid_self_closing_tag Self-closing HTML tags for non-void elements are ambiguous — use <%name% ...></%name%> rather than <%name% ... />
svelte_element_invalid_this this should be an {expression}. Using a string attribute value will cause an error in future versions of Svelte

选项 / 编译配置类

警告码 消息模板
options_deprecated_accessors The accessors option has been deprecated. It will have no effect in runes mode
options_deprecated_immutable The immutable option has been deprecated. It will have no effect in runes mode
options_missing_custom_element The customElement option is used when generating a custom element. Did you forget the customElement: true compile option?
options_removed_enable_sourcemap The enableSourcemap option has been removed. Source maps are always generated now, and tooling can choose to ignore them
options_removed_hydratable The hydratable option has been removed. Svelte components are always hydratable now
options_removed_loop_guard_timeout The loopGuardTimeout option has been removed
options_renamed_ssr_dom generate: "dom" and generate: "ssr" options have been renamed to "client" and "server" respectively

响应式 / 状态类(Runes 时代的核心警告)

警告码 消息模板
non_reactive_update %name% is updated, but is not declared with $state(...). Changing its value will not correctly trigger updates
state_referenced_locally This reference only captures the initial value of %name%. Did you mean to reference it inside a %type% instead?
reactive_declaration_invalid_placement Reactive declarations only exist at the top level of the instance script
reactive_declaration_module_script_dependency Reassignments of module-level declarations will not cause reactive statements to update
store_rune_conflict It looks like you're using the $%name% rune, but there is a local binding called %name%. Referencing a local variable with a $ prefix will create a store subscription. Please rename %name% to avoid the ambiguity

遗留语法 / 版本迁移类

警告码 消息模板
event_directive_deprecated Using on:%name% to listen to the %name% event is deprecated. Use the event attribute on%name% instead
export_let_unused Component has unused export property '%name%'. If it is for external reference only, please consider using export const %name%
legacy_code %code% is no longer valid — please use %suggestion% instead
legacy_component_creation Svelte 5 components are no longer classes. Instantiate them using mount or hydrate (imported from 'svelte') instead.
script_context_deprecated context="module" is deprecated, use the module attribute instead
script_unknown_attribute Unrecognised attribute — should be one of generics, lang or module. If this exists for a preprocessor, ensure that the preprocessor removes it
slot_element_deprecated Using <slot> to render parent content is deprecated. Use {@render ...} tags instead
svelte_component_deprecated <svelte:component> is deprecated in runes mode — components are dynamic by default
svelte_self_deprecated <svelte:self> is deprecated — use self-imports (e.g. import %name% from './%basename%') instead
unknown_code %code% is not a recognised code (did you mean %suggestion%?)

其他

警告码 消息模板
custom_element_props_identifier Using a rest element or a non-destructured declaration with $props() means that Svelte can't infer what properties to expose when creating a custom element. Consider destructuring all the props or explicitly specifying the customElement.props option.
node_invalid_placement_ssr %message%. When rendering this component on the server, the resulting HTML will be modified by the browser (by moving, removing, or inserting elements), likely resulting in a hydration_mismatch warning
perf_avoid_inline_class Avoid 'new class' — instead, declare the class at the top level scope
perf_avoid_nested_class Avoid declaring classes below the top level scope

重点警告逐条精讲

可访问性警告(a11y 系列)

Svelte 把可访问性检查做进了编译器,这是「编译期就发现无障碍缺陷」的核心价值。以下选取参考文档中附了示例的条目展开。

1. 事件与键盘可达性。 a11y_click_events_have_key_events 要求带 onclick 的可见非交互元素必须伴随键盘事件处理器。文档给出的建议顺序是:优先考虑语义化元素——动作用 <button type="button">、导航用 <a>Space/Enter 触发按钮,Enter 触发链接);若必须用非交互元素,则 onclick 应伴随 onkeyup/onkeydown,且元素需要通过 tabindex 变为可聚焦:

<!-- A11y: visible, non-interactive elements with an onclick event must be accompanied by a keyboard event handler. -->
<div onclick={() => {}}></div>

与之互补的 a11y_mouse_events_have_key_events 要求 onmouseover/onmouseout 分别伴随 onfocus(或 onfocusin)和 onblur(或 onfocusout),确保鼠标触发的功能对键盘用户同样可用。

2. 属性存在性与有效性。 a11y_missing_attribute 是一组规则:<a> 应有 href<area>/<object>/<input type="image"> 应有 altaria-labelaria-labelledby<html> 应有 lang<iframe> 应有 title<img> 应有 alta11y_invalid_attribute 则针对值本身——href 不应该是空串、'#'javascript:

<!-- A11y: '' is not a valid href attribute -->
<a href="">invalid</a>

3. ARIA 角色与状态。 这一族规则覆盖 ARIA 的正确使用面:

  • a11y_unknown_role:角色必须有效且非抽象,拼写错误时会给出 did you mean 建议(如 toooltiptooltip);a11y_no_abstract_role 直接禁止抽象角色。
  • a11y_no_redundant_roles<button> 已隐式拥有 button 角色,再写 role="button" 无效且冗余;<img role="img"> 同理。
  • a11y_role_has_required_aria_propsrole="checkbox" 必须定义 aria-checked
<!-- A11y: Elements with the ARIA role "checkbox" must have the following attributes defined: "aria-checked" -->
<span role="checkbox" aria-labelledby="foo" tabindex="0"></span>
  • a11y_role_supports_aria_props / ..._implicit:角色不支持的属性应报错,后者会在提示中说明该角色是元素隐式携带的(如 <li> 隐式是 listitem,不应带 aria-required)。
  • a11y_incorrect_aria_attribute_type 一族按类型细分校验:布尔(aria-hidden="yes" 非法)、ididlistintegertokentokenlist、三态值(true/false/mixed)。
  • 交互性约束:a11y_no_noninteractive_element_to_interactive_role(非交互元素不能升级为交互角色,如 <h3 role="searchbox"> 非法)、a11y_no_interactive_element_to_noninteractive_role(反向降级同样非法,如 <textarea role="listitem">)、a11y_interactive_supports_focus(带交互角色与交互处理器的元素必须 tabindex 可聚焦)、a11y_no_noninteractive_tabindex / a11y_positive_tabindex(Tab 导航只应落在可交互元素上,且正值 tabindex 会扰乱预期 Tab 顺序)。

4. 结构与内容。 a11y_figcaption_parent / a11y_figcaption_index 要求 <figcaption><figure> 的直接子元素且位于首或尾;a11y_missing_content 要求标题和链接有屏幕阅读器可读的内容(空的 <h1>、空 <a> 都会触发);a11y_hidden 阻止用 aria-hidden 隐藏对屏幕阅读器导航有意义的元素(如 <h2>)。

5. 易误用的属性。 参考文档中几条短消息背后是明确的反模式:a11y_accesskey(键盘快捷键与读屏器/纯键盘用户的操作冲突)、a11y_autofocus(自动聚焦对视力和非视力用户都有可用性损害)、a11y_distracting_elements(禁止 <marquee><blink> 等视觉上扰人的废弃元素)、a11y_img_redundant_alt(alt 文本不要以 image/picture/photo 开头,读屏器已会播报这是图片——注意 aria-hidden="true" 的图片不受此规则约束)、a11y_label_has_associated_control<label> 必须通过包裹控件或 for 属性关联到控件)、a11y_media_has_caption<video> 必须带 <track kind="captions">muted 的音视频豁免)。

元素结构警告:浏览器会「悄悄修改」你的 HTML

element_implicitly_closed 指向 HTML 的隐式闭合规则。<p> 不能嵌套 <p>

<!-- this HTML... -->
<p><p>hello</p>

<!-- results in this DOM structure -->
<p></p>
<p>hello</p>

父元素的闭合标签也会隐式闭合所有子元素——即使那个 </ 是笔误、你本意是新建一个元素。文档给出的实操结论是:总是写显式闭合标签以消除歧义

element_invalid_self_closing_tag 针对非 void 元素上的 <span /> 写法。HTML 规范里不存在自闭合标签,规范解析器会把它解析成「文本在元素内部」:

<div>
	<span class="icon" /> some text!
</div>

实际解析结果是 <span class="icon"> some text! </span>。Svelte 选择让你显式消除歧义,并提供了迁移命令自动修复:

npx sv migrate self-closing-tags

node_invalid_placement_ssr 是 SSR 场景下的同族问题:<p> 里放 <div> 会被浏览器自动闭合 <p><option> 里的 <div> 会被删除,<table> 会自动插入 <tbody>。纯客户端渲染时代码能跑,所以这只是警告;但一旦使用服务端渲染,浏览器「修复」后的 DOM 会与 Svelte 的预期结构不一致,导致水合失败并出现 hydration_mismatch 警告。

响应式警告:Svelte 5 状态模型的两个高频陷阱

non_reactive_update 的触发条件是编译器同时检测到:变量未用 $state/$state.raw 声明 + 变量被重新赋值 + 变量在响应式上下文中被读取。此时改变值不会正确触发更新:

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

<p>This value updates: {reactive}</p>
<p>This value does not update: {stale}</p>

<button onclick={() => {
	stale = 'updated';
	reactive = 'updated';
}}>update</button>

修复方式是把声明包进 $state

state_referenced_locally 更隐蔽:它检测「响应式变量被声明、之后被重新赋值、且在同一个作用域内被引用」的组合——这会把引用「钉死」在初始值上,断开与原状态的链接。参考文档的典型案例是把 $state 直接传给 setContext,子组件拿到的永远是初始值:

<!--- file: Parent.svelte --->
<script>
	import { setContext } from 'svelte';

	let count = $state(0);

	// warning: state_referenced_locally
	setContext('count', count);
</script>

<button onclick={() => count++}>
	increment
</button>
<!--- file: Child.svelte --->
<script>
	import { getContext } from 'svelte';

	const count = getContext('count');
</script>

<!-- This will never update -->
<p>The count is {count}</p>

修复手段是改造成惰性求值的引用——包一层函数:

<script>
	import { setContext } from 'svelte';

	let count = $state(0);
	setContext('count', () => count);
</script>
<script>
	import { getContext } from 'svelte';

	const count = getContext('count');
</script>

<!-- This will update -->
<p>The count is {count()}</p>

更多细节可参考 $state 文档 中「Passing state into functions」一节。

版本迁移警告:从 Svelte 4 到 Svelte 5

这批警告是迁移期的「路标」,均指向 v5 迁移指南

  • event_directive_deprecatedon:click 指令式监听已废弃,应使用属性语法 onclick
  • slot_element_deprecated<slot> 渲染父级内容的方式被 {@render ...} 标签取代。
  • svelte_component_deprecated:Runes 模式下组件默认就是动态的——X 变化时 <X> 会重新渲染,不再需要 <svelte:component this={X}>{#key X}。简单场景可用小写属性访问语法(如 <obj.component />);复杂解析逻辑可借助 @const$derived 引入一个首字母大写的中间变量:
{#each items as item}
	{@const Component = item.condition ? Y : Z}
	<Component />
{/each}
  • svelte_self_deprecated:递归组件改用自导入(import App from './App.svelte')。
  • legacy_component_creation:Svelte 5 组件不再是 class,实例化方式改为从 svelte 导入的 mount / hydrate
  • script_context_deprecated<script context="module"> 改为 <script module>
  • 选项类accessorsimmutable 在 runes 模式下失效;enableSourcemaphydratableloopGuardTimeout 已移除(源码映射始终生成、组件始终可水合);generate: "dom"/"ssr" 改名为 "client"/"server"

其他值得注意的警告

  • css_unused_selector:编译器会同时遍历模板和 <style> 标签,找出模板中用不到的选择器并剔除。当选择器指向编译器「看不见」的元素({@html ...} 内容、子组件内部覆盖样式)时,用 :global 保留原样:
<div class="post">{@html content}</div>

<style>
  .post :global {
    p {...}
  }
</style>

相关机制见 global styles 文档

  • bidirectional_control_characters:检测双向控制字符。这类字符可改变代码的视觉呈现方向(例如让 defabc 看起来像 abcdef),从外部无意识复制粘贴进来的代码可能因此产生意想不到的行为——这是一条安全类警告。

  • store_rune_conflict:当你写了 $foo 形式的本地绑定又恰好有同名变量时,$ 前缀会被解释为 store 订阅,需要重命名消除歧义。

  • attribute_quoted:组件/自定义元素上带引号的属性在后续版本中将被字符串化,不想要字符串请去掉引号。

  • perf_avoid_inline_class / perf_avoid_nested_class:避免内联 new class 与嵌套类声明,类应声明在顶层作用域。

  • unknown_code / legacy_codesvelte-ignore 注释里写了不认识或已废弃的码时会触发(见下文机制)。

警告消息的生成管线:从 Markdown 到可执行代码

参考文档第一行写着 This file is generated by scripts/process-messages/index.js. Do not edit!——警告体系在仓库中是「一份源、三处产物」。从源码结构看,process-messages/index.js 的管线如下:

  1. 消息源文件位于 packages/svelte/messages 下,按类别分目录:compile-warnings(含 a11y.md、misc.md、options.md、script.md、style.md、template.md)、client-errorsserver-errorsshared-warnings 等。每个警告用 ## code 二级标题定义,正文里以 > 开头的行是消息模板,其余段落是写进文档的详细说明。

  2. 解析与重排:脚本用正则 ## ([\w]+)\n\n([^]+?)(?=$|\n\n## ) 逐条切分,遇到重复码会抛错(Duplicate message code),然后把每个文件内的码按字母序重排回写。

  3. 双产物输出

    • 文档侧:把所有类别拼成 documentation/docs/98-reference/.generated/<category>.md,格式为 ### code + 消息模板代码块 + 详情说明——这正是本文参考的文件;
    • 代码侧:通过 AST 转换(acorn 解析 + zimmerframe 走查 + esrap 打印)把 templates/<category>.js 中的占位 export function CODE 函数模板为每个码生成具体函数。消息模板中的 %name% 占位符被展开为函数参数(PARAMETER 会被替换成全部变量名),多模板消息(如 unknown_code 的带/不带 suggestion 两个版本)会被编译成按参数存在性的条件表达式,CODES 标识符则被替换为全部码名的数组字面量。
  4. 最终落点:编译期警告函数写入 warnings.jstransform('compile-warnings', 'src/compiler/warnings.js')),该文件导出的 codes 数组即当前全部合法警告码,是校验 svelte-ignore 的依据。运行期警告同理生成到 src/internal/client|server|shared 下对应文件。

这意味着:新增或修改一条警告只需编辑 messages/ 下的 Markdown,文档与代码会自动保持一致;反过来,你在 .generated/compile-warnings.md 里看到的任何内容都以 messages/ 目录为准。

svelte-ignore 抑制误报

部分警告在你的具体场景下可能是误报,官方给出的处理方式是:在触发警告的那一行上方放置 <!-- svelte-ignore <code> --> 注释,例如:

<!-- svelte-ignore a11y_autofocus -->
<input autofocus />

一条注释里可以用逗号列出多个规则,并附加括号形式的说明:

<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions (because of reasons) -->
<div onclick>...</div>

其底层实现在 extract_svelte_ignore.js,几个值得知道的细节:

  • 码名校验与模糊建议:注释中的码会先对照 w.codes 加可忽略的运行期警告码(IGNORABLE_RUNTIME_WARNINGS);不在其中的码会先查 replacements 旧码映射表(如 empty-blockblock_emptyavoid-isattribute_avoid_is),再尝试把 - 替换为 _;仍不认识则触发 unknown_code 警告并给出 fuzzymatch 建议,旧码则触发 legacy_code 警告。
  • Runes 模式下的严格语法:runes 文件里,码与码之间必须用逗号分隔,第一个逗号之后的内容被视为说明文字(prose),因此上面的「(because of reasons)」写法只在 runes 模式下合法;非 runes 模式采用宽松解析以兼容旧版写法。
  • 抑制的生效机制warnings.js 中的 w() 函数在每条警告发出前会检查 ignore_map / ignore_stack——若该节点(或作用域栈顶)已声明忽略此码,则直接吞掉警告。

实践清单

  1. 把警告当静态分析用:a11y 系列能在编译期发现大量无障碍缺陷,建议 CI 中保留警告输出而不是一律静音。
  2. 优先修而不是 ignoresvelte-ignore 应只用于确认为误报的场景,并遵守 runes 模式的「逗号 + 括号说明」格式,避免触发 unknown_code
  3. 迁移项目:若看到 event_directive_deprecatedslot_element_deprecatedlegacy_component_creation 等,对照 v5 迁移指南 逐项处理;自闭合标签可批量执行 npx sv migrate self-closing-tags
  4. 状态相关警告别放过non_reactive_updatestate_referenced_locally 指向的是「UI 不更新」这类最难排查的运行时 bug,编译器替你提前发现,务必修正。
  5. 查文档看两个位置:可读性优先看 .generated/compile-warnings.md;需要修改消息文案或新增警告,编辑 packages/svelte/messages 下的源 Markdown 并运行生成脚本,切勿手改生成产物。
登录后查看全文
热门项目推荐
相关项目推荐