Vue3全家桶 - Vue3 - 【8】模板引用【ref】(访问模板引用 + v-for中的模板引用 + 组件上的ref)

news2024/11/27 4:23:40

模板引用【ref

  • Vue3官网-模板引用;
  • 如果我们需要直接访问组件中的底层DOM元素,可使用vue提供特殊的ref属性来访问;

一、 访问模板引用

  • 在视图元素上采用ref属性来设置需要访问的DOM元素:
    • ref 属性可采用 字符串 值的执行设置;
    • ref 属性可采用v-bind::ref的形式来绑定 函数,其函数的第一个参数则为该元素;
  • 如果元素的ref属性值采用的是字符串形式:
    • 在组合式API JS 中,我们需要声明一个同名的ref变量,来获得该模板的引用;
    • 在选项式API JS 中,可通过this.$refs来访问模板的引用;
  • 示例代码:
    • 组合式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 = el
        console.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 // 获取账号输入框的 DOM
                  console.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>
      

二、 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:
      image.png
    • 组合式API:
      image.png

三、 组件上的ref

  • 模板引用也可以被用在一个子组件上;这种情况下引用中获得的值是组件实例;

    • 如果子组件使用的选项式API,默认情况下父组件可以随意访问该子组件的数据和函数,除非在子组件使用expose选项来暴露特定的数据或函数,expose值为字符串数组;
    • 如果子组件使用的是组合式API<script setup>,那么该子组件默认是私有的,则父组件无法访问该子组件,除非子组件在其中通过defineExpose宏采用对象形式显示暴露特定的数据或函数;
  • 示例代码:

    • 组合式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):
          image.png
        • 通过defineExpose暴露特定的属性或方法:
          image.png
    • 选项式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.loginVue
            console.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
          image.png
        • 增加 epxose
          • 子组件没有暴露 toLogin 方法,所以此处访问不了;
            image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1511428.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【基于langchain + streamlit 完整的与文档对话RAG】

本地部署文档问答webdemo 支持 pdf支持 txt支持 doc/docx支持 源文档索引 你的点赞和收藏是我持续分享优质内容的动力哦~ 废话不多说直接看效果 准备 首先创建一个新环境&#xff08;选择性&#xff09; conda create -n chatwithdocs python3.11 conda activate chatwith…

Promise其实也不难

难点图解&#xff1a;then&#xff08;&#xff09;方法 ES6学习网站&#xff1a;ES6 入门教程 解决&#xff1a;回调地狱&#xff08;回调函数中嵌套回调&#xff09; 两个特点&#xff1a; &#xff08;1&#xff09;对象的状态不受外界影响。Promise对象代表一个异步操作&…

Linux常见指令总结

ls&#xff1a;显示当前目录下文件列表 常用的命令行参数&#xff1a; -l 显示更多的文件属性 -a 显示所有的文件/目录&#xff08;包括隐藏的&#xff09; -d 只显示目录 ps&#xff1a;参数可以叠加使用。 例如&#xff1a;ls -la 显示所有文件…

力扣刷题Days16(js)-67二进制求和

目录 1,题目 2&#xff0c;代码 2.1转换进制数 2.2模拟加法 3&#xff0c;学习与总结 Math.floor() 模拟加法思路回顾 重点复习巩固 模拟加法的思路和学习位运算&#xff1b; 今天没精力了&#xff0c;先休息 1,题目 给你两个二进制字符串 a 和 b &#xff0c;以二进制…

2m高分辨率土地利用分类矢量数据/植被类型分布数据

土地利用数据是在根据影像光谱特征&#xff0c;结合野外实测资料&#xff0c;同时参照有关地理图件&#xff0c;对地物的几何形状&#xff0c;颜色特征、纹理特征和空间分布情况进行分析&#xff0c;建立统一解译标志的基础之上&#xff0c;依据多源卫星遥感信息&#xff0c;结…

【Echarts】曲线图上方显示数字以及自定义值,标题和副标题居中,鼠标上显示信息以及自定义信息

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《前端》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握…

前端精准测试调用链路分析

精准测试在评估需求的测试范围时&#xff0c;需要评估一下代码的影响范围&#xff0c;这个范围有两部分&#xff1a;一是需求直接修改的代码&#xff1b;二是修改代码影响到的功能模块。代码影响到的功能一般是通过调用链路分析来实现的&#xff0c;java和kotlin代码可以由java…

小白必看,靠这几步写一份简单的产品说明书!

