codewhy的Vue3学习笔记-组件化开发
父子组件间的通信
- 父组件 => 子组件 :通过props属性
- 子组件 => 父组件: 通过$emit触发事件
父传子
我们可以通过 props 来完成组件之间的通信
props是什么?
- props 是你可以在组件上注册一些自定义的 attribute
- 父组件可以给这些 attribute 赋值,子组件通过 attribute 的名称获取到对应的值
props有两种常见的用法:
- 方式一:字符串数组,数组中的字符串就是 attribute 的名称
- 方拾二:对象类型;对象类型我们可以在指定 props 名称的同时,指定它需要传递的类型,是否是必须的,默认值
props的数组用法
props的对象用法
数组用法只能传入 attribute 的名称,并不能对形式做限制和传入默认值
- 指定传入的 attribute 的类型
- 指定传入的 attribute 是否是必传的
- 指定没有传入时,attribute 的默认值
对象类型的其他写法
非Prop的Attribute
当我们传递给一个组件某个属性但是该属性没有定义对应的props或者emits时,就称之为非props的Attribute
例如常见的:class , style , id
Attribute继承
当组件有单个根节点时,非 Prop 的 Attribute 将自动添加到根节点的 Attribute 中
禁用Attribute继承和多根节点
如果我们不希望组件的根元素继承 attribute,可以在组件中设置 inheritAttrs : false
我们可以通过 $attrs 来访问所有的 非 props 的 attribute
子传父
前置知识
- emits
emits可以是数组或者对象,从组件触发自定义事件,对象可以进行参数验证
如何实现
- 首先,我们需要在子组件中定义好在某些情况下触发的事件名称;
- 其次,在父组件中以 v-on 的方式传入要监听的事件名称,并绑定到对应的方法中
- 最后在子组件中发生某个事件的时候,根据事件名称触发对应的事件
自定义事件
自定义事件我理解的是:子组件传值给父组件的时候,父组件可以自定义事件名称例如 @customName 然后子组件就可以通过 emits 来接收到这个自定义事件并调用它,通过 this.$emits("customName",..args)args 是父组件中的这个自定义事件所需要的参数,可以传多个
非父子组件的通信
在我们实际开发中还有很多非父子组件的通信
主要有两种方式:
- Provide/Inject
- Mitt 全局事件总线
Provide 和 Inject
- 比如有一些深度嵌套的组件,子组件想要获取父组件的部分内容;
- 如果我们任然将 props 沿着组件链逐级传递下去,就会非常的麻烦;
- 对于这种情况,我们可以使用 Provide 和 Inject:
- 无论层级结构有多深,父组件都可以作为其所有子组件的依赖提供者
- 父组件有一个 provider 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据
- 实际上,你可以将依赖注入看成 'lang range props' 除了:父组件不需要知道哪些子组件使用它 provide 的property,子组件不需要知道 inject 的 property 来自哪里

全局事件总线mitt库
Vue3 从实例中移除了 $on 、$off 和 $once 方法,如果我们希望继续使用全局事件总线 Vue3 推荐使用 mitt 库
不是很理解,不知道实际使用场景
插槽Slot
使用场景:
在开发中,我们会经常封装一个个可复用的组件:
- 前面我们会通过props传递给组件一些数据,让组件进行展示
- 但是为了让组件有更强的通用性,我们不能将组件的内容限制为固定的div,span等等元素
- 比如某种情况下我们使用组件,希望组件显示的是一个按钮,换一种情况组件显示的说一张图片,
- 我们应该让使用者可以决定莫一块区域到底存放什么内容和元素
举个栗子:假如我们定制一个通用的导航组件 -NavBar
- 这个组件分为三个区域,左边 - 中间 - 右边

