之前说过了 ref() 函数,现在说的标签中的 ref 属性 和 ref() 函数也存在一定关联。
2、
标签中的 ref 属性分为两种情况:
-
用在普通
DOM
标签上,获取的是DOM
节点。 -
用在组件标签上,获取的是组件实例对象
Vue2 中标签上的 ref 属性
在Vue2 中, 当在 普通 DOM 元素上使用 ref 时,可以直接通过 this.$refs 拿到这个 DOM元素以及 DOM 元素中的某些值(value、innerHTML 等),例如:
<template><input type="text" ref="input"> <br><br><button @click="showDOM">获取DOM节点</button><button @click="showValue">获取DOM节点内容</button>
</template><script>
export default {name: "App",methods: {showDOM() {console.log(this.$refs.input);},showValue() {console.log(this.$refs.input.value);}}
}
</script>
Vue2 中的组件上的 ref 属性
ref 属性也可以用在组件上,获取的是组件的实例对象
<template><Child ref='Child'/><button @click="showDOM">获取组件实例</button> <br>
</template><script>
import Child from './components/Child.vue';
export default {name: "App",methods: {showDOM() {console.log(this.$refs.Child);},},components: { Child }
}
</script>
Vue3 中【 标签 】上的 ref 属性 +【 setup() 函数 】
在模板中的使用方式和 Vue2 一致,直接当做标签属性使用即可
<template><h2 ref="title">父组件内容</h2> <br><button @click="showDOM">获取DOM节点</button> <br>
</template>
但是,在 setup() 函数中的使用方式存在差异。
首先 需要通过 ref() 函数来创建一个空的 ref 对象。
然后通过变量接收,且该变量名称需要与 DOM 标签上的 ref 属性值相同。
<script>
import {ref} from 'vue'
export default {name: "App",setup(props, context) {// 通过 ref() 函数创建一个空的 ref 对象,// 且通过与DOM元素上ref属性值一致的 title 作为变量接收 let title = ref()function showDOM() {console.log(title.value);}return {title,showDOM,}},
}
</script>
展示效果:拿到了完整的DOM节点
Vue3 中【 组件标签 】上的 ref 属性 + 【 setup() 函数 】
在父组件中引入子组件,然后在子组件标签上添加 ref 属性。在 setup() 函数中同样通过 与 ref 属性值相同的变量名接收空的 ref 对象
<template><Child ref="child"/><button @click="showCompoment">获取组件实例</button>
</template><script>
import Child from './components/Child.vue';
import {ref} from 'vue'
export default {name: "App",setup() {let child = ref()function showCompoment() {console.log(child.value);}return {child,showCompoment}},components: { Child }
}
</script>
然后在子组件中 通过 ref() 定义几个响应式数据,但是没有使用,也没有返回。按照Vue2 的情况,此时在父组件中是可以访问到子组件中的数据的,但是通过打印,我们发现什么都没拿到。
<template><div>这是子组件</div>
</template><script>
import { ref } from 'vue'
export default {name: 'ChildComponent',setup() {let name = ref('al')let age = ref(29)return {}}
}
</script>
展示效果
此时我们将数据通过 return 返回。
setup() {let name = ref('al')let age = ref(29)return {name,age}
}
此时会发现,在父组件中能拿到返回出去的值了。
这说明了两点
-
ref
获取的是组件实例:通过ref
属性获取的是子组件的实例对象 -
访问公开的数据:只能访问子组件通过
setup
返回的、被公开的响应式数据或方法。子组件中的私有状态或未返回的数据是无法直接访问的。
于此同时,还有一种方式可以对外暴露数据,那就是 setup() 函数中接收的第二个参数 context。该参数是一个对象,对象中有一个属性为 expose。该属性是一个方法,接收一个对象,对象内部则是向外暴露的指定数据或方法。
setup(props, context) {let name = ref("al");let age = ref(29);function text() { }// 通过 context.expose 向外暴露指定的属性或方法context.expose({ text });// 返回值为空return { name, age };
},
父组件中接收到了 子组件中暴露的指定数据和方法。但是对于 return 返回出去的方法或数据并没有接收到
Vue3 中【 组件标签 】上的 ref 属性 + 【 <script setup> 】
在 setup() 函数中,我们可以通过 return 来返回数据暴露给父组件,也可以通过 expose() 向外暴露指定的属性和方法。那么在 <script setup> 中没有 context.expose(),也不能通过 return 返回,此时又该怎么办呢?
Vue3 提供了一个新的 宏方法 -- defineExpose() ,可以在 【 <script setup> 】模式中向外暴露指定的数据或方法。
子组件:定义三个数据和方法,但是并没有显式的 return 返回
<template><div>这是子组件</div>
</template><script setup lang="ts">
import { ref } from "vue";let name = ref("al");
let age = ref(29);
function text() { }</script>
父组件中获取子组件中的数据
<template><Child ref="child"/><button @click="showCompoment">获取组件实例</button>
</template><script>
import Child from './components/Child.vue';
import {ref} from 'vue'
export default {name: "App",setup() {let child = ref()function showCompoment() {console.log(child.value,'child');}return {child,showCompoment}},components: { Child }
}
</script>
展示效果:获取为空,数据或方法都没有获取到
通过 defineExpose() 向外暴露指定的数据或方法
<script setup lang="ts">
import { ref ,defineExpose} from "vue";let name = ref("al");
let age = ref(29);
function text() { }defineExpose({ name, age, text });</script>
展示效果:父组件获取到了子组件指定暴露的数据和方法
总结
1、在标签中添加 ref 属性的同时,需要在 脚本中 通过 ref() 函数定义一个容器来放置 给ref属性打上标签的DOM或组件实例,且接收的变量需要 与 标签中的ref 属性值相同。
2、标签中的 ref 属性允许在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用。可能存在的情况是,在挂载之前,ref() 函数已经执行,此时获取的引用则是undefined,在挂载之后,Vue 底层会自动找到对应的 ref() 与 经过ref 标记的DOM或组件,并赋值给 Value属性。
3、在Vue2 中,通过 this.$refs.xx 的方式,是可以直接获取组件实例对象上的任意方法或属性的,但是 Vue3 中禁止了这一情况。取而代之的是通过指定暴露的方式像父组件暴露数据或方法,这是因为 按照Vue3 的指定暴露方法,可以降低耦合度,增强组件封装性。
4、Vue3 中 存在两种情况,一种是 setup() 函数,一种是<script setup>,在这两种情况中对外暴露指定数据或方法的逻辑代码不一致。
- setup() 函数:setup函数接收两个对象参数(props,context),context对象中存在 expose 方法,该方法接收一个空值或一个对象。若为空,则表示不对外暴露任意数据方法,若为对象,则表示只对外暴露对象内部属性或方法。
- <script setup>:通过 新的 API defineExpose() 来指定对外暴露的数据或方法。
5、父组件只能访问子组件通过 setup
返回的、被公开的响应式数据或方法。子组件中的私有状态或未返回的数据是无法直接访问的。( 如果没有指定暴露数据,则 setup()函数中 return 出去的数据 父组件都能接受到。如果指定了暴露数据,父组件则只能接收到指定暴露的数据,return中的其他方法不能接收 )
6、<script setup> 中的代码会在编译时,生成一个 setup() 函数,将<script setup> 中的脚本经过编译后塞到 setup() 函数中。