Vue2之组件4 ref 和 $refs的使用

ref 和 $refs 概述

vue优势:MVVM在vue中,程序员不需要操作DOM。程序员只需要把数据维护好即可!(数据驱动视图)

假设:在vue中,需要操作DOM了,需要拿到页面上某个DOM元素的引用,此时怎么办?

有人会说直接引用jQuery。在vue项目,不建议大家安装和使用jQuery。因为jQuery依赖手动操作DOM强制干预视图会导致Vue的响应式系统失效。

我们可以使用 ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。什么是$refs呢?

请添加图片描述
上面的是vue中的this,其中带$符号的都是vue内置成员,是vue挂在上去的,我们要学的是$refs(){}

每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,组件的 $refs 指向一个空对象。

ref 和 $refs 的作用和特点

作用:利用 ref 和 $refs 可以用于 获取 dom 元素, 或 组件实例
特点:查找范围 → 当前组件内 (更精确稳定)

ref 和 $refs 操作dom元素

语法

  1. 目标标签 – 添加 ref 属性
    <div ref="chartRef">我是渲染图表的容器</div>
  2. 恰当时机, 通过 this.$refs.xxx, 获取目标标签。(适当时机是指vue的生命周期。具体情况根据实际需求。)
    mounted () {
    	console.log(this.$refs.chartRef)
    },
    

使用场景举例

问题代码

请看下面代码存在问题。

<template>
  <div class="app">
    <!-- App.vue -->
    <h2>父组件</h2>
    <hr>
    <BaseChart></BaseChart>
  </div>
  
</template>

<script>
import BaseChart from '@/components/BaseChart.vue';

export default {
  data() {
    return {
      
    }
  },
  components: {
    BaseChart
  }
}
</script>

<style lang="less" scoped>
.app {
  width: 800px;
  height: 600px;
  border: 1px solid #ccc;
  margin: 0 auto;
  padding: 20px;
  .chart{
    height: 80px;
  }
}
</style>
<template>
  <div class="base-chart">
    <!-- BaseCharts.vue -->
    <h3>子组件</h3>
    <div class="chart"></div>
  </div>
</template>

<script>
// 引入echarts主模块  可通过npm install echarts 安装
import * as echarts from "echarts";
export default {
  // 使用 mounted 钩子函数。 在组件挂载后执行
  // 也不一定在 mounted 中使用,也可以在 created 中使用,具体看需求
  mounted() {
    // 基于准备好的dom,初始化echarts实例
    const myChart = echarts.init(document.querySelector(".chart"));
    // 指定图表的配置项和数据
    var option = {
      title: {
        text: "衣服销量",
      },
      tooltip: {},
      xAxis: {
        data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"],
      },
      yAxis: {},
      series: [
        {
          name: "销量",
          type: "bar",
          data: [5, 20, 36, 10, 10, 20],
        },
      ],
    };
    // 使用刚指定的配置项和数据显示图表。
    myChart.setOption(option);
  },
};
</script>

<style lang="less" scoped>
.base-chart {
  width: 500px;
  height: 400px;
  border: 1px solid #ccc;
  .chart {
    width: 500px;
    height: 300px;
  }
}
</style>

代码解释
在这里插入图片描述
运行结果
在这里插入图片描述

问题分析

子组件的 初始化echarts实例代码const myChart = echarts.init(document.querySelector(".chart"));有问题。因为vue是单页面应用程序。所以使用document.querySelector获取的是第一个叫.chart的标签元素。如果父组件有一个.chart的标签元素。那么子组件就不显示了。
在这里插入图片描述

下面结果这是在父组件显示,而子组件不再显示。
在这里插入图片描述

使用ref和$refs 改变作用范围

<template>
  <div class="app">
    <!-- App.vue -->
    <h2>父组件</h2>
    <div class="chart">
      我和子组件的class="chart"相同
    </div>
    <hr>
    <BaseChart></BaseChart>
  </div>
  
</template>

<script>
import BaseChart from '@/components/BaseChart.vue';

export default {
  data() {
    return {
      
    }
  },
  components: {
    BaseChart
  }
}
</script>

<style lang="less" scoped>
.app {
  width: 800px;
  height: 600px;
  border: 1px solid #ccc;
  margin: 0 auto;
  padding: 20px;
  .chart{
    height: 80px;
  }
}
</style>
<template>
  <div class="base-chart">
    <!-- BaseCharts.vue -->
    <h3>子组件</h3>
    <!-- <div class="chart"></div> -->
    <div ref="mychart" class="chart"></div>
  </div>
</template>

