当前位置: 首页 > news >正文

学习vue3第十二节(组件的使用与类型)

1、组件的作用用途

目的:
提高代码的复用度,和便于维护,通过封装将复杂的功能代码拆分为更小的模块,方便管理,
当我们需要实现相同的功能时,我们只需要复用已经封装好的组件,而不需要重新编写相同的代码

好处:
代码复用:我们可以在多个地方复用封装好的组件,而不需要在每个地方都写相同的代码。
易于维护:当我们需要修改某个功能时,我们只需要在一个地方修改即可,而不需要在多个地方修改。
易于理解:通过将复杂的功能分解为更小的部分,我们可以更容易地理解和管理代码
单一职责:组件应该只做一件事情,而不是承担多个无关的职责,避免过度封装,导致功能复杂化

2、组件的分类

2.1. 全局组件

好处:只需要在main.ts 中挂载注册,不用在使用页面中一个一个的引入注册,减少了代码的冗杂;
坏处:会导致组件之间的依赖关系不明确,导致组件之间查找调试错误时候难以追踪具体位置;
同时,由于全局组件会被打包的打包后的文件中,导致初始化首屏加载时间长,编译和预处理时间长,从而影响用户的体验;谨慎使用全局组件

首先我们会先将一个vue组件定义在一个单独的.vue文件中,这就是单文件组件
如:定义一个全局的顶部搜索栏 headerForm.vue 文件

// headerForm.vue
<template><div class="header-form">全局顶部搜索栏name:{{ defineProps.name }}<el-row><el-col :span="6"><el-form :inline="true" :model="formInline" class="demo-form-inline"><el-form-item label="用户名"><el-input v-model="formInline.user" placeholder="请输入用户名"></el-input></el-form-item></el-form></el-col></el-row></div>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue'
// defineProps 是vue3中新增的宏,用于处理接收 props 的属性,不需要引入,可以直接在setup 语法糖中使用;后续文章会专门介绍
const defineProps = defineProps({name: {type: String,default: ''}
})
// 此处使用了 ref() 函数 创建了 对象,为什么不用reactive() 函数呢?因为使用reactive() 函数,要使用嵌套函数保持响应式 需要使用 toRefs() 将其转换ObjectRefImpl 对象,详情请看 第五节
let formInline = ref({user: defineProps.name // 将user默认值设置为父组件传入的name 属性值
})
// watch 的相关内容请看第六节 watch
watch(() => defineProps.name, (n,o) => {// 监听name 的变化,用于同步更新user属性;formInline.value.user = defineProps.name
})
</script>
<style lang='scss' scoped>
.header-form{width: 100vw;height: 46px;
}
</style>
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import App from './App.vue'
<!-- 引入全局组件 -->
import HeaderFrom from './components/headerForm.vue'
import 'element-plus/dist/index.css'
import './style.css'
const app = createApp(App)
<!-- 挂载全局组件 -->
// app.component('注册的组件名称', 引入的单文件组件)
app.component('HeaderFrom', HeaderFrom) // 支持链式调用<!-- 如果全局组件比较多,可以这样使用,将element的所有图标注册到项目中,非必要勿用 -->
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component)
}app.use(ElementPlus)
app.mount('#app')
<!-- App.vue -->
<template>
<div class="par">//  直接使用 HeaderFrom 即可,不需要再次引入<HeaderFrom :name="name"></HeaderFrom><Provide></Provide><button @click="handleChangeName">父组件change name</button></div>
</template>
<script setup>
import Provide from './components/provide1.vue'
import { ref, provide, inject } from 'vue'
let msg = ref('Andy start')
let name = ref('Andy')const handleChangeName = () => {name.value = 'Andy change'
}
const sChangeName = () => {msg.value = 'Andy sChangeName'
}
provide('pName', {msg, sChangeName})
</script><style lang="scss">
.img-box{display: inline-block;width: 100px;height: 100px;
}
</style>

注意:如果声明的组件不是项目中绝大多数页面共用的,请勿注册全局组件,应为即使在页面中没有使用该组件,也会导致引入的组件被打包到打包后的文件中,无法通过 tree-shaking 自动移除,因为组件被挂载到app实例上;

tree-shaking:消除无用的js代码;
1、在webpack中,通过配置 optimization.usedExports: true,开启tree-shaking功能
2、在vite中,通过配置 rollup-plugin-commonjs,开启tree-shaking功能;

2.2. 局部组件

