SvelteKit实战:从项目搭建到性能优化的完整指南

码农松奇 框架 阅读 1,627
赞 6 收藏
二维码
手机扫码查看
反馈

优化前:卡得不行

上个月接手一个 SvelteKit 项目,首页加载时间动不动就 4-5 秒,用户反馈“点进去以为页面挂了”。我自己本地 dev 模式跑起来也慢得离谱,首屏白屏时间长,滚动还卡顿。尤其在低端安卓机上,简直没法用。说实话,一开始我以为是后端接口慢,结果查了 network 面板才发现,光 JS bundle 就快 2MB(未压缩),而且主路由里塞了一堆没用的逻辑。

SvelteKit实战:从项目搭建到性能优化的完整指南

最头疼的是,这项目用了好几个第三方库(比如一个图表库、一个富文本编辑器),但其实只在某个子页面用到,却全被打包进主入口了。我试过直接删代码,但改完又怕影响其他功能,不敢动。折腾了两天,决定系统性地搞一次性能优化。

找到瓶颈了!

先别急着改代码,得先知道问题在哪。我打开 Chrome DevTools,重点看了 Performance 和 Lighthouse 报告。Lighthouse 给的首屏性能分只有 38,主要扣分项是“减少 JavaScript 执行时间”和“避免庞大的 DOM 结构”。

然后我用 svelte-checkvite-bundle-visualizer 看了打包体积。果然,那个图表库(ECharts)占了 600KB+,而它只在 /dashboard 页面用。还有个日期选择器组件,虽然小,但被全局引入了,每个页面都带着走。

另外,我发现有些数据请求写在 +page.svelteonMount 里,导致页面渲染完才去拉数据,用户看到的是“加载中”转圈好几秒。这体验太差了。

核心优化:拆包 + 数据预加载

第一个动作:把大块第三方库动态导入。SvelteKit 支持在 load 函数里做异步加载,还能配合 await import() 实现按需加载。

比如 ECharts,我原来这么写:

<!-- 旧写法:全局引入 -->
<script>
  import * as echarts from 'echarts';
  // ...一堆初始化代码
</script>

现在改成在 +page.js 的 load 函数里动态加载,并把实例传给页面:

// src/routes/dashboard/+page.js
export async function load() {
  const { default: initChart } = await import('$lib/utils/initEcharts');
  const chartData = await fetch('https://jztheme.com/api/chart-data').then(r => r.json());
  return {
    chartData,
    initChart
  };
}

然后在 +page.svelte 里:

<script>
  export let data;
  import { onMount } from 'svelte';

  onMount(() => {
    data.initChart(data.chartData);
  });
</script>

这样,ECharts 只会在访问 /dashboard 时才下载,主包体积直接少了 600KB。亲测有效!

第二个关键点:把数据请求从 onMount 移到 load。SvelteKit 的 load 在服务端(SSR)或客户端(CSR)都能运行,而且能并行请求,还能缓存。我改完之后,首页数据在 HTML 返回时就已经嵌入了,首屏不再白屏。

比如原来的首页:

<!-- 旧:onMount 里 fetch -->
<script>
  import { onMount } from 'svelte';
  let posts = [];

  onMount(async () => {
    const res = await fetch('/api/posts');
    posts = await res.json();
  });
</script>

现在改成:

// src/routes/+page.js
export async function load() {
  const posts = await fetch('https://jztheme.com/api/posts').then(r => r.json());
  return { posts };
}
<!-- src/routes/+page.svelte -->
<script>
  export let data;
  // 直接用 data.posts,无需额外请求
</script>

注意:这里我踩过坑——如果 API 地址写成相对路径(如 /api/posts),在 SSR 时会报错,因为服务端不知道这个路径对应哪个后端。所以要么用完整 URL,要么在 hooks.server.js 里代理。我图省事,直接用了完整 URL 示例(实际项目中会配环境变量)。

其他小优化(带过)

  • 图片懒加载:给 <img>loading="lazy",简单粗暴有效。
  • 移除未使用的 CSS:Svelte 本身 scoped CSS 很干净,但有些全局样式残留,用 PurgeCSS 清理了一下(不过 SvelteKit 默认不集成,我手动加了 Vite 插件)。
  • 减少 store 使用:发现有些地方用 store 存临时状态,其实用 local state 就够了,减少了不必要的 reactivity 开销。

还有一个细节:有些组件内部用了 {#if} 做条件渲染,但判断逻辑复杂,导致每次响应式更新都重新计算。我把它提到父组件,或者用 memoized 函数缓存,避免重复计算。

性能数据对比

优化前后,我在同一台机器(MacBook Pro M1)、同一网络环境下,用 Lighthouse 跑了三次取平均值:

  • 首屏加载时间:从 4.8s 降到 780ms
  • JS bundle size:从 1.9MB 降到 620KB(gzip 后)
  • Lighthouse 性能分:从 38 提升到 89

最明显的是,现在打开首页几乎瞬间出内容,滚动也流畅了。用户反馈“变快了”,虽然他们不知道具体技术细节,但体验提升是实打实的。

当然,也不是完美无缺。比如动态导入的 chunk 会有轻微延迟(首次进入 dashboard 时图表要等 200ms 才出现),但加个骨架屏就解决了,用户感知不强。另外,SSR 时如果 API 慢,还是会阻塞,但这属于后端问题,前端只能等。

结尾

以上是我这次 SvelteKit 性能优化的实战总结。核心就是两点:**按需加载第三方库** + **用 load 函数预取数据**。其他都是锦上添花。

这个方案不是最优解(比如还可以用 streaming SSR 或 partial hydration),但胜在简单、改动小、见效快。毕竟项目赶上线,没时间搞太复杂的架构。

以上是我踩坑后的总结,希望对你有帮助。有更优的实现方式欢迎评论区交流,比如你们怎么处理大型第三方库的?或者有没有更好的 SSR 数据缓存策略?

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论