文章目录
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元素
语法
- 目标标签 – 添加 ref 属性
<div ref="chartRef">我是渲染图表的容器</div>
- 恰当时机, 通过 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 操作组件
操作组件语法
-
目标组件 – 添加 ref 属性
<BaseForm ref="baseForm"></BaseForm>
-
恰当时机, 通过 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,并避免滥用。