vue组件($refs对象,动态组件,插槽,自定义指令)

news2024/9/21 10:54:09

一、ref

1.ref引用

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

2.使用ref获取dom元素的引用
<template>
   <h3 ref="myh3">ref组件</h3>
   <button @click="getRef">getRef</button>
</template>
<script>
export default{
  methods:{
    getRef(){
      console.log('this',this.$refs);
      this.$refs.myh3.style.color='red'
    }
  }
}
</script>
3.使用ref引用组件实例
<template>
   <MyIndex ref="myIndexRef"></MyIndex>
   <button @click="getRef">getRef</button>
</template>
<script>
import MyIndex from './index copy.vue';
export default{
  components:{MyIndex},
  methods:{
    getRef(){
      console.log('this',this.$refs);
      this.$refs.myIndexRef.reset()
    }
  }
}
</script>

<template>
   <div>
    {{count}}
    <button @click="this.count+=1">按钮</button>
   </div>
</template>
<script>
export default{
  data(){
    return{
      count:1
    }
  },
  methods:{
    reset(){
      this.count=0
    }
  }
}
</script>
4.控制文本框和按钮的按需切换
<template>
  <input type="text" v-if="inputVisible" ref="ipt">
  <button v-else @click="showInput">getRef</button>
</template>
<script>
export default{
  data(){
    return{
      inputVisible:false
    }
  },
 methods:{
  showInput(){
     this.inputVisible=true;
     //获取文本框的引用对象,访问的ipt是undefind,dom更新是异步的,拿不到最新dom
     this.$nextTick(()=>{
      this.$refs.ipt.focus();
     })
   }
 }
}
</script>
5.组件是异步执行dom更新的
6.this.$nextTick(cb)

组件的$nextTick(cb)方法,会把cb回调推迟到下一个dom更新周期之后执行。简单理解:等组件的dom异步地重新渲染完成后,再执行cb回调函数。从而能保证cb回调函数可以操作到最新的dom元素。

二、动态组件

1.什么是动态组件

动态组件时动态切换组建的显示与隐藏。vue提供了一个<component>组件,专门用来实现组件的动态渲染。

a.<component>是组件的占位符

b.通过is属性动态指定要渲染的组件名称

c.<component is="要渲染的组件名称"></component>

2.如何实现动态组件渲染
<template>
  <button @click="comName='Home'">首页</button>
  <button @click="comName='Movie'">电影</button>
  <hr>
  <component :is='comName'></component>
</template>
<script>
import Home from './home.vue'
import Movie from './movie.vue'
export default{
  components:{Home,Movie},
  data(){
    return{
      comName:'Home'
    }
  }
}
</script>
3.使用keep-alive保持组件的状态

默认情况下,动态切换组件时无法保持组件的状态。可以使用vue内置的<keep-alive>组件保持动态组件的状态。

<template>
  <button @click="comName='Home'">首页</button>
  <button @click="comName='Movie'">电影</button>
  <hr>
  <keep-alive>
    <component :is='comName'></component>
  </keep-alive>
</template>
<script>
import Home from './home.vue'
import Movie from './movie.vue'
export default{
  components:{Home,Movie},
  data(){
    return{
      comName:'Home'
    }
  }
}
</script>

<template>
  <div>Home组件{{ count }}
    <button @click="count+=1">+1</button>
  </div>
</template>
<script>
export default{
  data(){
    return{
      count:1
    }
  },
  created(){
    console.log('created');
  },
  unmounted(){
    console.log('unmounted');
  },
}
</script>

三、插槽

1.什么是插槽

插槽是vue组件为组件的封装者提供的能力。允许开发者在封装组件时,把不确定、希望由用户指定的部分定义为插槽。

可以把插槽认为是组件封装期间,为用户预留的内容占位符。

没有预留插槽的内容会被丢弃

<template>
  <Home>
    <p>-------------</p>
  </Home>
</template>
<script>
import Home from './home.vue'
export default{
  components:{Home},
}
</script>

<template>
  <div>Home组件
    <p>p1p1p1p1p1p1p1p1p1p1p1p1p1</p>
     <slot></slot>
    <p>p2p2p2p2p2p2p2p2p2p2p2p2p2</p>
  </div>