<script>
// 引入echarts主模块  可通过npm install echarts 安装
import * as echarts from "echarts";
export default {
  // 使用 mounted 钩子函数。 在组件挂载后执行
  // 也不一定在 mounted 中使用,也可以在 created 中使用,具体看需求
  mounted() {
    // 基于准备好的dom,初始化echarts实例
    // const myChart = echarts.init(document.querySelector(".chart"));
    const myChart = echarts.init(this.$refs.mychart);
    // 指定图表的配置项和数据
    var option = {
      title: {
        text: "衣服销量",
      },
      tooltip: {},
      xAxis: {
        data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"],
      },
      yAxis: {},
      series: [
        {
          name: "销量",
          type: "bar",
          data: [5, 20, 36, 10, 10, 20],
        },
      ],
    };
    // 使用刚指定的配置项和数据显示图表。
    myChart.setOption(option);
  },
};
</script>

<style lang="less" scoped>
.base-chart {
  width: 500px;
  height: 400px;
  border: 1px solid #ccc;
  .chart {
    width: 500px;
    height: 300px;
  }
}
</style>

代码解析
在这里插入图片描述

结果如下
在这里插入图片描述

ref 和 $refs 操作组件

操作组件语法

  1. 目标组件 – 添加 ref 属性

    <BaseForm ref="baseForm"></BaseForm>
    
  2. 恰当时机, 通过 this.$refs.xxx, 获取目标组件,就可以调用组件对象里面的方法。

    this.$refs.baseForm.组件方法()
    

操作组件使用

<template>
<!-- App.vue -->
  <div class="app">
    <h4>父组件 -- <button @click="getBaseForm">获取组件实例</button></h4>
    <BaseForm ref="baseForm"></BaseForm>
    <div>
      <button @click="getFormData">获取数据</button>
      <button @click="resetFormData">重置数据</button>
    </div>
  </div>
</template>

<script>
import BaseForm from './components/BaseForm.vue'
export default {
  components: {
    BaseForm,
  },
  methods: {
    getBaseForm() {
      const baseForm = this.$refs.baseForm;
      console.log('获取组件实例', baseForm);
    },
    getFormData() {
      const formdata = this.$refs.baseForm.getFormData();
      console.log('获取表单数据', formdata.username, formdata.password);
    },
    resetFormData() {
      this.$refs.baseForm.resetFormData()
      console.log("重置表单成功");
    }
   
  }
}
</script>

<style>
.app div button{
  margin-right: 8px;
}
</style>
<template>
<!-- BaseForm.vue -->
  <div class="app">
    <div>
      账号: <input v-model="username" type="text">
    </div>
     <div>
      密码: <input v-model="password" type="text">
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: 'admin',
      password: '123456',
    }
  },
  methods: {
    getFormData() {
      
      return {
        username: this.username,
        password: this.password,
      }
    },
    resetFormData() {
      this.username = ''
      this.password = ''
      
    },
  }
}
</script>

<style scoped>
.app {
  border: 2px solid #ccc;
  padding: 10px;
}
.app div{
  margin: 10px 0;
}

</style>

关键代码解释
在这里插入图片描述

运行结果如下
在这里插入图片描述

子传父和ref操作子组件传值的区别

子传父是父组件监听子组件的方法。子组件触发函数,父组件被动接收。

ref操作子组件传值 是 父组件主动调用子组件的方法。

优先使用 ​​$emit​​ 满足大部分通信需求,保持代码清晰和解耦。
仅在需要​​直接操控子组件​​时谨慎使用 ​​$refs​​,并避免滥用。

Vue 2中,孙子组件想要获取爷爷组件的`$refs`引用通常需要通过事件总线或者Vuex状态管理工具,因为直接父子、祖孙之间的数据传递通常是单向数据流,避免了深度嵌套的`this.$parent`或`this.$grandparent`。 1. **事件总线**:你可以创建一个全局的事件中心,当爷爷组件设置`ref`并触发一个事件时,孙子组件可以监听这个事件来获取`$refs`的相关信息。 ```javascript // 爷爷组件 <template> <button @click="emitGrandparentRef">点击获取ref</button> <Child ref="childRef" /> </template> <script> export default { methods: { emitGrandparentRef() { this.$emit('get-grandparent-ref', this.childRef); } } }; </script> // 孙子组件 <template> <div>{{ grandparentRef }}</div> </template> <script> import bus from '@/events/bus'; // 假设你有一个全局事件中心 export default { mounted() { bus.on('get-grandparent-ref', (grandparentRef) => { this.grandparentRef = grandfatherRef; }); }, beforeDestroy() { bus.off('get-grandparent-ref'); } }; </script> ``` 2. **Vuex**:如果数据需要在整个应用共享,也可以考虑使用Vuex来存储获取`$refs`。爷爷组件将`ref`值保存在store中,孙子组件订阅这个数据即可。 ```javascript // store.js state: { childRef: null, }, actions: { setGrandparentRef({ commit }, ref) { commit('SET_CHILD_REF', ref); } }, mutations: { SET_CHILD_REF(state, ref) { state.childRef = ref; } } // 爷爷组件 methods: { setChildRef() { this.$refs.childRef && this.$store.dispatch('setGrandparentRef', this.$refs.childRef); } } // 孙子组件 computed: { grandparentRef() { return this.$store.state.childRef; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值