好处
1、可以非常清晰父子组件之间的依赖关系,便于维护;
2、同时只有在页面加载时候才会使用组件,可以更好的支持tree-shaking减少打包体积;
坏处
需要每次使用时候导入一次,增加文件的导入语句;

<!-- 定义局部组件 dialogForm.vue -->
<template>
<div class="dialog-container"><div class="dialog-form"><div class="dialog-header"><div class="dialog-title">新增用户</div><el-icon class="dialog-close"><Close /></el-icon></div><el-form :model="formData" label-width="80px" ref="formDataRef"><el-form-item label="用户名:" prop="username"><el-input v-model="formData.username" placeholder="请输入用户名" /></el-form-item><el-form-item label="职位:" prop="post"><el-input v-model="formData.username" placeholder="请输入职位" /></el-form-item><el-button @click="submit" type="primary">提交</el-button></el-form></div>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue'const formDataRef = ref()
let formData = ref({username: '',post: ''
})const submit = () => {console.log('==', formData.value)
}</script>
<!-- 在父组件myComponents.vue 中引入使用 -->
<template><div class="my-components">页面类型:{{pageNameType}}<DialogForm></DialogForm></div></template><script setup>import { ref, reactive } from 'vue'// 在setup 中直接引入组件即可,打包编译时会自动编译成组件,不需要再次使用componentsimport DialogForm from './dialogForm.vue'const pageNameType = ref('局部组件')</script>

2.3. 动态组件

动态组件,当一个页面中的内容需要通过不同的组件来进行展示时,可以通过动态组件的切换来实现;
需要注意的是:vue3中 需要使用markRaw() 或者 shallowReactive() 来创建组件,否则页面会发出警告,导致页面不更新
请添加图片描述

动态组件是通过 :is 属性来实现组件之间的切换的;

<component :is="组件名"></component>

如下实现:

<template><div class="my-components">页面类型:{{pageNameType}}<component :is="comName"></component><el-button type="primary" @click="handleChangeCom">切换组件</el-button></div>
</template>
<script setup>import { ref, markRaw } from 'vue'import Work from './work.vue' // 组件内容: The job is GeYou's live.import UserInfo from './userInfo.vue' // 组件内容:用户信息const pageNameType = ref('动态组件')const workRaw = markRaw(Work) // 使用markRaw进行标记,避免转换为响应式对象const userInfoRaw = markRaw(UserInfo)let comName = ref(workRaw) // 默认展示组件为 Workconst handleChangeCom = () => {console.log('===', comName)if (comName.value === workRaw) {comName.value = userInfoRaw} else {comName.value = workRaw}}
</script>

2.4. 递归组件

vue2 中是通过组件中的name属性来区分组件的,直接在自己组件中使用自己同名组件即可实现递归;
如:
递归组件注意需要有跳出循环的出口,不然会导致死循环,进而栈溢出; 跳出条件:v-if=“endNum < 4”
若不跳出循环将会报错:

请添加图片描述

<template><div class="recursion-com" v-if="endNum < 4"><span>{{name}}--{{ num }}--{{ endNum }}</span><div >// 此处调用直接与自己的组件名称保持一致即可<RecursionCom :num="endNum" ></RecursionCom> </div></div>
</template>
<script>
export default {name: 'RecursionCom', // 注意:需要声明组件名称components: {},props: {num: {type: Number,default: 0}},data() {return {name: 'vue2递归组件',list: [22, 33],endNum: 0}},created() {this.endNum = this.numthis.endNum++}
}
</script>

vue3 中

父组件中:
<template>
<div>vue3 父组件<RecursionComV3 :data="data"></RecursionComV3>
</div>
</template>
<script setup>
import { ref } from 'vue'
import RecursionComV3 from './recursionComV3.vue';
// 声明一个data 变量数据
const data = ref([{id: '1-1',name: '张家',children: [{id: '1-1-2',name: '张三',children: [{id: '1-1-3',name: '张三郎',}]}]},{id: '1-2',name: '李家',children: [{id: '1-2-2',name: '李四',children: [{id: '1-2-3',name: '李四郎',}]}]}
])
</script>
<!-- 子组件中 -->
使用js 书写时:
<template>
<div class="my-recursion"><div v-for="(item, index) in props.data" :key="item.id"><div class="item">{{ item.id }}:{{item.name}}</div><myTreeV3 v-if="item.children && item.children.length" :data="item.children"></myTreeV3></div>
</div>
</template>
<script>
// 如果使用js 需要在子组件中使用export default{} 给组件命名name;
export default {name: 'myTreeV3', 
}
</script>
<script setup>
import { ref, reactive, toRefs, onMounted, watch, computed } from 'vue'
const props = defineProps({data: {type: Array,default: () => ([]),},
})
</script>

