本文深入对比 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 比较内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!