Svelte这几年越来越火,GitHub star数增长很快,很多公司开始在项目中使用Svelte,面试中也经常被问到。

我最近面试了几家公司,被问了很多Svelte相关的问题,有的简单有的深入。有的问题我答得很好,有的问题答得不好,回来后查了很多资料才搞明白。

今天整理了我被问到的Svelte面试题,包括基础概念、响应式原理、组件通信、性能优化、和React/Vue的对比等,附上详细答案。不管你是准备面试,还是想深入学习Svelte,都希望这篇文章能帮到你。

一、基础概念题

1. Svelte是什么?和React、Vue有什么区别?

Svelte是一个前端框架,由Rich Harris(Rollup作者)开发。它最大的特点是编译时框架,在构建时把组件编译成原生JavaScript,没有虚拟DOM,运行时开销小。

和React、Vue的区别:

  • React和Vue是运行时框架,需要带框架运行时,用虚拟DOM diff来更新页面
  • Svelte是编译时框架,构建时就把组件编译成直接操作DOM的代码,没有虚拟DOM,性能更好,包体积更小
  • Svelte的语法更简洁,代码量更少,学习成本低
  • React和Vue生态更成熟,Svelte生态相对小一些

2. Svelte的响应式原理是什么?

Svelte的响应式是基于编译时分析的。编译器在构建时分析组件中的变量依赖,当变量变化时,直接更新对应的DOM,不需要虚拟DOM diff。

具体来说:

  • 用let声明的变量,重新赋值时会触发更新
  • 用$:标记的响应式语句,会在依赖变化时自动重新执行
  • 编译器在构建时就分析好了依赖关系,生成直接更新DOM的代码
  • 没有数据劫持(Vue的Object.defineProperty/Proxy),也没有状态管理(React的setState),就是普通的变量赋值

3. Svelte组件的结构是什么?

一个Svelte组件就是一个.svelte文件,包含三部分:

  • script标签:JavaScript逻辑
  • 普通HTML标签:模板
  • style标签:样式(默认组件作用域)

