共计 4258 个字符,预计需要花费 11 分钟才能阅读完成。
缘起
在开发一个我的项目之前,咱们往往都是先做下需要剖析,针对前端而言,就是能够调研或者抉择一个根底组件库,来进步咱们的工作效率。毕竟,对比拟计较工夫老本的公司来说,不会给你拿看电视剧玩游戏的工夫去专门开发一个相似日历的组件。但在市面上的组件库,并不能都能满足咱们的需要。这个时候,咱们就须要本人手写组件,来利用到我的项目中。
而这就是我想说的: 如何设计组件,让其既能易于利用(或者说缩小代码量),又能进步扩展性,不便需要变更和后续保护?
能够有很多种形式,而利用 Ref 操作 Dom 的个性随是其中之一,但这个形式却让咱们在保护和操作 Modal、Popup 以及频繁操作 Dom 显示和暗藏交互的组件的时候,却施展得很大劣势。
就对 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 lang="ts">
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 版本中减少了
<script setup>
的试验个性。如果应用了,会提醒<script setup>
还处在试验个性阶段。 - 在 3.2.0 版本中移除
<script setup>
的试验状态,从此,宣告<script setup>
正式转正应用,成为框架稳固的个性之一
与组件选项setup
函数比照,<script setup>
咱们只须要写更少、更简洁的代码,不须要应用return {}
裸露变量和办法了,应用组件时不须要被动注册了,会主动帮你绑定
所以在 <script setup>
中申明的变量会主动被加到该 Vue 对象的自身 this 中,如
<script setup> |
<script> |
---|---|
const user = ref(null); |
this.$ref.user |
Ref 操作组件 Dom 和父子组件单向传递
props 父传子,子通过 emits 传父,这样单方向传递,在管制弹层组件的显示和暗藏方面也能够实现,然而如此一来,咱们就会像上面一样
父组件
<template>
<exchange-valid-modal-vue v-model:visible="visibleExchange" />
</template>
<script setup lang="ts">
// 转让管理员组件
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 lang="ts">
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 lang="ts">
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 lang="ts">
// 转让管理员组件
import ExchangeValidModalVue from './modal/ExchangeValidModal.vue';
// modal 弹层
const exchangeRef = ref(null); // 转让管理员可视化
const onExchangeOpen = () => {exchangeRef.onOpen() // 间接操作 dom 里 defineExpose 裸露进去的办法
};
</script>
如此,是不是比父子单向数据传递的形式更加高效易用?当然下面所说的只是我举的一个例子,当后续须要在组件内扩大性能也可按相似的办法代替单向数据流的形式扩大
但,请留神;这种操作 dom 形式,并不是什么场景下都是最佳的抉择;咱们能够分状况抉择,比方当一些数据只须要在子组件的领域中实现,而不须要父组件外加干预的状况下,抉择 ref 操作 dom 更为高效;
补充知识点:
defineExpose
在 Vue3.2 中,默认不会裸露任何在 <script setup>
中申明的绑定,即不能通过模板 ref
获取到组件实例申明的绑定。
Vue3.2 提供了 defineExpose
编译器宏,能够显式地裸露须要裸露的组件中申明的变量和办法