缘起

在开发一个我的项目之前,咱们往往都是先做下需要剖析,针对前端而言,就是能够调研或者抉择一个根底组件库,来进步咱们的工作效率。毕竟,对比拟计较工夫老本的公司来说,不会给你拿看电视剧玩游戏的工夫去专门开发一个相似日历的组件。但在市面上的组件库,并不能都能满足咱们的需要。这个时候,咱们就须要本人手写组件,来利用到我的项目中。

而这就是我想说的: 如何设计组件,让其既能易于利用(或者说缩小代码量),又能进步扩展性,不便需要变更和后续保护?

能够有很多种形式,而利用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>
// $refsshowManagerModal () {  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调整部门弹层Domconst 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 编译器宏,能够显式地裸露须要裸露的组件中申明的变量和办法