Svelte 与 React 深度对比:状态管理与组件渲染实战分析

作者:小齿轮时间:2026-10-26 13:49:50浏览:334

本文深入对比 Svelte 与 React 在身份验证状态管理和 Web Worker 集成方面的差异。通过具体代码示例,分析 React 中 useEffect 与 useRef 的使用复杂性,以及 Svelte 中 onMount 和可写存储带来的简洁性。同时探讨条件渲染中模板语法与纯 JavaScript 的权衡,揭示两种框架在开发体验和代码维护上的不同侧重。

Svelte 与 React 对比分析

本文旨在客观比较 Svelte 和 React 两种框架,并非表达个人偏好,而是总结在实际开发中观察到的两者差异。

身份验证状态管理

在 React 项目中,我定义了一个 useCurrentUser 自定义钩子,用于监听身份验证状态的变化并更新相应状态。该钩子可在 React 应用的任何位置调用,当身份验证状态发生更改时,会触发页面重新渲染。

export const useCurrentUser = () => {
  const [currentUser, setCurrentUser] = useState(undefined)

  useEffect(() => {
    return firebase.auth().onAuthStateChanged((details) => {
      setCurrentUser(
        details
          ? {
              displayName: details.displayName,
              provider: {
                'github.com': 'GitHub',
              }[details.providerData[0].providerId],
              uid: details.uid,
            }
          : null
      )
    })
  }, [])
  return [currentUser]
}

在任何组件中,均可通过以下方式调用:

const [currentUser] = useCurrentUser()

这种机制使得任何组件都能快速获取当前用户信息。然而,其潜在缺点是每个页面都包含一个 onAuthStateChanged 监听器,可能带来性能开销。

不过,可以通过限制监听器数量或将其置于执行上下文中来优化这一问题。

在 Svelte 中,可以采用类似方法,利用可写存储(writable store)来管理状态。

export const currentUser = writable()

export const listenForAuthChanges = () => {
  return firebase.auth().onAuthStateChanged((details) => {
    if (details) {
      currentUser.set({
        displayName: details.displayName,
        provider: { 
          'github.com': 'GitHub',
        }[details.providerData[0].providerId],
        uid: details.uid,
      })
    } else {
      currentUser.set(null)
    }
  })
}

在 Svelte 的顶层组件中,我可以在 onMount 生命周期钩子内调用该函数。该函数由 return 编辑,因此在组件卸载时会取消订阅,这类似于 useEffect 返回清理函数的机制。

onMount(() => {
  return listenForAuthChanges()
})

这样,Svelte 代码库中的任何组件都可以导入 currentUser 可写存储。你可以订阅它并手动控制状态变更:

currentUser.subscribe(newValue => {
  ...
})

或者,若需读取最新值:

console.log($currentUser)

这是 Svelte 语法的一个巧妙之处;使用 $ 前缀即可直接获取最新值。

虽然这对初学者来说可能略显陌生,但 Svelte 因此免去了调用 subscribe API 的繁琐步骤。

就基础身份验证而言,这两个库采用了相似的方法。尽管具体语法略有不同,但两者都允许订阅 Firebase 监听器并在身份验证状态变化时接收更新。

从使用者角度开发:Web Worker 集成

Pomodone 是一个旨在精确计时 25 分钟的计时器。当浏览器标签页处于后台(非焦点状态)时,大多数浏览器会降低 setTimeout 调用的优先级,导致其无法严格按时执行。虽然在普通网络应用中这通常不是问题,但当用户通过应用追踪 25 分钟的工作时,任何轻微的时间漂移都会导致最终结果出现显著偏差。然而,如果将这些超时任务移至 Web Worker 中,它们预计能按时执行,且不会被浏览器降低优先级。

因此,在我的 Tracker 组件中,我需要实例化一个 Web Worker,向其发送消息并接收数据(如剩余时间)。在此过程中,我发现 React 在管理这一领域时比 Svelte 更为复杂;