</template>
2.后备内容(默认内容)

封装组件时,可以为预留的<slot>插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。

<template>
  <Home>
    <!-- <p>-------------</p> -->
  </Home>
</template>
<script>
import Home from './home.vue'
export default{
  components:{Home},
}
</script>

<template>
  <div>Home组件
    <p>p1p1p1p1p1p1p1p1p1p1p1p1p1</p>
     <slot>默认xxxxxxxx</slot>
    <p>p2p2p2p2p2p2p2p2p2p2p2p2p2</p>
  </div>
</template>
3.具名插槽

如果在封装组件时需要预留多个插槽节点,则需要为每个<slot>插槽指定具体的name名称。这种带有具体名称的插槽叫做“具名插槽”。

v-slot:可以简写为字符#

<template>
  <Home>
    <!-- v-slot:可简写为# -->
    <template v-slot:header>头部</template>
    <p>-------------</p>
    <template #footer>底部</template>
  </Home>
</template>
<script>
import Home from './home.vue'
export default{
  components:{Home},
}
</script>

<template>
  <div>Home组件
    <slot name="header"></slot>
    <slot>默认xxxxxxxx</slot>
    <slot name="footer"></slot>
  </div>
</template>
4.作用域插槽

在封装组件的过程中,可以为预留的<slot>插槽绑定props数据,这种带有props数据的<slot>插槽叫做作用域插槽。

<template>
  <Home>
    <template v-slot:default="scope">{{ scope }}{{ scope.info.name }}</template>
  </Home>
</template>
<script>
import Home from './home.vue'
export default{
  components:{Home},
}
</script>

<template>
  <div>Home组件
    <slot :info="infomation" :msg="msg"></slot>
  </div>
</template>
<script>
export default{
  data(){
    return{
      infomation:{
        name:'zs',
        age:18
      },
      msg:'123'
    }
  }
}
</script>

解构作用域插槽

<template>
  <Home>
    <template #default="{info,msg}">{{ info.name }}{{ info.age }}{{ msg }}</template>
  </Home>
</template>
<script>
import Home from './home.vue'
export default{
  components:{Home},
}
</script>

作用域插槽的使用场景

<template>
  <Home>
    <template #default="{user}">
      <td>{{user.name}}</td>
      <td>{{ user.age }}</td>
      <td>
        <input type="checkbox" :checked="user.state">
      </td>
    </template>
  </Home>
</template>
<script>
import Home from './home.vue'
export default{
  components:{Home},
}
</script>

<template>
  <div>Home组件
    <table>
      <tr>
        <td>名字</td>
        <td>年龄</td>
        <td>状态</td>
      </tr>
      <tr v-for="item in list" :key="item">
        <slot :user="item"></slot>
      </tr>
    </table>
  </div>
</template>
<script>
export default{
  data(){
    return{
      list:[{
        name:'zs1',
        age:18,
        state:true
      },
      {
        name:'zs2',
        age:17,
        state:false
      },{
        name:'zs3',
        age:15,
        state:true
      }],
    }
  }
}
</script> 

四、自定义指令

vue 中自定义指令分为两类:私有自定义指令,全局自定义指令。

在每个vue组件中,可以在directives节点下声明私有自定义指令。

1.私有自定义指令
<template>
  <input type="text" v-focus>
</template>
<script>
export default{
  directives:{
    focus:{
      //当被绑定的元素插入到dom中时,会自定触发mounted函数
      mounted(el){
        el.focus()//让被绑定的元素自动获取焦点
      }
    }
  }
}
</script>
2.全局自定义指令

在main.js文件中声明自定义指令

const app = createApp(App)
app.directive('focus',{
  mounted(el){
    el.focus()
  }
})
3.updated函数

mounted函数只在元素第一次插入dom时被调用,当dom更新时mounted函数不会被触发。updated函数会在每次dom更新后被调用。

<template>
  <input type="text" v-focus>
  {{ count }}
  <button @click="count+=1">+1</button>
