05 使用 $refs 訪問子組件引用
目標
在之前的文章中,詳細闡述了子組件獲取父組件所提供屬性及方法的一些解決方案,如果我們想在父組件之中訪問子組件的一些方法和屬性怎麼辦呢?設想以下一個場景:
- 當前的 custom-button 組件中,有一個 input 元素
- 我們期望當 toggle 的開關狀態爲開時,顯示 input 元素並自動獲得焦點
這裏要想完成目標,需要獲取某個組件或者每個元素的引用,在不同的 mvvm 框架中,都提供了相關特性來完成這一點:
- angularjs: 可以使用依賴注入的 $element 服務
- Angular: 可以使用 ViewChild、ContentChild 或者 template ref 來獲取引用
- react: 使用
ref
屬性聲明獲取引用的邏輯
在 vue 中,獲取引用的方法與 react 類似,通過聲明 ref
屬性來完成。
實現
首先,在 custom-button
組件中增加一個 input 元素,如下:
<input v-if="on" ref="input" type="text" placeholder="addtional messages">
注意這裏的 ref="input"
,這樣在組件內部,可以通過 this.$refs.input
獲得該元素的引用,爲了實現目標中提及的需求,再添加一個新的方法 focus
來使 input 元素獲取焦點,如下:
focus() {
this.$nextTick(function() {
this.$refs.input.focus();
});
},
注意這裏的 this.$nextTick
,正常情況下,直接調用 input 的 focus
方法是沒有問題的,然而卻不行。因爲 input 的渲染邏輯取決於 prop 屬性 on 的狀態,如果直接調用 focus
方法,這時 input 元素的渲染工作很可能還未結束,這時 this.$refs.input
所指向的引用值爲 undefined
,繼續調用方法則會拋出異常,因此我們利用 this.$nextTick
方法,將調用的邏輯延遲至下次 DOM 更新循環之後執行。
同理,在 app
組件中,爲 custom-button
添加一個 ref
屬性,如下:
<custom-button ref="customButton" :on="status.on" :toggle="toggle"></custom-button>
之後修改 onToggle
方法中的邏輯以滿足目標中的需求,當 toggle
組件狀態爲開時,調用 custom-button
組件的 focus
方法,如下:
onToggle(on) {
if (on) this.$refs.customButton.focus();
console.log("toggle", on);
}
成果
點擊按鈕會發現,每當開關爲開時,input 元素都會顯示,並會自動獲得焦點。
你可以下面的鏈接來看看這個組件的實現代碼以及演示:
總結
文章中所舉例子的交互,在實際場景中很常見,比如:
- 當通過一個 icon 觸發搜索框時,期望自動獲得焦點
- 當表單校驗失敗時,期望自動獲得發生錯誤的表單項的焦點
- 當複雜列表的篩選器展開時,期望第一個篩選單元獲得焦點
這幾種情況下,都可以使用該模式來高效地解決問題,而不是通過使用 DOM 中的 api 或者引入 jquery 獲取相關元素再進行操作。