模板引用【ref
】
- Vue3官网-模板引用;
- 如果我们需要直接访问组件中的底层
DOM
元素,可使用vue
提供特殊的ref
属性来访问;
一、 访问模板引用
- 在视图元素上采用
ref
属性来设置需要访问的DOM
元素:- 该
ref
属性可采用 字符串 值的执行设置; - 该
ref
属性可采用v-bind:
或:ref
的形式来绑定 函数,其函数的第一个参数则为该元素;
- 该
- 如果元素的ref属性值采用的是字符串形式:
- 在组合式API
JS
中,我们需要声明一个同名的ref
变量,来获得该模板的引用; - 在选项式API
JS
中,可通过this.$refs
来访问模板的引用;
- 在组合式API
- 示例代码:
- 组合式API:
<template><!-- 字符串形式的 ref -->账号输入框:<input type="text" ref="account"><button @click="accountInputStyle">改变账号输入框的样式</button><!-- 函数形式的 ref ,必须采用 v-bind 或 : 的形式来给ref绑定属性值 -->密码输入框:<input type="password" :ref="passwordFn"><button @click="passwordInputStyle">改变密码输入框的样式</button> </template><script setup> import { ref, reactive, computed, onMounted, nextTick } from 'vue'// EXPLAIN 响应式数据 // ref 变量名 和 对应DOM元素的ref属性值 相等 let account = ref(null) let password = ref(null)// EXPLAIN 函数 const accountInputStyle = () => {// NOTE 此处设置的 style 均为行内样式account.value.style.padding = '15px'account.value.style.caretColor = 'red'account.value.className = 'rounded'account.value.focus() } // NOTE 采用函数给ref绑定属性值,该函数的第一个参数为该元素 // NOTE 在页面渲染的时候会自动执行 // NOTE 函数式生命的 ref,不会在 this.$refs 中获取 const passwordFn = (el) => {// el 元素是密码输入框password.value = elconsole.log(password.value) } const passwordInputStyle = () => {password.value.style.border = '4px solid green'password.value.style.padding = '15px'password.value.focus() }onMounted(() => {}); </script><style scoped> .rounded {border: 4px solid purple; } </style>
- 选项式API:
<script> export default {data: () => ({accountEl: null,passwordEl: null}),methods: {changeAccountInputStyle() {this.accountEl = this.$refs.account // 获取账号输入框的 DOMconsole.log(this.accountEl)this.accountEl.style = "padding: 15px"this.accountEl.className = "rounded"this.accountEl.focus()},passwordRef(el) { this.passwordEl = el // el 元素是密码输入框},changePasswordInputStyle() {console.log(this.passwordEl) console.log(this.$refs) // 函数式声明的 ref,不会在this.$refs中获取this.passwordEl.style = "padding: 15px"this.passwordEl.className = "rounded"this.passwordEl.focus()},} } </script><template><!-- ref 字符串值形式 -->账号输入框:<input type="text" ref="account"><button @click="changeAccountInputStyle">改变账号输入框的样式</button><hr><!-- ref 函数形式:元素渲染后,会立即执行该函数 -->密码输入框:<input type="password" :ref="passwordRef"><button @click="changePasswordInputStyle">改变密码输入框的样式</button> </template><style> .rounded {border-radius: 15px; } </style>
- 组合式API:
二、 v-for
中的模板引用
-
当在
v-for
中使用模板引用时:- 如果
ref
值是 字符串 形式,在元素被渲染后包含对应整个 列表的所有元素【数组】; - 如果
ref
值是 函数 形式,则会每渲染一个列表元素就会执行对应的函数【不推荐使用】;
- 如果
-
注意:需要
v3.2.25
及以上的版本; -
示例代码:
- 组合式API:
<script setup> import { onMounted, ref } from "vue";// 书本 let books = ref([{ id: 1, name: "海底两万里" },{ id: 2, name: "骆驼祥子" },{ id: 3, name: "老人与海" },{ id: 4, name: "安徒生童话" }, ]);let bookList = ref(null);onMounted(() => {console.log(bookList.value); // 获取引用的 DOM 对象,并打印,发现那么是数组,bookList.value[2].className = "error"; }); </script><template><ul><li v-for="b in books" :key="b.id" ref="bookList">{{ b.name }}</li></ul> </template><style> .error {border: 1px solid red; } </style>
- 选项式API:
<script> export default {data: () => ({books: [{ id: 1, name: "红楼梦" },{ id: 2, name: "三国演义" },{ id: 3, name: "水浒传" },{ id: 4, name: "西游记" },],students: [{ id: 1, name: "Jack" },{ id: 2, name: "Annie" },{ id: 3, name: "Tom" },],}),methods: {changeBookListStyle() {console.log(this.$refs.bookList);this.$refs.bookList[2].style = "color: red";},studentsRef(el) {console.log(el);},}, }; </script><template><ul><!-- 如果 ref 值是字符串形式,在元素被渲染后包含对应整个列表的所有元素【数组】 --><li v-for="b in books" :key="b.id" ref="bookList">{{ b.name }}</li></ul><button @click="changeBookListStyle">点我查看 bookList</button><hr /><!-- 如果ref值是函数形式,则会每渲染一个列表元素则会执行对应的函数【不推荐使用】 --><ul><li v-for="s in students" :key="s.id" :ref="studentsRef">{{ s.name }}</li></ul> </template>
- 组合式API:
-
运行效果:
- 选项式API:
- 组合式API:
- 选项式API:
三、 组件上的ref
-
模板引用也可以被用在一个子组件上;这种情况下引用中获得的值是组件实例;
- 如果子组件使用的选项式API,默认情况下父组件可以随意访问该子组件的数据和函数,除非在子组件使用
expose
选项来暴露特定的数据或函数,expose
值为字符串数组; - 如果子组件使用的是组合式API
<script setup>
,那么该子组件默认是私有的,则父组件无法访问该子组件,除非子组件在其中通过defineExpose
宏采用对象形式显示暴露特定的数据或函数;
- 如果子组件使用的选项式API,默认情况下父组件可以随意访问该子组件的数据和函数,除非在子组件使用
-
示例代码:
- 组合式API:
-
父组件:
<script setup> // NOTE 组合式API中,默认情况下,子组件中的数据、函数等等都是私有的,不能访问 // NOTE 如果 子组件 通过 defineExpose 宏采用对象形式显式暴露特定的数据或函数等等 import Vue1 from '@/components/27-组件上的ref - 组合式API/1.vue' import { ref, onMounted } from 'vue' const login_vue = ref(null) const showSonData = () => {console.log(login_vue.value.account)console.log(login_vue.value.password)login_vue.value.toLogin() } onMounted(() => {}); </script><template><h3>登录页面</h3><hr><!-- 组件上的 ref 的值为该组件的实例 --><Vue1 ref="login_vue"></Vue1><hr><button @click="showSonData">查看子组件的信息</button> </template>
-
子组件:
<script setup> import { ref } from 'vue' const account = ref('admin') const password = ref('123456') const toLogin = () => {alert('登录中……') } // TODO 采用 defineExpose 将指定数据、函数等等暴露出去 defineExpose({account,toLogin }); </script><template>账号:<input type="text" v-model="account"><br>密码:<input type="text" v-model="password"><hr><button @click="toLogin">登录</button> </template>
-
效果展示:
- 默认情况下(没有使用
defineEpxose
):
- 通过
defineExpose
暴露特定的属性或方法:
- 默认情况下(没有使用
-
-
选项式API:
- 父组件:
<script> // NOTE 选项式API中,默认情况下,父组件可以随意访问子组件的数据和函数、计算属性等等 // NOTE 如果 子组件 增加 expose 选项之后,就只能访问 expose 暴露的属性和函数等等 import Vue1 from '@/components/27-组件上的ref - 选项式API/1.vue' export default {name: 'App',components: { Vue1 },data: () => ({login_vue: null}),methods: {showSonData () {console.log(this.login_vue.account)console.log(this.login_vue.password)this.login_vue.toLogin()}},mounted () {// 打印出来的式子组件的ref对象this.login_vue = this.$refs.loginVueconsole.log(this.login_vue)} } </script><template><h3>登录页面</h3><hr><!-- 组件上的 ref 的值为该组件的实例 --><Vue1 ref="loginVue"></Vue1><hr><button @click="showSonData">查看子组件的信息</button> </template>
- 子组件:
<script> export default {name: 'Vue1',data: () => ({account: 'admin',password: '123456'}),methods: {toLogin () {alert('登录中……')}},// TODO 向外暴露属性函数,增加这个选项之后,父组件只能访问该组件暴露的属性或方法等等expose: ['account', 'password'] } </script><template>账号:<input type="text" v-model="account"><br>密码:<input type="text" v-model="password"><hr><button @click="toLogin">登录</button> </template><style scoped lang='scss'> </style>
- 运行展示:
- 未增加
expose
:
- 增加
epxose
:- 子组件没有暴露
toLogin
方法,所以此处访问不了;
- 子组件没有暴露
- 未增加
- 父组件:
- 组合式API: