Ref操作Dom為何既易用又有效率?以下這篇文章帶大家聊聊Ref操作,介紹一下Ref取得Dom的本質、其在Vue2.x和Vue3.x的不同等,希望對大家有所幫助!
在開發一個專案之前,我們往往都是先做下需求分析,針對前端而言,就是可以研究或選擇一個基礎元件庫,來提升我們的工作效率。畢竟,對於比較計較時間成本的公司來說,不會給你拿看電視劇玩遊戲的時間去專門開發一個類似日曆的組件。但在市面上的元件庫,並不能都能滿足我們的需求。這時候,我們需要自己手寫元件,來應用在專案中。
而這就是我想說的: 如何設計元件,讓其既能易於應用(或者說減少程式碼量),又能提高擴展性,方便需求變更和後續維護?
可以有很多種方式,而利用Ref操作Dom的特性隨是其中之一,但這個方式卻讓我們在維護和操作Modal、Popup以及頻繁操作Dom顯示和隱藏互動的組件的時候,卻發揮得很大優勢。 【相關推薦:vuejs影片教學、web前端開發】
#就對Ref操作Dom的相關知識點以及應用實例分幾個面向來做下剖析
- Ref取得Dom的本質
- Ref操作Dom在Vue2.x和Vue3.x的不同
- Ref操作元件Dom和父子元件單向傳遞對比
詳說
Ref取得Dom的本質
Vue2.x中Vue的對象屬性$refs,其實就是所有註冊過的ref的一個集合,而ref對應著template模版中,不同組件或普通Dom元素上關聯的ref="xx"; 源碼中ref的實際獲取方式也是通過原生方式getElementById而得到的Dom節點;可以說ref是document.getElementById
的語法糖。 vue3的ref延續了vue2的用法,也增加了一個作用就是創建響應式資料
也許有人會問了,既然ref和getElementById都能取得到Dom,那麼在項目開發中,我選擇哪一種方式都沒什麼差別呢?
關於這個問題,經過數據表明,$refs相對document.getElementById的方法,會減少獲取dom節點的消耗;而具體原因,等下一篇文章再詳細探討。
Ref運算Dom在Vue2.x和Vue3.x的不同
##Vue2.x
#我們只需要在對應的Dom元素或元件加上ref="xx"屬性,然後在Vue物件中使用this.$refs.xx,就可以直接取得到該Dom並操作其方法屬性,
<user-and-dep-tree-select-modal
ref="avaUserTreeSelect"
title="選擇可見範圍"
:project-id="currentProjectId"
:visible.sync="avaUserModalVisible"
@ok="editAvailableUser"
/>
或者
<div class="user" ref="user">dd</div>
// $refs
showManagerModal () {
this.$refs.avaUserTreeSelect.showModal(this.form.managers)
console.log(this.$refs.user.text)
},
Vue3.2
在Vue3.2版本使用的方式
//普通Dom
<div class="user" ref="user"></div>
//组件
<batch-adjust-department-modal ref="batchAdjustDepartmentRef" />
<script setup>
import { ref } from 'vue';
// modal调整部门弹层Dom
const batchAdjustDepartmentRef = ref(null);
const user = ref(null);
</script>
也許這裡有人疑問,為什麼宣告了一個和template的ref中同名的常數變數綁定了對應的dom?
這裡再補充說明:
Vue3 在早期版本( 3.0.0-beta.21 之前)中對composition api 的支持,只能在元件選項- setup
函數中使用。而對應式的變數都是透過在setup()方法中return {寫入需要在模版中使用的變數或方法}
<script>
import { defineComponent, ref } from 'vue'
export default defineComponent({
name: 'HelloWorld',
setup(props, ctx) {
const count = ref(0)
function add() {
count.value++
}
// 使用return {} 把变量、方法暴露给模板
return {
count,
add,
}
},
})
</script>
在3.0.0-beta.21 版本中增加了- 5101c0cdbdc49998c642c71f6b6410a8
的實驗特性。如果使用了,會提示
5101c0cdbdc49998c642c71f6b6410a8 還處在實驗特性階段。
在3.2.0 版本中移除- 5101c0cdbdc49998c642c71f6b6410a8
的實驗狀態,從此,宣告
5101c0cdbdc49998c642c71f6b6410a8 正式轉正使用,成為框架穩定的特性之一
與元件選項
setup 函數對比,
5101c0cdbdc49998c642c71f6b6410a8 我們只需要寫更少、更簡潔的程式碼,不需要使用
return {} 暴露變量和方法了,使用元件時不需要主動註冊了,會自動幫你綁定
所以在
5101c0cdbdc49998c642c71f6b6410a8中宣告的變數會自動被加到在該Vue物件的本身this中,如
5101c0cdbdc49998c642c71f6b6410a8
| ##3f1c4e4b6b16bbbd69b2ee476dc4f83a
|
const user = ref(null);
| this.$ref.user
|
Ref操作组件Dom和父子组件单向传递
props父传子,子通过emits传父,这样单方向传递,在控制弹层组件的显示和隐藏方面也可以实现,但是如此一来,我们就会像下面一样
父组件
<template>
<exchange-valid-modal-vue v-model:visible="visibleExchange" />
</template>
<script setup>
// 转让管理员组件
import ExchangeValidModalVue from './modal/ExchangeValidModal.vue';
// modal弹层
const visibleExchange = ref(false); // 转让管理员可视化
const onExchangeAdmin = () => {
visibleExchange.value = true;
};
</script>
子组件ExchangeValidModalVue.vue
<template>
<t-dialog
v-model:visible="visibleExchange"
header="转让主管理员"
attach="body"
width="384px"
:confirm-on-enter="true"
:on-close="onCloseExchange"
/>
</template>
<script setup>
const visibleExchange = ref(false);
const props = defineProps({
data: {
type: Object,
default: () => {},
},
visible: {
type: Boolean,
default: false,
},
});
watch(
() => props.visible,
(cur, pre) => {
visibleExchange.value = cur;
if (cur) {
firstTag.value = 1;
}
},
);
watch(
() => visibleExchange.value,
(cur, pre) => {
emits('update:visible', cur);
},
);
</script>
从代码里面我们就可以发现通过用父子组件单向传递的方式去实现一个组件的显示和隐藏功能,我们需要如此费劲地声明多个变量,还要做两次监听,万一后面不止一个这样的参数进行传递,那么代码量可想而知,而且也不易维护。
其实显示和隐藏的功能可以直接在内部中进行值的响应即可,并不需要在父级别中操作,如下将上面代码改变一下:
子组件ExchangeValidModalVue.vue
<template>
<t-dialog
v-model:visible="visible"
header="转让主管理员"
attach="body"
width="384px"
:confirm-on-enter="true"
:on-close="onCloseExchange"
/>
</template>
<script setup>
import { ref } from 'vue';
const visible = ref(false);
const emits = defineEmits(['call']);
const onEmitSelectSuperiod = () => { // 省略
emits('call');
};
const onOpen = () => {
visible.value = true;
};
const onClose = () => {
visible.value = false;
};
defineExpose({
onOpen,
onClose,
});
</script>
那么在父组件中,我们只需要通过ref得到该组件Dom,然后操作Dom内部的方法即可;
如:父组件改写
<template>
<exchange-valid-modal-vue ref="exchangeRef" />
</template>
<script setup>
// 转让管理员组件
import ExchangeValidModalVue from './modal/ExchangeValidModal.vue';
// modal弹层
const exchangeRef = ref(null); // 转让管理员可视化
const onExchangeOpen = () => {
exchangeRef.onOpen() // 直接操作dom里defineExpose暴露出来的方法
};
</script>
如此,是不是比父子单向数据传递的方式更加高效易用?当然上面所说的只是我举的一个例子,当后续需要在组件内扩展功能也可按类似的方法代替单向数据流的方式扩展
但,请注意;这种操作dom方式,并不是什么场景下都是最佳的选择;我们可以分情况选择,比如当一些数据只需要在子组件的范畴中实现,而不需要父组件外加干涉的情况下,选择ref操作dom更为高效;
补充知识点:
defineExpose
在 Vue3.2 中,默认不会暴露任何在 5101c0cdbdc49998c642c71f6b6410a8
中声明的绑定,即不能通过模板 ref
获取到组件实例声明的绑定。
Vue3.2 提供了 defineExpose
编译器宏,可以显式地暴露需要暴露的组件中声明的变量和方法
(学习视频分享:vuejs入门教程、编程基础视频)
以上是淺析Ref操作Dom在Vue2.x和Vue3.x的不同的詳細內容。更多資訊請關注PHP中文網其他相關文章!