</template>
<script>
export default{
  data(){
    return{
      count:1
    }
  }
}
</script>

//main.jsmain.js
const app = createApp(App)
app.directive('focus',{
  mounted(el){
    el.focus()
  },
  updated(el){
    el.focus()
  }
})

在vue2的项目中使用自定义指令时,mounted->bind,updated->update

如果mounted,updated函数中的逻辑完全相同,可以简写

const app = createApp(App)
app.directive('focus',(el)=>{
  el.focus()
})

4.指令的参数值

在绑定指令时,可以通过等号的形式为指令绑定具体的参数值。binding.value

<template>
  <input type="text" v-model.number="count" v-focus v-color="'red'">
  <p v-color="'green'">{{ count }}</p>
  <button @click="count+=1">+1</button>
</template>
<script>
import Home from './home.vue'
export default{
  components:{Home},
  data(){
    return{
      count:1
    }
  }
}
</script>

//main.js
const app = createApp(App)
app.directive('focus',(el)=>{
  el.focus()
})
app.directive('color',(el,binding)=>{
  el.style.color=binding.value
})

五、Table案例

1.搭建项目基本结构
npm create vite
cd vite-project
npm install
2.请求商品列表数据

npm i axios

//main.js
import axios from 'axios'
//配置请求的根路径
axios.defaults.baseURL='http://localhost:3000'
//将axios挂载为全局的$http自定义属性
app.config.globalProperties.$http=axios
3.封装MyTable组件

a.通过props属性,为MyTable.vue组件指定数据源

b.在MyTable.vue组件中,预留header的具名插槽

c.在MyTable.vue组件中,预留body的作用域插槽

<template>
  <table>
    <thead>
      <slot name="header"></slot>
    </thead>
    <tbody>
      <tr v-for="(item,index) in data" :key="item">
      <slot name="body" :row="item" :index="index"></slot>
    </tr>
    </tbody>
  </table>
</template>
<script>
export default{
  props:{
    data:{
      type:Array,
      default:[],
      require:true
    }
  }
}
</script>
4.实现删除功能
5.实现添加标签的功能

a.实现input和button的按需展示(v-if,v-else)

b.让input自动获取焦点(自定义指令)

c.文本框失去焦点自动隐藏

d.文本框的enter,esc按键事件

把用户在文本框中输入的值,预先转存到常量val中,清空文本的值,v-if='false'隐藏文本框

判断val的值是否为空,如果为空,则不进行添加,

判断val的值是否已经存在于数组中,如果存在,则不进行添加,return

将用户输入的内容,作为新标签push到当前tag数组中

<template>
  <MyTable :data="goodsList">
    <template #header>
      <td>#</td>
      <td>商品名称</td>
      <td>价格</td>
      <td>标签</td>
      <td>操作</td>
    </template>     
    <template #body="{row,index}">
      <td>{{index+1}}</td>
      <td>{{ row.title }}</td>
      <td>¥{{ row.price }}</td>
      <td>
        <input
          v-if="row.inputVisible"
          ref="InputRef"
          v-model.trim="row.inputValue"
          class="ml-1 w-20"
          @keyup.enter="handleInputConfirm(row)"
          @blur="handleInputConfirm(row)"
          @keyup.esc="row.inputValue=''"
          v-focus
        />
        <el-button v-else class="button-new-tag ml-1" size="small" @click="row.inputVisible = true">
          +Tag
        </el-button>
        <el-tag class="ml-2" type="success" v-for="item in row.tag" :key="item">{{ item }}</el-tag></td>
      <td>
        <el-button type="danger" @click="deleteFn(row.id)">删除</el-button>
      </td>
    </template>
  </MyTable>
</template>
<script>
import MyTable from './MyTable.vue'
export default{
  components:{MyTable},
  data(){
    return{
      goodsList:[]
    }
  },
  methods:{
    async getGoodsList(){
      const {data:res}=await this.$http.get('/goodsList')
      console.log('res',res);
      this.goodsList = res.map(item=>{
        return{
          ...item,
          inputVisible:false,
          inputValue:''
        }
      })
    },
    deleteFn(id){
      this.goodsList = this.goodsList.filter(item=>item.id!==id)
    },
    handleInputConfirm(row){
      const val = row.inputValue;
      row.inputValue = '';
      row.inputVisible = false;
      if(!val||row.tag.indexOf(val) !==-1)return
      row.tag.push(val)
    },
  },
  created(){
    this.getGoodsList()
  },
  directives:{
    focus(el){
      el.focus()
    }
  }
}
</script>

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

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