最终运行结果如下图
请添加图片描述

若子组件使用ts 书写:
注意:ts 中需要使用到递归组件,需要使用到递归类型,需要将数据类型进行声明,
同时 调用的递归组件名称 需要与 自己的文件名保持一致,否则不生效:比如文件名为recursionComV3Ts.vue,则调用时需要使用:

如下:

<template>
<div class="my-recursion"><div v-for="(item) in data" :key="item.id"><div class="item">{{ item.id }}:{{item.name}}</div><!-- 使用ts 写递归组件时,调用的必须与自己的文件名保持一致 --><recursionComV3Ts v-if="item?.children && item?.children?.length" :data="item?.children"></recursionComV3Ts></div>
</div>
</template>
<script setup lang="ts">
// TreeData 声明接口类型
interface TreeData{id: stringname: stringchildren?:TreeData[] // children 是可以选项
}
defineProps<{data?:TreeData[]
}>()
</script>

3、组件的使用 注意事项

a、单文件组件结构、
b、命名规范,便于管理;
c、props属性传递声明类型,明确;
d、emit事件机制;
e、slot插槽,非常重要的部分,可以动态的将内容html插入到组件中;

下节 单独介绍 组件之间的传参,事件等

相关文章:

学习vue3第十二节(组件的使用与类型)

1、组件的作用用途 目的&#xff1a; 提高代码的复用度&#xff0c;和便于维护&#xff0c;通过封装将复杂的功能代码拆分为更小的模块&#xff0c;方便管理&#xff0c; 当我们需要实现相同的功能时&#xff0c;我们只需要复用已经封装好的组件&#xff0c;而不需要重新编写相…...

flume配置文件后不能跟注释!!

先总结&#xff1a;Flume配置文件后面&#xff0c;不能跟注释&#xff0c;可以单起一行写注释 报错代码&#xff1a; [ERROR - org.apache.flume.SinkRunner$PollingRunner.run(SinkRunner.java:158)] Unable to deliver event. Exception follows. org.apache.flume.EventDel…...

【docker】Dockerfile自定义镜像

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;中间件 ⛺️稳中求进&#xff0c;晒太阳 1.Dockerfile自定义镜像 常见的镜像在DockerHub就能找到&#xff0c;但是我们自己写的项目就必须自己构建镜像了。 而要自定义镜像&#xff0c;就…...

webpack项目打包console git分支、打包时间等信息 exec

相关链接 MDN toLocaleString child_process Node.js strftime 格式 代码 buildinfo.js const { execSync, exec } require("child_process"); // exec: 在 Windows 执行 bat 和 cmd 脚本// execSync 同步 // exec 异步// exec 使用方法 // exec(git show -s,…...

Linux centos7离线搭建FTP

1、下载、安装ftp 下载ftp安装包&#xff0c;可以从rpm下载站搜索合适的版本&#xff0c;使用wget命令下载。 wget https://mirrors.aliyun.com/centos/7/os/x86_64/Packages/vsftpd-3.0.2-28.el7.x86_64.rpm 安装&#xff1a; rpm -ivh vsftpd-3.0.2-28.el7.x86_64.rpm 2…...

关于GPT-SoVITS语音合成的效果展示(西游之西天送葬团)

目录 使用效果总结合成效果展示 使用效果总结 使用的是2024年03月21日22点28分更新的版本。 使用起来很方便&#xff0c;从它“自带界面”这点就能看出&#xff0c;易于使用也是目的之一&#xff0c;而且从训练到推理的每个步骤都能在界面中完成。 集成了多个实用工具&#…...

如何安装OceanBase的OBD

选择一&#xff1a;借助 all-in-one 安装包安装 OBD&#xff08;推荐&#xff09; OceanBase 社区版的all-in-one安装包是一个集成了多种工具的一键式安装包。它包含了数据库软件本身&#xff0c;以及OBD、OBProxy、OBClient&#xff0c;自4.1版本起&#xff0c;还额外加入了O…...

Unity 读写Excel打包后无法运行可能的解决方案

读写Excel打包后无法运行可能的解决方案 &#x1f4a1;.适用于NPOI、EPPlus。 &#x1f4a1;.下载 资源包&#x1f448;,解压后把dll放到Assets目录中再重新打包即可。...

算法沉淀 —— 深度搜索(dfs)

