Svelte 響應式資料之$
tags: Svelte
category: Front-End
description: Svelte 響應式資料之$
created_at: 2023/04/02 19:00:00
前言
這邊我覺得比較像是語法糖。
在 Vue
中比較像 watchEffect()
或是你要說像 watch()
、 computed()
也可以,因為有重疊的部分,下面會提到。
在 React
比較像 useEffect()
,但又不是一模一樣。
然後上一篇講太多太乾枯乏味,這一篇輕鬆一點。
而且因為 $
能講的也不多,所以乾脆就來個與 Vue
、 React
的比較。
用法 1
畫面上呈現
firstName = John
lastName = Doe
fullName = John Doe
Svelte code
<script>
let firstName = 'John';
let lastName = 'Doe';
$: fullName = `${firstName} ${lastName}`;
</script>
<div>
<p>firstName = {firstName}</p>
<p>lastName = {lastName}</p>
<p>fullName = {fullName}</p>
</div>
Vue code
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
</script>
<template>
<div>
<p>firstName = {{ firstName }}</p>
<p>lastName = {{ lastName }}</p>
<p>fullName = {{ fullName }}</p>
</div>
</template>
React code
function App() {
const [firstName, setFirstName] = useState('John')
const [lastName, setLastName] = useState('Doe')
const fullName = `${firstName} ${lastName}`
return (
<div>
<p>firstName = {firstName}</p>
<p>lastName = {lastName}</p>
<p>fullName = {fullName}</p>
</div>
)
}
可能有人會有疑問的是 React
的部分,可能會有人寫成這樣
const [fullName, setFullName] = useState(`${firstName} ${lastName}`)
甚至是這樣
useEffect(() => {
setFullName(`${firstName} ${lastName}`)
}, [firstName, lastName])
千萬不要這樣做,你這樣只會無故造成多餘的 re-render
。
在這邊,當 firstName
或 lastName
有更新的時候,會觸發一次 re-render
,而這時 fullName
自然就會被運算成新的值,所以並不需要寫那麼複雜。
用法 2
監聽 count
,每隔一秒幫我把 count += 1
,且當他變化的時候幫我 console.log
Svelte code
<script>
let count = 0;
$: console.log('count changed', count)
setInterval(() => {
count++;
}, 1000);
</script>
Vue code
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (val) => {
console.log('count changed', val)
})
setInterval(() => {
count.value++
}, 1000)
</script>
React code
function App() {
const [count, setCount ] = useState(0)
useEffect(() => {
console.log('count changed', count)
}, [count])
setTimeout(() => {
setCount(count + 1)
}, 1000)
return <></>
}
可能有人會有疑問的還是 React
的部分,就你毛最多。
為什麼不是使用 setInterval
?,你用啊,直接用啊,保證發生災難。
用的好也是可以用啦,只是你要多寫一些東西,例如下面這樣,很沒必要:
useEffect(() => {
console.log('count changed', count)
const timer = setInterval(() => {
setCount(count + 1)
}, 1000)
return () => {
clearInterval(timer)
}
}, [count])
用法 3
監聽 count
,我不希望 count
的值超過 5
,最多就到 5
,且在 console
呈現警告訊息。
Svelte code
<script>
let count = 0;
$: if (count > 5) {
console.warn('count is dangerously high!');
count = 5;
}
setInterval(() => {
count++;
}, 1000);
</script>
<div>{count}</div>
Vue code
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (val) => {
if (val > 5) {
console.warn('count is dangerously high!')
count.value = 5
}
})
setInterval(() => {
count.value++
}, 1000)
</script>
<template>
<div>{{ count }}</div>
</template>
React code
function App() {
const [count, setCount ] = useState(0)
useEffect(() => {
if (count > 5) {
console.warn('count is dangerously high!')
setCount(5)
return
}
const timer = setInterval(() => {
setCount(count + 1)
}, 1000)
return () => {
clearInterval(timer)
}
}, [count])
return (
<div>
{count}
</div>
)
}
這次 React
總算沒什麼毛了,主要就是宣告計時器的方式要對,免得他在一次 re-render
跑多次。
但總之就是想帶到 Svelte
可以監聽一個區塊內使用到的東西。
用法3 - 延伸範例
當變數變化,去改網站標題
Svelte code
<script>
let title = 'Hello World';
$: {
if (typeof document !== 'undefined') {
document.title = title;
}
}
setTimeout(() => {
title = '1 second later';
}, 1000);
</script>
Vue code
<script setup>
import { ref, watchEffect } from 'vue'
const title = ref('Hello World')
watchEffect(() => {
document.title = title.value
})
setTimeout(() => {
title.value = '1 second later'
}, 1000)
</script>
React code
function App() {
const [title, setTitle] = useState('Hello World')
useEffect(() => {
document.title = title
}, [title])
useEffect(() => {
setTimeout(() => {
setTitle('1 second later')
}, 1000)
}, [])
return <></>
}
$ 也是批次更新
如同上一篇所提到的批次更新,$
也是一樣。
所以有可能會遇到下面的情況
<script>
let count = 0;
$: doubleCount = count * 2;
const update = () => {
count += 1;
console.log(count, doubleCount);
};
</script>
<div>count: {count}</div>
<div>doubleCount: {doubleCount}</div>
<button on:click={update}>count++</button>
畫面上呈現是對的,但是 console
是錯的。
解決辦法,拉出去:
let count = 0;
$: doubleCount = count * 2;
const update = () => {
count += 1;
};
$: console.log(count, doubleCount);
總結
總之就是監聽變數變化之後要幹嘛,就用 $
這個東西,用法也很簡單,相信可以從上面範例看的出來。(只是要記得注意他也是批次更新)