svelte中對(duì)于組件內(nèi)的狀態(tài)管理比較簡(jiǎn)單,但是如何像其它框架一樣進(jìn)行跨組件間交互呢?
利用props進(jìn)行狀態(tài)傳遞
我們首先通過(guò)官網(wǎng)APi可以了解到和其它框架一樣的傳遞策略,它們使用props傳遞狀態(tài),從而進(jìn)行交互。
當(dāng)一個(gè)組件需要與另一個(gè)組件共享數(shù)據(jù)時(shí),可以在組件樹中將狀態(tài)上移,直到這些組件有了一個(gè)公共父級(jí)。
需要向下傳遞狀態(tài),直到到達(dá)需要此狀態(tài)信息的所有組件為止。
這是使用props完成的,并且我認(rèn)為這是一種最好的技術(shù),因?yàn)樗芎?jiǎn)單。
Context API
在某些情況下props不實(shí)用。 例如在組件樹中,兩個(gè)組件之間的距離相當(dāng)遠(yuǎn)遙,以至于我們不得不將狀態(tài)提升到頂級(jí)組件。
這種情況下可以使用另一種技,它稱為Context,當(dāng)您希望讓多個(gè)組件與后代進(jìn)行通信,但又不希望到處傳遞props時(shí),這是一種很好的方法。
在Context中設(shè)置對(duì)象,并將其與鍵關(guān)聯(lián):
<script>
import { setContext } from 'svelte'
const someObject = {}
setContext('someKey', someObject)
</script>
在另一個(gè)組件中,可以使用getContext檢索分配給鍵的對(duì)象
<script>
import { getContext } from 'svelte'
const someObject = getContext('someKey')
</script>
注意:您只能在使用setContext的組件中或者它的后代中使用getContext來(lái)檢索數(shù)據(jù)。
如果您想讓位于2個(gè)不同組件樹中的兩個(gè)組件進(jìn)行通信,那么還有另一種方式供我們使用:stores。
Svelte stores
當(dāng)組件需要相互通信而不需要傳遞太多的props時(shí),Svelte stores是處理應(yīng)用程序狀態(tài)的好方式。
首先我們需要導(dǎo)入從svelte/store導(dǎo)入writable:
import { writable } from 'svelte/store'
使用writable()函數(shù)創(chuàng)建存儲(chǔ)變量,并將默認(rèn)值作為第一個(gè)參數(shù)傳遞.
const username = writable('Guest')
它可以放在一個(gè)單獨(dú)的文件中,您可以將其導(dǎo)入多個(gè)組件,例如store.js。
import { writable } from 'svelte/store'
export const username = writable('Guest')
現(xiàn)在加載此文件的任何其他組件都可以訪問(wèn)存儲(chǔ)
<script>
import { username } from './store.js'
</script>
現(xiàn)在,可以使用set()將這個(gè)變量的值設(shè)置為一個(gè)新值,并將新值作為第一個(gè)參數(shù)傳遞
username.set('new username')
它可以使用update()函數(shù)進(jìn)行更新,這與set()不同,因?yàn)槟恢皇菍⑿轮祩鬟f給它—而是運(yùn)行一個(gè)將當(dāng)前值作為參數(shù)傳遞的回調(diào)函數(shù)。
const newUsername = 'new username!'
username.update(existing => newUsername)
你可以在這里添加更多的邏輯
username.update(existing => {
console.log(`Updating username from ${existing} to ${newUsername}`)
return newUsername
})
如果想要獲取一次store的值,我們可以使用從svelte/store導(dǎo)入的get()方法。
import { readable, get } from 'svelte/store'
export const username = writable('Guest')
get(username) //'Guest'
相反,要?jiǎng)?chuàng)建一個(gè)響應(yīng)性的變量(每當(dāng)它更改時(shí)就更新),可以使用username)預(yù)先設(shè)置store變量。使用它將使組件在存儲(chǔ)值發(fā)生更改時(shí)重新運(yùn)行。
Svelte把$作為一個(gè)保留值,將阻止您將它用于與存儲(chǔ)值無(wú)關(guān)的內(nèi)容(這可能會(huì)導(dǎo)致混淆),因此,如果您使用$來(lái)預(yù)先當(dāng)做DOM引用,那么不要在Svelte中這樣做。另一種選擇是使用username的subscribe()方法,如果需要在變量更改時(shí)執(zhí)行某些邏輯,這是最適合的方式。
username.subscribe(newValue => {
console.log(newValue)
})
除了writable stores之外,Svelte還提供了兩種特殊類型的存儲(chǔ):readable stores 和 derived stores.
Svelte Readable Stores
Readable stores之所以特別,是因?yàn)樗鼈儫o(wú)法從外部進(jìn)行更新-沒(méi)有set()或update()方法。 相反,一旦設(shè)置了初始狀態(tài),便無(wú)法從外部進(jìn)行修改。
官方的Svelte文檔展示了一個(gè)有趣的使用計(jì)時(shí)器更新日期的例子。我可以考慮設(shè)置一個(gè)計(jì)時(shí)器來(lái)從網(wǎng)絡(luò)獲取資源、執(zhí)行API調(diào)用、從文件系統(tǒng)獲取數(shù)據(jù)(使用本地Node.js服務(wù)器)或其他任何可以自主設(shè)置的東西。
在本例中,我們沒(méi)有使用writable()來(lái)初始化存儲(chǔ)變量,而是使用了readable().
import { readable } from 'svelte/store'
export const count = readable(0)
您可以在默認(rèn)值之后提供一個(gè)函數(shù),該函數(shù)將負(fù)責(zé)更新它。這個(gè)函數(shù)接收set函數(shù)來(lái)修改值
<script>
import { readable } from 'svelte/store'
export const count = readable(0, set => {
setTimeout(() => {
set(1)
}, 1000)
})
</script>
在本例中,我們?cè)?秒后將值從0更新為1。
你也可以在這個(gè)函數(shù)中設(shè)置一個(gè)區(qū)間:
import { readable, get } from 'svelte/store'
export const count = readable(0, set => {
setInterval(() => {
set(get(count) + 1)
}, 1000)
})
您可以在另一個(gè)類似的組件中使用它:
<script>
import { count } from './store.js'
</script>
{$count}
Svelte Derived Stores
derived store允許您創(chuàng)建依賴于現(xiàn)有存儲(chǔ)的值的新存儲(chǔ)值。
您可以使用svelte/store導(dǎo)出的derived store來(lái)實(shí)現(xiàn)此目的,它以現(xiàn)有存儲(chǔ)值作為第一個(gè)參數(shù),以函數(shù)作為第二參數(shù),該函數(shù)接收該存儲(chǔ)值作為其第一個(gè)參數(shù):
import { writable, derived } from 'svelte/store'
export const username = writable('Guest')
export const welcomeMessage = derived(username, $username => {
return `Welcome ${$username}`
})
<script>
import { username, welcomeMessage } from './store.js'
</script>
{$username}
{$welcomeMessage}