算法沉淀 —— 深度搜索&#xff08;dfs&#xff09; 一、计算布尔二叉树的值二、求根节点到叶节点数字之和三、二叉树剪枝四、验证二叉搜索树五、二叉搜索树中第K小的元素 一、计算布尔二叉树的值 【题目链接】&#xff1a;2331. 计算布尔二叉树的值 【题目】&#xff1a; …...

#设计模式#3.1用做松鼠桂鱼来理解抽象工厂(对象创建型模式)

概念&#xff1a;xx工厂&#xff0c;xx产品 区分 工厂是动作&#xff0c;产品是结果&#xff08;菜品&#xff09; 概念&#xff1a;抽象xx&#xff0c;具体xx 区分 抽象产品&#xff1a;“中式菜品” 具体产品&#xff1a;“麻婆豆腐”、“宫保鸡丁” 抽象工厂&#xff1a;“…...

adb基本命令

下载安装 adb 概述: ADB 全称为 Android Debug Bridge&#xff0c;起到调试桥的作用&#xff0c;是一个客户端-服务器端程序。其中客户端是用来操作的电脑&#xff0c;服务端是 Android 设备。 下载地址: Windows版本&#xff1a;https://dl.google.com/android/repository/pl…...

小工具实战-Python实现小工具输出字符串大小写转换、字符串统计、编解码、MD5加密

小工具实战-Python实现小工具输出字符串大小写转换、字符串统计、编解码、MD5加密 学习建议字符串大小写转换实现思路部分代码 字符串统计实现思路部分代码&#xff1a; 字符串编解码实现思路部分代码 字符串MD5加密实现思路部分代码 小工具整体设计设计思路工具完整代码实现输…...

MySQL进阶-----索引的语法与SQL性能分析

目录 前言 一、索引语法 1.SQL语法 2.案例演示 二、SQL性能分析 三、慢查询日志 1.开启日志 2.测试样例 四、profile详情 1.开启profile 2.profile测试SQL语句 五、explain详情 1.语法结构 2.执行顺序示例&#xff08;id&#xff09; 3.执行性能示例(type) 前言 本…...

Ansible剧本playbooks详解

一、playbook简介 playbook是ansible用于配置&#xff0c;部署和管理托管主机剧本&#xff0c;通过playbook的详细描述&#xff0c;执行其中一系列tasks&#xff0c;playbook字面意思是剧本&#xff0c;现实中由演员按剧本表演&#xff0c;在ansible中由计算机进行安装&#x…...

vue3封装Element导航菜单

1. 导航外层布局 AsideView.vue <template><el-menu:default-active"defaultActive"class"my-menu":collapse"isCollapse":collapse-transition"false"open"handleOpen"close"handleClose"><menu…...

字符串的函数

头文件 # include <string.h> 五大函数&#xff1a; strlen()、strcpy、strcat()、strcmp()、strstr() 用法&#xff1a; strlen()&#xff1a;计算字符串长度&#xff0c;但不计\0这个字符 #include <string.h> int main() {char arr[] "abcdef"…...

Linux安装redis(基于CentOS系统,Ubuntu也可参考)

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; 一、下载并解压Redis 1、执行下面的命令下载redis&#xff1a;wget https://download.redis.io/releases/redis-6.2.6.tar.gz 2、解压redis&#xff1a;tar xzf redis-6.2.6.tar.gz 3、移动redis目录&a…...

ChatGPT引领量化交易革命:AI在金融创新的浪潮中崭露头角

随着科技的飞速发展,金融领域正迎来一场前所未有的创新浪潮。在这场变革中,ChatGPT凭借其卓越的自然语言处理能力和深度学习能力,正引领量化交易进入新时代。 量化交易,作为现代金融领域的一种重要交易方式,依赖于复杂的数学模型和大量的历史数据来制定交易策略。然而,传…...

无忧微服务:如何实现大流量下新版本的发布自由

作者&#xff1a;项良、十眠 微服务上云门槛降低&#xff0c;用好微服务才是关键 据调研数据显示&#xff0c;约 70% 的生产故障是由变更引起的。在阿里云上的企业应用如茶百道、极氪汽车和来电等&#xff0c;他们是如何解决变更引起的稳定性风险&#xff0c;实现了在白天高流…...

Halcon3D表面平面度检测-平面差值法

//倾斜平面矫正 https://blog.csdn.net/m0_51559565/article/details/137146179 //平面度和平面缺陷检测&#xff0c;平面矫正法 https://blog.csdn.net/m0_51559565/article/details/137163729前言 通常我们对表面平面度进行检测时&#xff0c;通常使用2种方式。1&#xff1a…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...