相关文章

手机切换IP简单方法:掌握技巧,轻松实现IP变换‌

在当今的数字化时代&#xff0c;IP地址作为网络身份的重要标识&#xff0c;对于网络访问、数据传输等方面都起着至关重要的作用。然而&#xff0c;在某些特定场景下&#xff0c;我们可能需要切换手机的IP地址&#xff0c;以满足不同的网络需求。本文将为大家介绍几种手机切换IP…

Linux —— 多线程

一、本篇重点 1.了解线程概念&#xff0c;理解线程与进程区别与联系 2.理解和学会线程控制相关的接口和操作 3.了解线程分离与线程安全的概念 4.学会线程同步。 5.学会互斥量&#xff0c;条件变量&#xff0c;posix信号量&#xff0c;以及读写锁 6.理解基于读写锁的读者写…

Kafka 3.0.0集群部署教程

1、集群规划 主机名 ip地址 node.id process.roles kafka1 192.168.0.29 1 broker,controller Kafka2 192.168.0.30 2 broker,controller Kafka3 192.168.0.31 3 broker,controller 将kafka包上传以上节点/app目录下 mkdir /app 解压kafka包 tar -zxvf kafka_…

BLE 协议之链路层

目录 一、前言二、状态和角色三、Air Interface Packets1、Preamble 字段2、Access Address 字段2.1 静态地址2.2 私有地址 3、PDU 字段3.1 Advertising Channel PDU3.1.1 Header 字段3.1.2 Payload 字段 3.2 Data Channel PDU3.2.1 Header 字段3.2.2 Payload 字段 4、CRC 字段…

【UE5】将2D切片图渲染为体积纹理,最终实现使用RT实时绘制体积纹理【第一篇-原理】

如果想直接制作&#xff0c;请看【第二篇】内容 这次做一个这样的东西&#xff0c;通过在2DRT上实时绘制&#xff0c;生成动态的体积纹理&#xff0c;也就是可以runtime的VDB 设想的文章流程: 对原理进行学习制作体积渲染制作实时绘制 第一篇&#xff08;本篇&#xff09;是对“…

Spring MVC 基础 : 文件、cookies的接收 ,REST响应

一、接受文件 在 Spring MVC 中&#xff0c;可以使用 RequestPart 注解来接收文件。这个注解常用于处理复杂的请求&#xff0c;如同时发送 JSON 数据和文件。RequestPart 非常适用于多部分请求&#xff08;multipart requests&#xff09;&#xff0c;这在单个请求中同时发送文…

法定退休年龄计算器,看看你还有多少年退休?

最近延迟退休上了微博热搜&#xff0c;从2025年开始实行。 分享个法定退休年龄计算器&#xff0c;看看你还有多少年退休&#xff1f; 官方出品的计算器&#xff0c;网站地址 https://si.12333.gov.cn/304647.jhtml 输入出生日期和性别就可以。 当然也可以在微信的城市服务。…

基于asp.net固定资产管理系统设计与实现

博主介绍&#xff1a;专注于Java vue .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的…

JZ2440开发板——S3C2440的UART

以下内容源于韦东山课程的学习与整理&#xff0c;如有侵权请告知删除。 一、UART硬件简介 UART&#xff0c;全称是“Universal Asynchronous Receiver Transmitter”&#xff0c;即“通用异步收发器”&#xff0c;也就是我们日常说的“串口”。 它在嵌入式中用途非常广泛&…

【计算机网络篇】物理层

本文主要介绍计算机网络第二章节的物理层&#xff0c;文中的内容是我认为的重点内容&#xff0c;并非所有。参考的教材是谢希仁老师编著的《计算机网络》第8版。跟学视频课为河南科技大学郑瑞娟老师所讲计网。 文章目录 &#x1f3af;一.基本概念及公式 &#x1f383;基本概念…

