欢送有趣味的小伙伴,一起做点有意义的事!

我发动了一个周刊翻译打算,仓库地址,拜访地址

当初还很缺气味相投的小伙伴,纯属个人兴趣,当然对于晋升英语和前端技能也会有帮忙,要求:英语不要差的离谱、github纯熟应用、有恒心、虚心、对本人做的事负责。

想参加的小伙伴,能够wx私信,也能够给仓库发issue留言,我博客也有具体的集体联系方式:daodaolee.cn

前言

Pomodone:一个小型工夫跟踪应用程序,基于以 25 分钟为距离工作的番茄工作法。它有一个 25 分钟的计时器(在 Web Worker 中运行)并将“poms”的历史记录保留到一个小型 Firebase 数据库中,传送门

二者的用法

React的钩子是一个很好的封装,我创立了一个 useCurrentUser 钩子,让它侦听身份验证的变动并相应地设置一些状态。而后,当留神到身份验证变动时,我就能够确信 React 会按需从新渲染。

export const useCurrentUser = () => {  const [currentUser, setCurrentUser] = useState(undefined)  useEffect(() => {    return firebase.auth().onAuthStateChanged((details) => {      setCurrentUser(        details          ? {              displayName: details.displayName,              provider: {                'google.com': 'Google',                '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: {          'google.com': 'Google',          'github.com': 'GitHub',        }[details.providerData[0].providerId],        uid: details.uid,      })    } else {      currentUser.set(null)    }  })}

在高级 Svelte 组件中,能够在 onMount 中调用它,它会在组件装置的时候运行一次(该办法会被返回,所以能够在移除组件的时候勾销订阅,就像 useEffect 返回一个函数一样)

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

当初,在 Svelte 代码里的任何地位,组件都能够导入 currentUser 可写store。我比拟在意的是,currentUser 不是一个值,而是一个store,因而有相对的状态控制权。当然也能够订阅它并手动批改状态的扭转:

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

或者,如果只想读取最新的值,能够加一个 $ 前缀:

console.log($currentUser)

以上就是 Svelte 的一些语法技巧,$ 符号会主动订阅store里最新的值,它的益处就是 Svelte 不会每次须要读取最新的值的时候都得订阅一次。

简略来说,两个库仿佛都采纳了相似的办法:都容许订阅一个 Firebase 监听器(上文的demo),并在状态发生变化的时候更新数据。

应用 Worker

我设计的 Pomodone) 必须放弃尽可能精确的 25 分钟的计时器运行。如果浏览器选项卡在后盾(例如,不是焦点选项卡),大多数浏览器将升高其 setTimeout 调用的优先级,而不是严格按工夫运行它们。大多数时候在网络上这不是什么大问题,然而当用户通过您的利用跟踪 25 分钟的工作时,问题就呈现了!此外,在 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])

接下来,当用户点击 Start 按钮的时候,就会给 worker 发送一个音讯:

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

如果用 Svelte 写的话,代码很相似,不过有两个中央会更简便:

  1. 不用将 worker 保留在 userRef 上,只须要调配一个变量就好
  2. 能够将事件监听封装到函数里,该函数也不会成为 userEffect 的依赖 — 也就是封装到 userCallback 里。
let workeronMount(() => {  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...  }}

条件渲染

我喜爱 React 的局部起因是 在模板语法中嵌入JS:

// React<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 的模板语法也很好用:

// Svelte<ul class="poms-list">  {#each currentDayPoms as value, index}    <li      title={`Finished at ${format(        new Date(value.timeFinished),        'H:mm:ss'      )}`}    >      {index + 1}    </li>  {/each}</ul>

在渲染组件时,Svelte 的语法也很简便,很像JSX:

// Svelte<History pomodoros={pomodoros} />  // Svelte(collapsed props)<History {pomodoros}/>

Svelte中的响应式$

React 要求应用 userEffect 和 其余钩子管制代码运行,并在从新渲染组件时从新运行代码。Svelte 不同之处在于,默认状况下,大部分代码只会运行一次。当然,当有大量数据的节点渲染夹杂函数运行的时候,React 的从新渲染也有它的长处。比方这样:

const input = props.inputDataconst transformed = input.map((item) => transformItem(item))return <div>{JSON.stringify(transformed, null, 2)}</div>

如果用户提供了新的 props.inputData,组件将从新渲染更新,拿到的值也是新的。而在 Svelte 中并非如此:

<script>export let input;const transformed = input.map((item) => transformItem(item))</script><div>{JSON.stringify(transformed, null, 2)}</div>

解决响应式问题,要么应用 $ 符号,要么将转换逻辑加到模板里:

// 应用 $<script>export let input;$: transformed = input.map((item) => transformItem(item))</script><div>{JSON.stringify(transformed, null, 2)}</div>// 应用模板<script>export let input;</script><div>{JSON.stringify(input.map((item => transformItem(item))), null, 2)}</div>

组合式组件

组合式组件在组件级别框架里特地重要,React 的 children 属性简化了渲染:

function Box(props) {  return <div>{props.children}</div>}function App() {  return (    <Box>      <p>hello world!</p>    </Box>  )}

Svelte 里用插槽:

<!-- Box component --><div class="box">  <slot></slot></div><!-- App component --><Box>  <p>hello world!</p></Box>

当波及到多个子组件的时候,二者会有不同的解决形式:

  • React 会有多个Props:

    function Box(props) {  return (    <div>      <div class="left">{props.left}</div>      <div class="right">{props.right}</div>    </div>  )}function App() {  return <Box left={<p>hello</p>} right={<p>world!</P>} />}
  • 会显示命名多个插槽:

    <!-- Box component --><div class="box">  <slot name="left"></slot>  <slot name="right"></slot></div><!-- App component --><Box>  <p slot="left">hello</p>  <p slot="right">world!</p></Box>

类名判断

Svelte 有一个小性能,能够有条件地赋值 class:

<div class:is-active={isActive}>

事件监听

Svelte 蕴含了一些用来绑定事件的修饰符:

<script>function click() {  // No need to preventDefault ourselves  // logic here}</script><button on:click|preventDefault={click}>  Click me!</button>

相干链接

原文链接

翻译打算原文