番茄钟也能很简单:Svelte + Vite的状态管理实践

举报
yd_233495430 发表于 2026/08/30 20:26:31 2026/08/30
【摘要】 用 Svelte + Vite 写番茄钟:let 即状态、赋值即更新、$: 响应式语句兼做派生显示与 localStorage 持久化,setInterval 驱动 25 分钟计时。对比 React 说明状态管理更省事,并提定时器清理/后台节流等坑。

一、背景:番茄钟是练前端响应式的绝佳小题

番茄工作法(25 分钟专注、5 分钟休息,循环)很多人都知道,但真用 App 计时,要么功能花哨、要么要账号。自己写一个最朴素的番茄钟——开始/暂停/重置、到点响一下、记录完成了几个——既能用,又能把"状态怎么驱动界面"这件前端基本功练一遍。

选 Svelte 是因为它在这类小工具上特别顺手:没有虚拟 DOM 那套、状态用 let$: 响应式语句就能驱动界面,状态管理几乎没有额外概念,比 React 的 useState/useEffect 心智负担小。本文用 Svelte + Vite 写一个番茄钟,讲清响应式、计时器和持久化。具体 API 以 Svelte 官方文档为准。

二、Svelte 的响应式:赋值即更新

Svelte 的核心理念是"对状态的赋值,自动触发依赖它的界面更新"。不需要 setState、不需要 ref.value,就是普通的变量赋值:

<script>
  let seconds = 0          // 已计秒数
  let running = false
  $: display = format(seconds)   // $: 表示"响应式语句",seconds 变它就重算

  function format(s) {
    const m = String(Math.floor(s/60)).padStart(2,'0')
    const ss = String(s%60).padStart(2,'0')
    return `${m}:${ss}`
  }
</script>

<h1>{display}</h1>
<button on:click={() => running = !running}>{running ? '暂停' : '开始'}</button>

let seconds = 0 就是状态;$: display = format(seconds) 是响应式派生——只要 seconds 变了,display 自动重算,<h1> 跟着变。点按钮 running = !running 也只是赋值,Svelte 自动重渲染用到 running 的地方。这套"赋值即更新"比 React 套 useState 直观很多,小工具尤其省事。

三、计时器:setInterval 驱动秒数

番茄钟的核心是每秒 +1。用 setIntervalrunning 为 true 时跑,暂停时清掉:

<script>
  let seconds = 0, running = false, timer = null
  const WORK = 25 * 60

  function toggle() {
    running = !running
    if (running) {
      timer = setInterval(() => {
        seconds += 1
        if (seconds >= WORK) { finish() }
      }, 1000)
    } else {
      clearInterval(timer)
    }
  }
  function finish() {
    clearInterval(timer); running = false; seconds = 0
    pomodoroCount += 1        // 完成一个番茄
    alert('专注完成,休息一下')
  }
</script>

toggle 在"开始"时建 setInterval、每秒 seconds += 1;"暂停"时 clearInterval 停表。注意 seconds += 1 是赋值,Svelte 会更新界面——这就是 Svelte 的妙处,不用包 setState。到 WORK(25 分钟)调 finish:清定时器、归零、pomodoroCount += 1 记录完成数、提示休息。pomodoroCount 也是普通变量,照样响应式。

四、持久化:今天完成了几个要留着

完成的番茄数、当前设置,刷新页面就丢不好。用 localStorage 落盘:

<script>
  let pomodoroCount = Number(localStorage.getItem('pomo') || 0)
  $: localStorage.setItem('pomo', pomodoroCount)   // 响应式:一变就存
</script>

pomodoroCount 初始化从 localStorage 读;$: localStorage.setItem(...) 是响应式语句——只要 pomodoroCount 变,就写回磁盘。Svelte 的 $: 在这里既做派生显示、又做副作用(持久化),一个语法两种用途,很紧凑。注意 localStorage 只能存字符串,读用 Number(...) 转回数字。

五、几个要注意的点

第一,setInterval 的清理。组件卸载或暂停时必须 clearInterval,否则定时器还在跑、界面已不在,会报"更新已卸载组件"或计数偷偷涨。Svelte 里可以用 onDestroy(() => clearInterval(timer)) 兜底清,别只靠暂停按钮清。

第二,页面后台会节流。浏览器对后台标签页的 setInterval 有节流(可能降到 1 秒一次都不保证),前台切后台再切回,计时可能偏慢。严肃计时场景用"基于时间戳"算(Date.now() 差值)而非单纯累加秒数,更准。番茄钟这种容忍几秒误差的,累加也够用,知道有这限制即可。

第三,**:的依赖要纯。响应式语句: 的依赖要纯**。响应式语句 `: x = f(a)依赖它"读到的"变量(这里是a)。如果 f` 里偷偷读了别的状态却没出现在赋值右侧,Svelte 可能不重算。保持响应式依赖显式、纯函数,避免隐蔽 bug。

第四,状态别散太多。番茄钟状态就 seconds / running / pomodoroCount 几个,全在组件里。如果状态要跨多个组件共享,用 Svelte 的 writable store(import { writable } from 'svelte/store')集中管理,和前面 Vue 用 Pinia 思路一致,别各组件各一份。

第五,响铃体验。纯 alert 粗糙,真要做可以 <audio> 放一段提示音,或用 Notification API 弹系统通知。演示用 alert 足够说明流程。

28964e9a-adc2-408a-a4e8-a19a234f6263.png

六、小结

用 Svelte 写番茄钟,状态管理成本低到几乎"无感":普通 let 是状态、赋值即更新、$: 响应式语句既做派生显示(如 display)又做副作用(如持久化)。计时靠 setInterval 每秒 seconds += 1,到 25 分钟 finish 记一个完成数。比 React 的 useState/useEffect 心智负担小,适合这类小工具。注意定时器要清理、后台标签计时会节流、跨组件状态用 writable store。Svelte 语法和生命周期 API 以官方文档为准。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。