比较 Svelte 和 React(一)

1,088 阅读5分钟

比较 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 中可以采用的方法并使用可写存储

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 侦听器并在身份验证状态更改时获得更新。

从使用者角度开发

Pomodone 是一个 25 分钟的计时器,并尽可能准确。如果浏览器选项卡处于后台(不是焦点选项卡),大多数浏览器将降低其 setTimeout 调用的优先级并且不会严格按时运行它们。大多数时候在网络上这不是什么大不了的事,但是当用户通过您的应用程序跟踪 25 分钟的工作时,它就有问题,在 25 分钟的过程中,任何轻微的时间漂移都会导致最终时间相差甚远。但是,如果将这些超时转移到 Web Worker 中,它们应该会按时运行,并且不会被浏览器取消优先级。

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

因为 React 组件在每次重新渲染时都会重新执行,所以很容易导致创建数千个 worker!必须使用 useRef 通过维护对您创建的工作程序的引用来避免此问题。

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

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 代码更易于阅读:

  1. 我们不必将 worker 保留在 useRef 中,只需将其分配给一个变量即可。
  2. 我们可以更轻松地将事件侦听器代码拉出到一个函数中,因为该函数不会成为 useEffect 的依赖项——此时我们必须将它包装在 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就像在跳圈。你不能创建一个工作实例,它必须进入 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 (collapsed props) 

<History {pomodoros}/>

未完待续