Astro 本身是沒有共享全局變數的功能,不過文件中建議使用 nanostores 這個套件,可以在純 JS 和各種前端框架中使用,以及支援 TypeScript,且使用上也很簡單。
在 Astro 中我對 nanostores 使用方式的理解是,因為 Astro 預設每個頁面都會重新初始化,因此狀態管理就可以非常簡單,只需管理跨元件的共用狀態,不用管切換路由後如何清理資料。
這篇文章記錄了一下我第一次用 nanostores 做的範例,是訊息提示元件的狀態管理,可以呼叫顯示訊息提示的函數,並在 2 秒後自動消失。
安裝 nanostores
yarn add nanostores
實作成功訊息功能
首先先定義一個 messageStateStore
在 src/stores/message.ts
中,messageStateStore
保存了顯示訊息類型和訊息內容,然後寫一個 showMessage()
函數來設定呼叫時要顯示的類型和內容,並在 2 秒後會自己消失:
import { atom } from 'nanostores'
export type MessageType = 'success' | 'info'
export const messageStateStore = atom({
type: null as MessageType | null,
content: '',
})
export function showMessage(type: MessageType, content: string) {
messageStateStore.set({ type, content })
setTimeout(() => {
messageStateStore.set({ type: null, content: '' })
}, 2000)
}
然後需要增加一個 src/components/MessagesContainer.astro
用來顯示訊息提示,會監聽剛才的 messageStateStore
更新訊息內容,有內容存在就顯示出來:
<div class="messages-container"></div>
<script>
import { messageStateStore } from '@/stores/message'
messageStateStore.subscribe(value => {
const messagesContainer = document.querySelector('.messages-container') as HTMLElement
if (value.type) {
messagesContainer.innerHTML = `
<div class="message message-${value.type}">
${value.content}
</div>
`
} else {
messagesContainer.innerHTML = ''
}
})
</script>
這樣一個簡易的訊息提示功能就完成了~ 現在用一個複製連結按鈕當範例,用法就是綁一個點擊事件,然後呼叫 showMessage()
函數即可:
<script>
import { showMessage } from '@/stores/message'
const btnCopy = document.querySelector('.btn-copy') as HTMLButtonElement
btnCopy.addEventListener('click', () => {
// 複製連結...
showMessage('success', '連結已複製')
})
</script>
只是現在如果短時間內觸發多次的話,舊的 setTimeout()
會干擾到新的狀態呈現,這邊我們可以加一個 timer
,在設定 setTimeout()
之前先清除掉舊的 timer
:
let timer = null as ReturnType<typeof setTimeout> | null
export function showMessage(type: MessageType, content: string) {
messageStateStore.set({ type, content })
if (typeof timer === 'number') {
clearTimeout(timer)
}
timer = setTimeout(() => {
messageStateStore.set({ type: null, content: '' })
}, 2000)
}