我们都知道&#xff0c;无论是新产品发布&#xff0c;还是老产品的推广&#xff0c;产品说明书都扮演着至关重要的角色。产品说明书可以帮助用户正确、高效地使用产品&#xff0c;也是传递企业发展理念、展示企业形象的有效途径。但作为一个小白&#xff0c;怎样才能写一份简单…

JSONObject在Android Main方法中无法实例化问题

目录 前言一、Main(非安卓环境)方法下运行二、安卓坏境下运行三、why? 前言 原生的json,即org.json.JSONObject; 在Android Studio中的Main方法里运行报错&#xff0c;但在安卓程序运行过程正常 一、Main(非安卓环境)方法下运行 static void test() {try {// 创建一个 JSON …

动态类型是什么?——跟老吕学Python编程

动态类型是什么&#xff1f;——跟老吕学Python编程 前言动态编程语言动态编程语言特点&#xff1a;动态编程语言的优点&#xff1a;动态编程语言的缺点&#xff1a; 静态编程语言静态编程语言特点&#xff1a;静态编程语言的优点&#xff1a;静态编程语言的缺点&#xff1a; 总…

【Vue3】什么是路由?Vue中的路由基本切换~

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

Linux操作系统-07-Linux安装应用

一、使用rpm安装应用&#xff08;不推荐&#xff09; 先下载到本地&#xff0c;以.rpm文件名结尾&#xff0c;下载完成后&#xff0c;再安装 rpm -qa | grep mysql #查询当前系统是否有下载过mysql包 先上传mysql的rpm安装包到linux的opt目录 安装 rpm -ivh …

云游戏发行是什么?云游戏发行的演进历程

云游戏发行是一系列基于云游戏技术的游戏发行策略或行为&#xff0c;融合云试玩、云微端、可玩广告、跨端移植等技术&#xff0c;从而在传统游戏发行生态的基础上实现更为卓越的发行效果。 云游戏发行出现的原因 近年来&#xff0c;游戏市场出现负增长。其原因一方面在于游戏版…

删除数据表

oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 删除数据表属于数据库对象的操作 drop table 表名称; 删除 emp30 表 SQL> drop table emp30;表已删除。 上面这个语句运行后&#xff0c;就会把数据表 emp30 删除 在…

AV1:编码块划分

​AV1是AOM于2018年发布的一代视频编码标准&#xff0c;相比于VP9其编码效率提升30%&#xff0c;相对于H.26X系列标准&#xff0c;AV1完全免去专利费可以自由使用。 AV1和其他视频编码标准类似&#xff0c;也采用基于块的编码架构。当编码器读进一帧图像&#xff0c;首先将其划…

Vue 3中的provide和inject:跨组件通信的新方式

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【linux】冯诺依曼体系与操作系统的理解

本篇文章是进程的预备知识&#xff0c;但也不仅仅是进程的预备知识&#xff0c; 也可以更好地帮助我们理解整个计算机体系。 目录 冯诺依曼体系结构&#xff1a;进一步理解操作系统&#xff1a; 冯诺依曼体系结构&#xff1a; 关于这张图先进行一下必要的解释&#xff1a; 输…

【清晰易懂】@Mapper注解和BaseMapper爱恨情仇

此问题的提出在于自己没有弄明白一个问题&#xff0c;就是Mapper注解有时候可以不加&#xff0c;有时候又需要加。 先说结论&#xff1a;Mapper注解和BaseMapper类在项目中起着相同的作用&#xff0c;都是为了实现数据库基本简单的CRUD&#xff0c;省去在xml文件中再去写&#…

java八股文复习----java集合,CAS---2024/03/12

1.java常见的集合类 2.List&#xff0c;Set,Map的区别 3.上述三个集合有哪些常用的方法 4.List,Set,Map哪几个是线程安全的&#xff1f; 5.ArrayList和LinkedList的区别 6.ArrayList和Vector的区别 7.ArrayList的扩容机制 8.HashMap集合 8.1数据结构 8.2哈希冲突的解决办法有哪…

Conmi遇到的坑——禅道的PCDN

好家伙&#xff0c;悄悄在后台吃了七十多G流量&#xff0c;我把你当兄弟宣传&#xff0c;你把我当PCDN吸。 还纳闷今天创建个VUE项目怎么提示D盘没空间&#xff0c;明明留了几十G&#xff0c;好家伙&#xff0c;一下子全吸干了。 删了两个&#xff0c;还有一个&#xff08;已…