- 但是你看,左边可能显示一个菜单图标,也可能显示一个返回按钮,可能还是什么都不显示;中间和右边同理
使用插槽slot
重要提醒:
插槽的使用其实就是抽取共性,预留不同;我们将共同的元素,内容依然组件内进行封装;同时将不同的元素使用slot 作为占位符,让使用这个组件的实例来定义 slot 内的内容
vue 中将 <slot> 元素作为承载分发内容的出口;在封装组件中,使用特殊的元素 <slot> 就可以为封装组件开启一个插槽,插槽内的内容完全取决于其实例组件
插槽的默认内容
有时候我们希望在使用插槽时,如果没有对应的内容,那么我们需要显示一个默认的内容,这个默认的内容只会在没有插入内容时候才会显示
多个插槽效果
如果一个组件中含有多个插槽,但是元素我们插入多个内容,我们会发现每个插槽都会插入所有的内容,为了让内容插到对应的插槽就有了具名插槽
具名插槽就是给插槽起一个名字 <slot> 元素就有了一个特殊的 attribute:name , 一个不带 name 的 slot 会含有隐式的名字 default , 它的效果是:
动态插槽名
- 目前我们使用的插槽名称都是固定的;例如:v-slot:left, 这里这个 left 就是写死的,我们可以通过 v-slot:[dynamicSlotName],
具名插槽的缩写:即把v-slot:替换为#
渲染作用域
在vue中有渲染作用域的概念:
- 父模板中的所有内容都是在父级作用域中编译;子模板也是一样;其实很好理解;例如我们无法在父模板中访问或者使用子模板的data变量或者方法
作用域插槽
从上面可知,父子作用域独立,但是有时候我们希望插槽可以访问到子组件中的内容,这个是很常见的需求
- 当一个组件被用来渲染一个数组元素时,我们使用插槽,并且希望插槽中没有显示每项的内容,这时候就需要作用域插槽
如果还有其他的具名插槽, 那么默认插槽也必须使用 template 来编写
独占默认插槽的缩写
如果我们的插槽是默认插槽default,那么在使用的时候 v-slot:default="slotProps" 可以简写为 v-slot="slotProps"
默认插槽和具名插槽混合使用:使用 v-slot:why(一个名称其他也行)来定义具名插槽,使用 v-slot="slotProps"来使用默认插槽
动态组件的使用
动态组件是使用 component 组件,通过特殊的 attribute is 实现
认识 keep-alive
产生背景
在默认情况下,我们在切换组件时候,about组件会被销毁掉,再次回来时会重新创建组件,但是我们在开发中我们希望继续保持组件的状态,而不是销毁掉,这个时候我们可以使用一个内置组件:keep-alive
keep-alive属性
- include : string | RegExp | Array。只有在名称匹配的组件会被缓存
- exclude : string | RegExp | Array。任何匹配的组件都不会被缓存
- max : number | string。最多可以缓存多少组实例,一旦达到这个数字,那么缓存中最近没有被访问的实例被销毁
缓存组件的生命周期
对于缓存的组件来说,再次进入时,我们是不会执行created或者mounted等生命周期函数的
- 但是我们有时候希望监听何时重新进入和离开这个组件
- 这时候我们可以使用activated和deactivated来监听;
vue实现异步组件
1. 产生背景:
当我们项目过大了,对于某些组件我们希望可以通过异步的方式来进行加载(目的是可以对其分包处理,也就是让 wabpack 对其进行单独打包)
2. 使用
vue 给我们提供了一个函数:defineAsyncComponent 它接收两种类型参数,
- 工厂函数,该工厂函数需要返回一个 Promise 对象
- 接收一个对象类型,对异步函数进行配置
异步组件和 Suspense
注意: 这是一个实验性的特性,API可能随时修改
Suspense 是一个内置的全局组件,该组件有两个插槽:
- default: 如果 default 可以显示,那么显示 default 内容
- fallback:如果 default 无法显示 那么会显示 fallback 插槽的内容
$ref的使用
使用场景:
在某些情况下,我们组件想要直接获取到元素对象或者子组件示例:我们可以给元素绑定一个 ref 的 attribute 属性
组件实例有一个 $refs 属性:
它是一个对象,持有注册过 ref attribute 的所有DOM元素和组件实例
生命周期
定义:
每个组件都会经历从创建,挂载,更新,卸载等一系列的过程。在这些过程的某个阶段,可能会想要添加一些属于自己的代码逻辑,(比如请求数据)这个时候就需要 Vue 给我们提供的生命周期函数来实现
使用:
生命周期函数就是一些钩子函数,在某个时间会被 Vue 源码内部进行回调,我们调用生命周期函数在里面写自己的逻辑,这样就可以实现页面在加载完成同时做完我们的事情
组件的v-model
总结:
父组件需要在使用的子组件中写 attribute ; v-model 值为一个对象,当我们把这个值传入子组件后,子组件就可以通过 $emit 来实现在子组件中直接修改父组件中的值,并响应式更新
具体实现是通过子组件的 computed 的 get 接收父组件传来的值和 set 修改父组件传来的值,使用 emits 来声明我们 this.$emit 所需要的第一个参数,通常是这样的 update:modelValue
子组件绑定多个属性(使用 v-model )
需求:
我们希望在一个组件上使用多个 v-model 来绑定多个属性
我们知道,默认情况下的 v-model 其实是绑定了 modelValue 属性和 @update:modelValue 事件,当我们需要给一个组件绑定多个属性时,可以这样写 v-model:num='data' 其中 num 是传给子组件 props 接收参数的名称,data 是传过去的父组件的 data 中定义的数据
Mixin
产生背景:
我们在实际开发中,会遇到组件和组件之间有时候会存在相同的代码逻辑,我们希望对相同的代码逻辑进行抽取,这样就不用重复书写,那么我们就可以使用Mixin来实现
使用:
Mixin 提供了一种非常灵活的方式,来分发 Vue 组件中可复用功能,
一个Mixin对象可以包含任何组件选项;
当组件使用 Mixin 对象时,所有 Mixin 对象的选项将被混合进入该组件本身的选项中;
Mixin的合并规则
如果 Mixin 对象中的选项和组件对象中的选项发生冲突,vue会这样操作
- 如果是data函数的返回值对象
- 返回值对象默认会进行合并
- 如果data返回值对象的属性发生了冲突,那么会保留组件自身的数据
- 如果是生命周期钩子函数
- 钩子函数会被合并到数组中,都会被调用;
- 值为对象的选项,例如 methods,components, directives, 将会合并为一个对象
- 例如methods选择,如果都定义了方法,那么就会合并,但是如果对象的key相同,那么就会取组件对象的键值对
全局混入Mixin
如果组件中某些选择,是所有组件都需要的,那么我们可以使用全局Mixin
- 全局 Mixin 可以使用应用 app 的方法 mixin 来完成注册
- 一旦注册,那么全局混入的选项将会影响每一个组件

