這是〈Svelte 從小白到入門〉系列的第十三集,各集連結如下:
- 〈Svelte 從小白到入門(一)元件、事件、Reactivity〉
- 〈Svelte 從小白到入門(二)Props、邏輯、迴圈、Await〉
- 〈Svelte 從小白到入門(三)事件〉
- 〈Svelte 從小白到入門(四)數據綁定〉
- 〈Svelte 從小白到入門(五)生命週期〉
- 〈Svelte 從小白到入門(六)Store〉
- 〈Svelte 從小白到入門(七)動態效果〉
- 〈Svelte 從小白到入門(八)進出場效果〉
- 〈Svelte 從小白到入門(九)動畫效果〉
- 〈Svelte 從小白到入門(十)Action〉
- 〈Svelte 從小白到入門(十一)CSS Class〉
- 〈Svelte 從小白到入門(十二)Slot〉
- 〈Svelte 從小白到入門(十三)Context〉
- 〈Svelte 從小白到入門(十四)Svelte 的特殊元素〉
- 〈Svelte 從小白到入門(十五)Module Context〉
- 〈Svelte 從小白到入門(十六)Debugging〉
- 〈Svelte 從小白到入門(十七)結束與展望〉
Context API 是 Svelte 中一種元件間交換資料的機制,當一個父元件以 setContext(key, context)
設定一組 context,那麼它的子元件,以及子元件的子元件,都可以透過 getContext(key)
來取得那組 context。
在下面的地圖範例中,我們有 Map 元件,以及子元件 MapMarker。
Map 元件的主要成員是 map
,它是 mapbox.Map()
構成的地圖物件,在範例中,我們用 setContext()
把 getMap
成為一組 context,使子元件們(MapMarker)也得以取得 map
這個地圖物件:
在 Map 元件中,注意到 map = new mapbox.Map()
這句生成地圖物件的敘述是放在 onMount()
內,onMount()
在第五集生命週期有出現過,onMount()
指的是當 Svelte 元件在被瀏覽器生成之時,而地圖物件的生成也必須是它的容器元素已經被瀏覽器建立之後,也就是 Map 元件內的那個 <div>
必須先存在,Mapbox 才有可能以它為容器,建構出地圖物件本身,所以這些構成地圖物件的程式碼才得放在 onMount()
裡面。
並且需要注意的是,setContext()
得在元件初始化時調用,但此時 map
僅是 undefined
,尚未生成地圖物件,因此我們並不直接在 context 中置入 map
,而是用 getMap()
函式來處理:
setContext(key, {
getMap: () => map
});
因此在 MapMarker 中,我們透過 getContext()
得到的並非是 map
真身,而是可以取得 map
的函式 getMap()
,如此迂迂迴迴 只為了求你點個讚 讓我們得以把元件細節都封裝在元件內,使最外層的 App 元件看起來是如此的清晰而且語意明確:
<Map lat={35} lon={-100} zoom={2}>
<MapMarker lat={37.8225} lon={-122.0024} label="Svelte Body Shaping"/>
<MapMarker lat={29.7230} lon={-95.4189} label="Svelte Waxing Studio"/>
</Map>
Context 的 Key
在上面的範例中,我們再做 context 時用的 key
只是一個空物件 {}
,在 Svelte 的約定裡,可以用任意物件當 key
,只要自己確保沒有與其他物件搞混就好,所以 Svelte 推薦我們用物件做為 key
,因為在 JavaScript 的設計上,兩個一樣的空物件是參照到不同的記憶體位址,即 {} !== {}
。
當然,如果有兩個 context,key
也必須是各自獨立的。
Context 與 Store
同樣做為跨元件的資料交換機制,第六集的 store 與本集的 context 有著以下差異:
- Store 是全域的,任何元件都可以使用,而 context 是一個元件及它的子代元件才可使用
- 承上,當一個元件有 A、B 兩個實體時(被調用兩次),他們各自的 context 是獨立的,不互相影響,他們的子元件調用到的 context 當然也是獨立的,A 的子元件吃到的 context 就是來自 A,不會進錯家門上錯床吃到 B 的 context
- Store 有觀察者模式的特性,元件可以 subscribe 一個 store 的即時變化,而 context 不具有這樣的特性
- 承上,透過 Store 的 subscribe 機制,我們可以說 store 是 reactivity 的,而 context 是沒有 reactivity 的特性的
基於上述的最後一點,如果某個 context 的內容會改變,而且我們希望它的改變要反映在其他地方,那可以把 store 與 context 合併運用:
const { these, are, stores } = getContext(...);
如此一來,這些 store 的值來自 getContext(...)
,並且也有 reactivity 惹!
小結
本集重點整理:
- Context 是跨元件的資料交換機制,但僅限父元件與它的子代元件間做交換
- 用
setContext(key, content)
設定 context、用getContext(key)
取得 context setContext()
得在元件的初始化階段調用- 關於 context 與 store 的差異請看上一節