由于 React 组件在每次重新渲染时都会重新执行,这极易导致创建数千个 Worker 实例!必须使用 useRef 来维护对已创建 Worker 的引用,从而避免此问题。

首先,我设置了组件所需的初始状态:

const [currentPom, setCurrentPom] = useState(null)
const [currentUser] = useCurrentUser()
const worker = useRef(null)

接着创建一个 useEffect 钩子,在需要时实例化 Worker,并绑定事件监听器以接收消息:

useEffect(() => {
  if (!worker.current) {
    worker.current = new Worker(workerURL)
    window.worker = worker.current
  }

  const onMessage = (event) => {
    if (event.data.name === 'tick') {
      setCurrentPom((currentPom) => ({
        ...currentPom,
        secondsRemaining: event.data.counter,
      }))
    } else if (event.data.name === 'start') {
      // More branches removed here to save space...
    }
  }
  worker.current.addEventListener('message', onMessage)

  return () => {
    worker.current.removeEventListener('message', onMessage)
  }
}, [currentUser])

随后,当用户点击“开始”按钮时,我必须向 Worker 发送一条消息:

const onStartPom = () => {
  if (!worker.current) return
  worker.current.postMessage('startTimer')
}

Svelte 的代码结构看似相似,但在我看来,有两处细微改动使 Svelte 代码更具可读性:

  • 我们无需将 Worker 实例保存在 useRef 中,直接将其赋值给变量即可。
  • 我们可以更轻松地将事件监听器代码提取到一个独立函数中,因为该函数不会成为 useEffect 的依赖项——而在 React 中,我们不得不将其包装在 useCallback 中。
let worker
onMount(() => {
  worker = new Worker(workerURL)
  worker.addEventListener('message', onWorkerMessage)
  return () => {
    worker.removeEventListener('message', onWorkerMessage)
  }
})

此外,我们无需遵循 React 的 setX(oldX => newX) 约定来设置状态,直接修改局部变量即可:

function onWorkerMessage(event) {
  if (event.data.name === 'tick') {
    currentPom = {
      ...currentPom,
      secondsRemaining: event.data.counter,
    }
  } else if (event.data.name === 'start') {
    // More branches here removed to save space...
  }
}

这正是 Svelte 的优势所在;两者逻辑相似,但一旦习惯 Svelte,会感觉 React 像是在“跳圈”。你不能直接创建 Worker 实例,它必须进入 useRef,而且不能轻易将代码提取到函数中而不使用 useCallback,以确保其对 useEffect 的安全依赖。

使用 Svelte 时,我编写的代码更接近“纯”JavaScript;而在 React 中,我的大量代码被包裹在 React 原语之中。

条件渲染

React 强调“它只是 JavaScript”。与 Svelte 的模板语言相比,在 React 中你无需使用独特的模板语法,而是直接编写 JavaScript:

SideTabs

<ul>
  {pomsForCurrentDay.map(entryData, index) => {
    const finishedAt = format(new Date(entryData.timeFinished), 'H:mm:ss')
    return <li title={`Finished at ${finishedAt}`}>{index + 1}</li>
  })}
</ul>

过去,我曾认为模板语言虽具趋势但缺乏灵活性,而 Svelte 提供了恰到好处的模板,同时允许使用 JavaScript。不过,我始终觉得 React 的方法更简单——至少在思维模型上——我认为这对熟悉 JavaScript 并正在学习库的开发者更为友好。

然而,在渲染组件时,Svelte 确实对其语法进行了一些不错的改进(感觉非常像 JSX)。我最喜欢的是折叠道具的能力:

<History pomodoros={pomodoros} />

Svelte (折叠道具示例)

<History {pomodoros}/>

至此,关于 Svelte 和 React 的比较详解(一)的介绍到此结束。更多相关 Svelte 和 React 比较内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

作者最新文章
相关文章
更多