学习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插入到组件中;

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构:基于数组实现栈

1 前言 栈是一种先进后出的线性表。向一个栈插入新元素可以叫做进栈、入栈、压栈&#xff0c;新元素必须放到栈顶元素上面&#xff0c;使之成为新的栈顶&#xff1b;从一个栈删除元素可以叫做出栈、退栈&#xff0c;它将栈顶元素删除&#xff0c;使和原来栈顶元素相邻的元素称…

RabbitMQ 实验消费原始队列消息, 拒绝(reject)投递死信交换机过程

如果你想通过 RabbitMQ 的死信队列功能实现消费者拒绝消息投递到死信交换机的行为&#xff0c;你可以按照以下步骤操作&#xff1a; 创建原始队列&#xff0c;并将其绑定到一个交换机上&#xff1a; export RABBITMQ_SERVER127.0.0.1 export RABBITMQ_PORT5672 export RAB…

Apache Hive的基本使用语法(二)

Hive SQL操作 7、修改表 表重命名 alter table score4 rename to score5;修改表属性值 # 修改内外表属性 ALTER TABLE table_name SET TBLPROPERTIES("EXTERNAL""TRUE"); # 修改表注释 ALTER TABLE table_name SET TBLPROPERTIES (comment new_commen…

Tomcat配置https

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; 一、证书 CA证书申请下载不赘述了。 二、上传证书 进入tomcat根目录&#xff0c;conf同级目录下创建cert文件夹&#xff0c;并将证书两个文件上传到该文件夹&#xff1b; 三、编辑conf/server.xml文件 ① …

20221124 kafka实时数据写入Redis

一、上线结论 实现了将用户线上实时浏览的沉浸式视频信息&#xff0c;保存在Redis中这样一个功能。为实现沉浸式视频离线推荐到实时推荐提供了强有力的支持。目前只是应用在沉浸式场景&#xff0c;后续也能扩展到其他所有场景。用于两个场景&#xff1a;&#xff08;1&#xf…

WPF自定义Panel:让拖拽变得更简单

在 WPF 应用程序中&#xff0c;拖放操作是实现用户交互的重要组成部分。通过拖放操作&#xff0c;用户可以轻松地将数据从一个位置移动到另一个位置&#xff0c;或者将控件从一个容器移动到另一个容器。然而&#xff0c;WPF 中默认的拖放操作可能并不是那么好用。为了解决这个问…

Vue中v-for多个Echarts图表组件只渲染一个要素问题排查

这个系列主要是用于记录我日常工作中遇到的一些Bug,既属于知识分享&#xff0c;也是对学习习惯的维持… 问题描述 今天&#xff0c;在开发一个WebGIS大屏项目时&#xff0c;我遇到了多个三维Echarts饼图图表渲染的问题&#xff0c;因为相似图表很多&#xff0c;我决定将Echart图…

Unity3d使用Jenkins自动化打包(Windows)(二)

文章目录 前言一、Unity工程准备二、Unity调取命令行实战一实战二实战三实战四实战五 总结 前言 自动化打包的价值在于让程序员更轻松地创建和管理构建工具链&#xff0c;提高编程效率&#xff0c;将繁杂的工作碎片化&#xff0c;变成人人&#xff08;游戏行业特指策划&#x…

CDH集群hive初始化元数据库失败

oracle数据库操作&#xff1a; 报错如下&#xff1a;命令 (Validate Hive Metastore schema (237)) 已失败 截图如下&#xff1a; 后台日志部分摘录&#xff1a; WARNING: Use “yarn jar” to launch YARN applications. SLF4J: Class path contains multiple SLF4J binding…

tdesign坑之EnhancedTable树形结构默认展开所有行

⚠️在官方实例中&#xff0c;树形结构的表格提供了2种方法控制展开全部节点&#xff1a; 一是通过配置属性tree.defaultExpandAll为true代表默认展开全部节点&#xff08;仅默认情况有效&#xff09;&#xff1b; 二是使用组件实例方法expandAll()可以自由控制树形结构的展开…

VESTA模拟计算XRD标准卡片

先上Crystallography Open Database网站下载标准CIF卡片&#xff08;以PbI2为例&#xff09; 1.直接进网站搜元素就行 2.点CIF直接下载 3.打开VESTA&#xff0c;导入刚刚下载的CIF 4.导入成功就是这样的 5.按照我这个操作来计算 6.点Calculation 7.已经计算出来了&#xff…

Jmeter参数化 —— 循环断言多方法

1、参数化接口测试数据 注意&#xff1a;csv文档参数化&#xff0c;里面有多少条数据&#xff0c;就要在线程组里循环多少次&#xff0c;不然就只执行一次 2、添加配置元件-计数器 关于计数器&#xff1a; ①Starting Value&#xff1a;给定计数器的初始值; ②递增&#xff1a…