雖然 Vue 透過模板幫助我們處理了大部分的DOM 操作
,但在某些情況下,仍然需要手動操作 DOM。例如,在使用第三方套件(如 Bootstrap 的 Modal)時,或者需要調用像video.play()
和video.pause()
這類的原生 Web API
時,我們必須直接與 DOM 元素進行交互。此時,可以使用ref
屬性為DOM 元素設置標記
,以便方便地引用並手動操作這些元素。
觀察Bootstrap
的Modal 組件
可以發現,若要使用 JavaScript 來控制 Modal,需要先創建一個 Bootstrap Modal 的實例,並將對應的 Modal 元素 ID 傳遞給這個實例。完成這一步後,就可以透過 modal.show()
或modal.hide()
這樣的方法來控制 Modal 的開啟和關閉。
原生的 Boostrap Modal 使用流程說明:
Boostrap
實例綁定對應顯示的 Modal DOM 元素。show()
和hide()
方法。👉 Boostrap Modal 純 javaScript 寫法實作連結
HTML:
<div class="modal" tabindex="-1" role="dialog" id="modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button id="saveButton" type="button" class="btn btn-primary">Save changes</button>
<button id="closeButton" type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
javascript:
const myModal = new bootstrap.Modal(document.getElementById("modal"));
// 開啟 Modal 按鈕
document.querySelector("#openModal").addEventListener("click", function () {
myModal.show();
});
// Modal 儲存按鈕
document.querySelector("#saveButton").addEventListener("click", function () {
myModal.hide();
});
// Modal 關閉按鈕
document.querySelector("#closeButton").addEventListener("click", function () {
myModal.hide();
});
接著我們將上面的控制方式改成使用 Vue 進行管控。
👉 Vue3 Options API Bootstrap Modal ref 實作連結
document.getElementById("modal")
的部分取得的 DOM 元素,改成透過ref
的方式取得。v-on
綁定click
事件,點擊的時候觸發對應的方法show()
和hide()
方法。Vue Template:
<div id="app">
<div class="p-4">
<button @click="openModal" id="openModal" type="button" class="btn btn-primary">打開 modal</button>
<div class="modal" tabindex="-1" role="dialog" ref="modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button @click="closeModal" id="saveButton" type="button" class="btn btn-primary">Save changes</button>
<button @click="closeModal" id="closeButton" type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
</div>
javascript:
const rootComponent = {
data() {
return {
myModal: null
};
},
methods: {
// 開啟 Modal
openModal() {
this.myModal.show();
},
// 關閉 Modal
closeModal() {
this.myModal.hide();
}
},
mounted() {
this.myModal = new bootstrap.Modal(this.$refs.modal);
}
};
⭐ 預設ref
對象會是undefined
如果需要讀取到其對應的DOM 元素需要在mouted
模板渲染完成之後的階段。
除了上述與第三方套件搭配的案例外,在使用像video
這類的原生 HTML 標籤時,也經常需要使用其內建的 Web API,例如:play()
和pause()
方法來控制播放行為。在這些情況下,可以利用 Vue 的ref
屬性來標記這些 DOM 元素,以便進行手動控制和操作。
👉 Vue3 Options API HTML video tag with ref 實作連結
Vue Template:
<video src="https://www.w3schools.com/html/mov_bbb.mp4" ref='video'></video>
<div class="btn-group" role="group" aria-label="Basic example">
<button @click="play" type="button" class="btn btn-secondary">play</button>
<button @click="pause" type="button" class="btn btn-secondary">pause</button>
<button @click="reset" type="button" class="btn btn-secondary">reset</button>
javascript:
const rootComponent = {
data() {
return {
videoInstance: null
};
},
methods: {
play() {
this.videoInstance.play();
},
pause() {
this.videoInstance.pause();
},
reset() {
this.videoInstance.pause();
this.videoInstance.currentTime = 0;
}
},
mounted() {
this.videoInstance = this.$refs.video;
}
};
在 Vue 中,當ref
與v-for 指令
一起使用時,Vue 會自動將生成的 DOM 元素存儲在一個陣列
中。可以透過this.$refs
訪問這些元素。
👉 Vue3 Options API v-for 搭配 ref 陣列實作連結
Vue Template:
<ul>
<li v-for="item in productList" :key="item" ref="items">{{ item }}</li>
</ul>
javascript:
const app = createApp({
data() {
return {
productList: ["Apple", "Orange", "Banana"]
};
}
mounted() {
console.log("this.items", this.$refs.items);
}
});
⭐ 無法保證綁定陣列中ref 屬性
的順序與原始陣列數據的順序一致
可以透過綁定一個動態 ref 屬性
,在每次模板更新時將該ref
傳入到一個函數中進行處理。如果綁定的元素被銷毀,傳入函數的參數值將會自動變為null
。
流程說明:
動態綁定的 ref 屬性
,自動將焦點聚焦到該輸入框,達到 focus() 效果。👉 Vue3 Options API ref 動態綁定方法(method)實作連結
Vue Template:
<div v-if="isShowAccount" class="input-group">
<label for="inputAccount">使用者帳號:</label>
<input id="inputAccount" type="text" :ref="setRef">
</div>
<button @click="register" type="button">切換顯示使用者帳號輸入框</button>
javascript:
methods: {
setRef(ref) {
// ref 是動態傳入的屬性
if (ref) {
this.$nextTick(() => {
ref.focus();
});
}
},
register() {
this.isShowAccount = !this.isShowAccount;
}
}
...略
當ref 屬性
被應用在 HTML 元素上時,它會標記並引用實際的HTML DOM 元素
;而當ref 屬性
被應用在子組件上時,ref 屬性
則會引用該子組件的實例
。透過這個ref 屬性
,可以直接訪問子組件的內部數據和方法。不過,過度使用ref 屬性
來操作子組件的資料和行為,可能會導致資料流混亂,需要謹慎使用。
流程說明:
👉 Vue3 Options API ref 取得組件實體內數據及方法實作連結
父組件 Vue Template:
<child-component ref="child"></child-component>
<p>取得子組件的響應式變數值:{{ RootCount }}</p>
<button @click="getChildCount" type="button">取得子組件的響應試變數值</button>
<button @click="addNum" type="button">使用子組件 addNum 增加子組件 childCount 變數值</button>
⭐ 【 取得子組件的響應試變數值 】按鈕可以取得子組件childCount
當前變數值。
⭐ 【 使用子組件 addNum 讓子組件 childCount 變數值 + 1 】按鈕可以取得子組件addNum
函數,並且將子組件 childCount 變數值 + 1。
父組件 javascript:
const rootComponent = {
data() {
return {
RootCount: 0
};
},
methods: {
// 取得子組件數據 childCount
getChildCount() {
this.RootCount = this.$refs.child.childCount;
},
// 使用子組件內的 addNum 方法
addNum() {
this.$refs.child.addNum();
}
},
components: {
childComponent
}
};
子組件 javascript:
const childComponent = {
template: "#childComponent",
data() {
return {
// 紀錄計數器當前數字
childCount: 0
};
},
methods: {
// 將變數 childCount + 1
addNum() {
this.childCount += 1;
}
}
};
如果希望限制子組件實例中哪些數據和方法可以被外部訪問,可以在子組件中使用expose 選項
,來控制外層透過ref 屬性
能夠讀取的範圍。
以上面的案例為例,可以使用expose
選項來限制只有addNum
方法能被外部通過 ref 屬性訪問,而子組件的數據childCount
則無法被外部讀取。
當呼叫根組件getChildCount
方法試圖取得子組件childCount
參數的時候,會回傳undefined
。
👉 Vue3 Options API ref 取得組件實體內數據及方法(搭配 expose 限制)實作連結
javascript:
const childComponent = {
expose: ["addNum"],
template: "#childComponent",
data() {
return {
// 紀錄計數器當前數字
childCount: 0
};
},
methods: {
// 將變數 childCount + 1
addNum() {
this.childCount += 1;
}
}
};
HTML 元素
進行操作時,可以使用 Vue 的ref 屬性
來標記對應的HTML DOM 元素
,方便與第三方套件或 Web API 結合使用。v-for
生成多個元素時,可以使用ref 屬性
來將這些元素存儲為一個陣列
,便於後續進行批次操作。函
數的方式動態綁定 ref
,在需要時對原生 HTML 元素進行操作。當綁定的元素或組件被銷毀時,函數中的ref 參數
會自動變為null
。ref 綁定子組件實例
,直接訪問子組件內部的變數或調用其方法。需要注意的是,這種操作可能會導致資料流的混亂,因此應謹慎使用。ref 屬性
訪問內部數據和方法的範圍,可以使用expose 參數
來定義哪些數據和方法可以被外部訪問。