力扣-1035不相交的线(Java详细题解)

题目链接&#xff1a;力扣-1035不相交的线 前情提要&#xff1a; 因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。 dp五部曲。 1.确定dp数组和i下标的含义。 2.确定递推公式。 3.dp初始化。 4.确定dp的遍历顺序。 5.如果没有ac打印dp数组 利于debug。 每一…

安全基础学习-AES128加密算法

前言 AES&#xff08;Advanced Encryption Standard&#xff09;是对称加密算法的一个标准&#xff0c;主要用于保护电子数据的安全。AES 支持128、192、和256位密钥长度&#xff0c;其中AES-128是最常用的一种&#xff0c;它使用128位&#xff08;16字节&#xff09;的密钥进…

充电宝什么品牌比较好?五大性价比高充电宝品牌推荐!

在这个电子设备无处不在的时代&#xff0c;充电宝已成为我们生活中不可或缺的重要配件。无论是通勤、旅行还是户外休闲&#xff0c;充电宝都能为我们的手机、平板等提供及时的电量补充&#xff0c;确保我们时刻保持在线。一个安全可靠的充电宝&#xff0c;不仅能为我们的设备提…

GRU(门控循环单元)的原理与代码实现

1.GRU的原理 1.1重置门和更新门 1.2候选隐藏状态 1.3隐状态 2. GRU的代码实现 #导包 import torch from torch import nn import dltools#加载数据 batch_size, num_steps 32, 35 train_iter, vocab dltools.load_data_time_machine(batch_size, num_steps)#封装函数&…

VScode开发GD32移植(标准库通用),保姆级!!!!!!!

VScode开发GD32移植(标准库通用)&#xff0c;保姆级&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 文章目录 VScode开发GD32移植(标准库通用)&#xff0c;保姆级&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#…

[产品管理-30]:NPDP新产品开发 - 29 - 产品生命周期管理 - 可持续产品创新

目录 一、可持续开发与可持续创新 可持续开发 可持续创新 可持续开发与可持续创新的关系 二、循环经济 1、循环经济的定义 2、循环经济的起源与发展 3、循环经济的原则 4、循环经济的实践案例 5、循环经济的意义与展望 三、三重底线 1. 财务底线 - 企业。赚钱 2. …

11. DPO 微调示例:根据人类偏好优化LLM大语言模型

在部署大模型之后&#xff0c;我们必然要和微调打交道。现在大模型的微调有非常多的方法&#xff0c;过去的文章中提到的微调方法通常依赖于问题和答案对&#xff0c;标注成本较高。 2023 年所提出的 Direct Preference Optimization&#xff08;DPO&#xff09;为我们提供了一…

卡牌抽卡机小程序:市场发展下的创新

今年以来&#xff0c;卡牌成为了行业中的黑马&#xff0c;在国内迅速流行&#xff0c;成为消费者的心头好。小小的卡牌创下了百亿的市场规模&#xff0c;发展前景巨大&#xff01; 不过&#xff0c;随着卡牌市场的不断增长&#xff0c;市场发展也需要进行创新。线上抽卡机小程…

Yocto - 使用Yocto开发嵌入式Linux系统_02 认识 Yocto 项目

Meeting the Yocto Project 本章向你介绍 Yocto 项目。这里讨论的项目主要概念将贯穿全书。此外&#xff0c;我们还将简要讨论 Yocto 项目的历史、OpenEmbedded、Poky、BitBake、元数据和版本模式。系好安全带&#xff0c;欢迎加入我们的行列&#xff01; This chapter introdu…

信息安全数学基础(19)同余式的基本概念及一次同余式

一、同余式概念 同余式是数论中的一个基本概念&#xff0c;用于描述两个数在除以某个数时所得的余数相同的情况。具体地&#xff0c;设m是一个正整数&#xff0c;a和b是两个整数&#xff0c;如果a和b除以m的余数相同&#xff0c;则称a和b模m同余&#xff0c;记作a≡b(mod m)。反…