和Vue的单文件组件类似,但Svelte的模板就是原生HTML,没有指令系统(v-if、v-for这些),用JavaScript的语法({#if}、{#each})。

4. Svelte中的$:是什么?

$:是Svelte的响应式标记,放在语句前面,表示这条语句是响应式的,当依赖的变量变化时会自动重新执行。

比如:

<script>
  let count = 0;
  $: doubled = count * 2;
  $: if (count > 10) {
    console.log('count is too big');
  }
</script>

当count变化时,doubled会自动更新,if语句也会自动重新执行。$:可以用在变量声明、表达式、if语句、for循环等任何语句前面。

5. Svelte的样式默认是作用域的吗?怎么写全局样式?

是的,Svelte的style标签里的样式默认是组件作用域的,编译器会给元素加一个唯一的属性(比如class="svelte-xxx"),样式只作用于当前组件。

要写全局样式,用:global()包裹:

<style>
  :global(body) {
    margin: 0;
  }
  .local {
    color: red;
  }
</style>

也可以用没有scoped属性的style标签(但Svelte默认就是scoped,要全局就用:global)。

二、响应式和状态管理题

6. Svelte怎么实现组件间通信?

Svelte组件间通信有几种方式:

  • props:父组件通过属性传数据给子组件,子组件用export let声明props。和Vue类似。
  • 事件:子组件通过createEventDispatcher派发事件,父组件用on:监听。和Vue的$emit类似。
  • slot:内容分发,父组件把内容插到子组件的slot里。
  • context:setContext和getContext,跨层级传递数据,不需要一层层props。
  • store:全局状态管理,跨组件共享状态。

7. Svelte的store是什么?怎么用?

store是Svelte的全局状态管理方案,是一个可订阅的对象。Svelte内置了writable、readable、derived三种store。

writable可写的store:

import { writable } from 'svelte/store';
export const count = writable(0);

在组件中用$前缀订阅:

<script>
  import { count } from './store.js';
</script>
<button on:click={() => $count++}>{$count}</button>

$count是语法糖,自动订阅store,组件销毁时自动取消订阅。也可以手动调用subscribe方法。

readable是只读的store,derived是从其他store派生的store。

8. Svelte的响应式和Vue的响应式有什么区别?

  • Vue 2用Object.defineProperty,Vue 3用Proxy,在运行时劫持数据的读写
  • Svelte在编译时分析依赖,生成直接更新的代码,运行时没有劫持
  • Vue的响应式对数组、对象有特殊处理(Vue 2需要特殊方法),Svelte就是普通变量,重新赋值就触发更新
  • Svelte的响应式更简单直接,但只能在组件内用$:,跨组件需要store
  • Vue的响应式更强大,有计算属性、watch等完整的系统

9. Svelte中怎么处理异步?

Svelte模板中可以用{#await}处理异步:

{#await promise}
  <p>Loading...</p>
{:then value}
  <p>{value}</p>
{:catch error}
  <p>{error.message}</p>
{/await}

这是Svelte特有的语法,很方便处理异步加载的状态。

逻辑中用async/await,和普通JavaScript一样。

三、性能优化题

10. Svelte为什么性能好?

Svelte性能好的原因:

  • 没有虚拟DOM,编译时直接生成DOM操作代码,没有diff开销
  • 精确更新:编译器分析出哪个变量变化影响哪个DOM,只更新需要更新的部分,比虚拟DOM的整树diff更精确
  • 包体积小:没有运行时,编译后的代码很小,首屏加载快
  • 响应式开销小:没有数据劫持,就是普通变量赋值

但要注意,Svelte不是所有场景都比React快。极端情况下,比如大量节点同时更新,React的批量更新和虚拟DOM可能更有优势。但大部分场景下Svelte性能更好。

11. Svelte怎么优化性能?

虽然Svelte默认性能就不错,但还是有一些优化技巧:

  • keyed each:列表渲染用{#each items as item (item.id)},加key,这样列表变化时只更新变化的项,不会全部重建
  • 避免不必要的响应式:不需要响应式的变量不要用$:,减少编译生成的更新代码
  • 延迟渲染:用{#if}条件渲染,不需要的内容不渲染
  • 虚拟滚动:长列表用虚拟滚动,只渲染可见区域
  • 图片懒加载:loading="lazy"或者用Intersection Observer
  • 代码分割:用动态import()按需加载组件
  • 避免在each里用复杂计算:把计算提到外面,用derived store或者$:

12. Svelte的编译时优化有哪些?

Svelte编译器在构建时做了很多优化:

  • 分析依赖,生成精确的DOM更新代码
  • 样式作用域,给元素加唯一属性
  • 无用代码消除(tree-shaking),只打包用到的功能
  • 静态分析,把不变的内容提取出来
  • 事件委托,减少事件监听器
  • 优化循环和条件渲染

四、生态和工程化题

13. SvelteKit是什么?

SvelteKit是Svelte官方的全栈应用框架,类似Next.js之于React。支持:

  • 服务端渲染(SSR)
  • 静态站点生成(SSG)
  • 客户端路由
  • API routes
  • 代码分割
  • 适配多种部署平台(Vercel、Netlify、Node.js等)

SvelteKit基于Vite构建,开发体验好。如果用Svelte做完整应用,推荐用SvelteKit。

14. Svelte支持TypeScript吗?

支持。在script标签加lang="ts"就能用TypeScript:

<script lang="ts">
  let count: number = 0;
</script>

SvelteKit默认支持TypeScript,普通Svelte项目需要配置svelte-preprocess。

15. Svelte的测试怎么做?

组件测试可以用@testing-library/svelte,配合Vitest或Jest。端到端测试用Playwright或Cypress。

Svelte的组件测试和React Testing Library类似,渲染组件,查询元素,模拟交互,断言结果。

16. Svelte有哪些常用的UI库?

Svelte的UI库生态不如React丰富,但也有一些不错的:

  • Svelte Material UI:Material Design风格
  • Smelte:Material Design,基于Tailwind
  • Carbon Components Svelte:IBM的Carbon设计系统
  • Skeleton:基于Tailwind的UI库
  • Flowbite Svelte:基于Tailwind
  • DaisyUI:基于Tailwind(需要svelte集成)

如果没有合适的UI库,也可以自己封装组件,Svelte写组件很简单。

五、对比和深入题

17. Svelte和React怎么选?

选Svelte的情况:

  • 项目中小型,团队小,追求开发效率和性能
  • 包体积要求高,比如移动端、嵌入式
  • 团队喜欢简洁的语法,不想学Hooks那一套
  • 个人项目、工具类应用、活动页

选React的情况:

  • 大型项目,需要成熟的生态和组件库
  • 团队大,需要招很多人,React人才多
  • 需要复杂的状态管理和大量第三方库
  • 公司技术栈是React,有历史积累

18. Svelte的缺点是什么?

Svelte的缺点:

  • 生态不如React、Vue成熟,UI库、第三方工具少
  • 就业市场小,招Svelte开发的公司不多
  • 大型项目的最佳实践不如React多,复杂状态管理需要自己设计
  • 社区规模小,遇到问题搜索到的解决方案少
  • 有些IDE支持不如React(但已经在改善)

19. Svelte的each循环为什么要加key?

和React、Vue的key类似,key帮助Svelte识别列表中哪些项变化了。如果没有key,列表变化时Svelte可能会重建所有项,而不是只更新变化的项,影响性能。

加了key后,Svelte会根据key匹配新旧列表,只更新、添加、删除变化的项。

{#each items as item (item.id)}
  <div>{item.name}</div>
{/each}

20. Svelte的事件处理有什么特别的?

Svelte的事件用on:前缀:

<button on:click={handleClick}>Click</button>

可以用事件修饰符:

<form on:submit|preventDefault={handleSubmit}>...</form>

支持的修饰符:preventDefault、stopPropagation、capture、once、passive、self、trusted等。

还可以用on:*转发所有事件,或者用createEventDispatcher自定义事件。

六、实战和场景题

21. Svelte中怎么实现表单绑定?

用bind:实现双向绑定:

<script>
  let name = '';
  let email = '';
</script>
<input bind:value={name} placeholder="姓名">
<input bind:value={email} type="email" placeholder="邮箱">

支持的绑定:value、checked、files、innerHTML、textContent,以及各种元素属性(class、style、href等),还有组件的props绑定(bind:prop)。

22. Svelte中怎么实现生命周期?

Svelte的生命周期函数:

  • onMount:组件挂载后执行,类似React的useEffect(() => {}, [])
  • beforeUpdate:DOM更新前执行
  • afterUpdate:DOM更新后执行
  • onDestroy:组件销毁前执行,类似useEffect的清理函数
<script>
  import { onMount, onDestroy } from 'svelte';
  onMount(() => {
    console.log('mounted');
  });
  onDestroy(() => {
    console.log('destroyed');
  });
</script>

23. Svelte中怎么处理错误?

Svelte有错误边界的概念,用<ErrorBoundary>组件(需要svelte-error-boundary库,或者SvelteKit内置)。组件内可以用try/catch处理逻辑错误。

SvelteKit有错误处理机制,+error.svelte文件处理错误页面。

24. Svelte中怎么做代码分割和懒加载?

用动态import():

<script>
  let Component;
  async function load() {
    Component = (await import('./HeavyComponent.svelte')).default;
  }
</script>
{#if Component}
  <svelte:component this={Component} />
{/if}

SvelteKit自动做路由级代码分割,每个页面单独打包。

七、写在最后

以上就是我面试中被问到的Svelte相关问题,整理成了24道题,附了详细答案。这些题目覆盖了基础概念、响应式原理、组件通信、性能优化、生态工具、对比分析、实战场景等方面。

Svelte是一个很优秀的前端框架,简洁、高效、性能好。虽然生态还不如React成熟,但发展很快,值得前端开发者学习和关注。

面试的时候,除了背题,更重要的是真正理解原理,有实际项目经验。面试官可能会根据你的回答深入追问,只有真正用过、理解了,才能应对自如。

如果你也在准备Svelte面试,希望这篇文章能帮到你。如果你有其他面试题或者更好的答案,欢迎在评论区交流。

最后,祝大家面试顺利,拿到心仪的offer。也祝Svelte生态越来越好,有更多人用Svelte做出优秀的产品。