vue3(三)——组件
文章目录
Vue3组件化开发(一)
认识组件的嵌套
前面我们是将所有的逻辑放到一个App.vue中
- 在之前的案例中,我们只是
创建了一个组件App
; - 如果我们一个应用程序
将所有的逻辑都放在一个组件
中,那么这个组件就会变成非常的臃肿和难以维护
; - 所以组件化的核心思想应该是
对组件进行拆分
,拆分成一个个小的组件
; - 再
将这些组件组合嵌套在一起
,最终形成我们的应用程序
我们来分析一下下面代码的嵌套逻辑,假如我们将所有的代码逻辑都放到一个App.vue组件中:
我们来分析一下下面代码的嵌套逻辑,假如我们将所有的代码逻辑都放到一个App.vue组件中:
- 我们会发现,将所有的代码逻辑全部放到一个组件中,代码是非常的臃肿和难以维护的。
- 并且在真实开发中,我们会有更多的内容和代码逻辑,对于扩展性和可维护性来说都是非常差的。
- 所以,在真实的开发中,我们会对组件进行拆分,拆分成一个个功能的小组件。
组件的拆分
我们可以按照如下的方式进行拆分
按照如上的拆分方式后,我们开发对应的逻辑只需要去对应的组件编写就可。
组件的通讯
上面嵌套逻辑如下,他们存在如下关系:
App组件是Header、Main、Footer组件的父组件;
Main组件是Banner、ProductList组件的父组件;
在开发过程中,我们经常遇到组件之间相互进行通信
- 比如
App可能使用了多个Header
,每个地方的Header展示的内容不同
,那么我们就需要使用者传递给Header一些数据
,让其进行展示; - 又比如我们在Main中一次性
请求了Banner数据和ProductList数据
,那么就需要传递给它们
来进行展示; - 也可能是
子组件中发生了事件
,需要由父组件来完成某些操作
,那就需要子组件向父组件传递事件
;
父子组件之间通信的方式
父子组件之间如何进行通信呢?
- 父组件传递给子组件:
通过props属性
; - 子组件传递给父组件:
通过$emit触发事件
;
- 在开发中很常见的就是父子组件之间通信,比如父组件有一些数据,需要子组件来进行展示:
- 这个时候我们可以
通过props来完成组件之间的通信
;
- 这个时候我们可以
- 什么是Props呢?
- Props是你可以在组件上
注册一些自定义的attribute
; - 父组件给
这些attribute赋值,子组件通过attribute的名称获取到对应的值
;
- Props是你可以在组件上
- Props有两种常见的用法:
-
方式一∶
字符串数组
,数组中的字符串就是attribute的名称;
-
方式二∶
对象类型
,(对象里面放的是键值对)对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、默认值等等;
区别(大多数使用对象的写法)
-
- 数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制,接下来我们来看一下对象的写法是如何让我们的props变得更加完善的。
- 当使用对象语法的时候,我们可以对传入的内容限制更多∶
- 比如指定传入的
attribute的类型;
- 比如指定传入的
attribute是否是必传的
; - 比如指定没有传入时,
attribute的默认值
;
细节一:那么type的类型都可以是哪些呢?
- 比如指定传入的
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
细节二:对象类型的其他写法
** 细节三:Prop的大小写命名**
Prop的大小写命名(camelCase vs kebab-case) - HTML中的
attribute名是大小写不敏感
的,所以浏览器会把所有大写字符解释为小写字符
; - 这意味着当你
使用DOM中的模板
时,camelCase (驼峰命名法)的prop名需要使用其等价的 kebab-case(短横线分隔命名)命名
;
非Prop的Attribute
什么是非Prop的Attribute呢?
- 当我们
传递给一个组件某个属性
,但是该属性并没有定义对应的props或者emits时
,就称之为非Prop的Attribute
; - 常见的包括class、style、id属性等;
Attribute继承
当组件有单个根节点
时,非Prop的Attribute将自动添加到根节点的Attribute
中:
禁用Attribute继承和多根节点
- 如果我们不希望组件的根元素继承attribute,可以在组件中设置inheritAttrs: false :
- 禁用attribute继承的
常见情况
是需要将attribute应用于根元素之外的其他元素
; - 我们可以通过
$attrs来访问所有的非props的attribute
;
- 多个根节点的attribute
多个根节点的attribute如果没有显示的绑定
,那么会报警告,我们必须手动的指定要绑定到哪一个属性
上:
子组件传递给父组件
什么情况下子组件需要传递内容到父组件呢?
- 当
子组件有一些事件发生
的时候,比如在组件中发生了点击,父组件需要切换内容; - 子组件
有一些内容想要传递给父组件
的时候;
我们如何完成上面的操作呢? - 首先,我们需要在
子组件中定义好在某些情况下触发的事件名称
; - 其次,在
父组件中以v-on的方式传入要监听的事件名称
,并且绑定到对应的方法中; - 最后,在子组件中发生某个事件的时候,
根据事件名称触发对应的事件
;\
自定义事件的流程
- 我们封装一个CounterOperation.vue的组件∶
- 内部其实是监听两个按钮的点击,点击之后通过this.$emit的方式发出去事件;
- 内部其实是监听两个按钮的点击,点击之后通过this.$emit的方式发出去事件;
自定义事件的参数和验证
- 自定义事件的时候,我们也可以传递一些参数给父组件:
- 在vue3当中,我们可以对传递的参数进行验证:
非父子组件的通信
- 在开发中,我们构建了组件树之后,除了父子组件之间的通信之外,还会有非父子组件之间的通信。
- 这里我们主要讲两种方式:
Provide/Inject
;Mitt全局事件总线
;
Provide和Inject
Provide/Inject用于非父子组件之间共享数据∶
- 比如有
一些深度嵌套的组件
,子组件想要获取父组件的部分内容
; - 在这种情况下,如果我们仍然
将props沿着组件链逐级传递
下去,就会非常的麻烦;
对于这种情况下,我们可以使用Provide和 Inject : - 无论层级结构有多深,父组件都可以作为其所有子组件的
依赖提供者
; - 父组件有一个
provide选项
来提供数据; - 子组件有一个
inject选项
来开始使用这些数据;
实际上,你可以将依赖注入看作是“long range props”,除了∶ - 父组件不需要知道哪些子组件使用它provide的 property
- 子组件不需要知道inject的 property来自哪里
Provide和Inject基本使用
我们开发一个这样的结构
Provide和Inject函数的写法:
如果Provide中提供的一些数据是来自data,那么我们可能会想要通过this来获取∶
处理响应式数据
-
我们先来验证一个结果∶如果我们修改了this.names的内容,那么使用length的子组件会不会是响应式的?
-
我们会发现对应的子组件中是没有反应的:
- 这是因为当我们
修改了names之后
,之前在provide中引入的this.names.length 本身并不是响应式
的;
- 这是因为当我们
-
那么怎么样可以让我们的数据变成响应式的呢?
- 非常的简单,我们可以使用
响应式的一些API
来完成这些功能,比如说computed函数
; - 当然,
这个computed是vue3的新特性
;
注意∶我们在使用length的时候需要获取其中的value
- 非常的简单,我们可以使用
-
这是因为computed返回的是一个ref对象,需要取出其中的value来使用;
全局事件总线mitt库
Vue3从实例中移除了 o n 、 on、 on、off和$once方法,所以我们如果希望继续使用全局事件总线,要通过第三方的库:
- Vue3官方有推荐一些库,例如
mitt或tiny-emitter
; - 这里我们主要讲解一下
mitt库
的使用;
首先我们需要安装这个库:
npm install mitt
其次,我们可以封装一个工具eventbus.js
使用事件总线工具
在项目中可以使用它们:
- 我们在Home.vue中监听事件;
- 我们在App.vue中触发事件;
Mitt的事件取消
在某些情况下我们可能希望取消掉之前注册的函数监听
认识插槽Slot
- 前面我们会
通过props传递
给组件—些数据,让组件来进行展示; - 但是为了让这个组件具备
更强的通用性
,我们不能将组件中的内容限制为固定的div、span
等等这些元素; - 比如某种情况下我们使用组件,希望组件显示的是
一个按钮
,某种情况下我们使用组件希望显示的是一张图片
; - 我们应该让使用者可以决定
某一块区域到底存放什么内容和元素
;
举个栗子∶假如我们定制一个通用的导航组件-NavBar
- 这个组件分成三块区域:左边-中间-右边,每块区域的内容是不固定;
- 左边区域可能显示一个菜单图标,也可能显示一个返回按钮,可能什么都不显示;
- 中间区域可能显示一个搜索框,也可能是一个列表,也可能是一个标题,等等;
- 右边可能是一个文字,也可能是一个图标,也可能什么都不显示;
这个时候我们就可以来定义插槽slot:
- 插槽的使用过程其实是
抽取共性、预留不同
; - 我们会将
共同的元素、内容依然在组件内
进行封装; - 同时会将
不同的元素使用slot作为占位
,让外部决定到底显示什么样的元素;
如何使用slot呢?
- Vue中将
<slot>元素作为承载分发内容
的出口﹔ - 在封装组件中,使用
特殊的元素<slot>
就可以为封装组件开启一个插槽
; - 该插槽
插入什么内容取决于父组件
如何使用;
插槽的基本使用
- 我们一个组件MySlotCpn.vue :该组件中有一个插槽,我们可以在插槽中放入需要显示的内容;
- 我们在App.vue中使用它们:我们可以插入普通的内容、html元素、组件元素都可以是可以的;
插槽的默认内容
有时候我们希望在使用插槽时,如果没有插入对应的内容,那么我们需要显示一个默认的内容: - 当然这个默认的内容只会在没有提供插入的内容时,才会显示;
多个插槽的效果
我们先测试一个知识点︰如果一个组件中含有多个插槽,我们插入多个内容时是什么效果? - 我们会发现默认情况下每个插槽都会获取到我们插入的内容来显示;
![在这里插入图片描述](https://img-
blog.csdnimg.cn/5f3d3ede182443e997dec6118843270e.png)
具名插槽的使用
- 事实上,我们希望达到的效果是插槽对应的显示,这个时候我们就可以使用具名插槽︰
- 具名插槽顾名思义就是
给插槽起一个名字
,<slot>元素有一个特殊的attribute : name
; 一个不带name 的slot,会带有隐含的名字defaut
;
- 具名插槽顾名思义就是
动态插槽名
什么是动态插槽名呢?
- 目前我们使用的插槽名称都是固定的;比如v-slot:left、v-slot:center等等;
- 我们可以通过v-slot:[dynamicSlotName]方式动态绑定一个名称;
剧名插槽使用时候的缩写
跟v-on和v-bind一样,v-slot也有缩写;即把参数之前的所有内容(v-slot:)替换为字符#;
渲染作用域
在Vue中有渲染作用域的概念:
- 父级模板里的所有内容都是
在父级作用域中编译
的; - 子模板里的所有内容都是
在子作用域中编译
的;
如何理解这句话呢?我们来看一个案例: - 在我们的案例中ChildCpn自然是可以访问自己作用域中的title内容的;
- 但是在App中,是访问不了ChildCpn中的内容的,因为它们是跨作用域的访问
认识作用域插槽
但是有时候我们希望插槽可以访问到子组件中的内容是非常重要的:
- 当一个组件被用来渲染一个
数组元素
时,我们使用插槽
,并且希望插槽中没有显示每项的内容
; - 这个Vue给我们提供了
作用域插槽
;
更多推荐
所有评论(0)