【Vue3 基础】05.组件化

这是 Vue3 + Vite + Pinia +TS + Element-Plus 实战系列文档。最近比较忙没什么时间写文章,争取早日把这个系列完结吧~

生命周期和模板引用

在本章之前,我们通过响应式 api 和声明式渲染,处理了 DOM 的更新,但光是这些,对于一些复杂的需要手动操作 DOM 的情况,之前介绍的就无法满足了。

生命周期

每个 Vue 组件在创建时经历的一系列初始化步骤的阶段,我们需要在这些阶段做额外操作的话,需要调用对应阶段的钩子。

这些阶段包括:设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM 等。Vue 官方给出了图示,可以帮助我们更好的理解生命周期:

  1. setup:红色的所有生命周期 API 都在组件的 setup() 阶段被同步调用。
  2. 红色方框:不同阶段代表的生命周期,后续我们写的 生命周期钩子 会在此阶段执行。
  3. 主轴上的:代表组件从初始化到卸载的主要事件。

这里我们先简单介绍,在介绍完生命周期钩子之后,相信你会更理解这张图。

生命周期钩子

了解了上述的生命周期,我们想在对应的周期做一些事情的话,在 Vue3 中我们使用 onXxx 的生命周期钩子,例如:

如果你使用过 Vue2 的话,你会发现差别:

使用方法就如前面写的,在 setup 中,在生命周期 API 中注入回调就可以了。这里我们就不去做 Vue2 和 Vue3 的对比了,全当新学的,按照生命周期的顺序:

  1. setup:beforeCreate 和 created 被 setup 方法代替。
  2. onBeforeMount() :在组件被挂载之前执行回调。组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。
  3. onMounted() :在组件挂载完成后执行回调。通常用于执行需要访问组件所渲染的 DOM 树相关的副作用。
  4. onBeforeUpdate() :在组件即将因为响应式状态变更而更新其 DOM 树之前执行回调。通常用来在 Vue 更新 DOM 之前访问 DOM 状态。
  5. onUpdated() :在组件因为响应式状态变更而更新其 DOM 树之后执行回调。会在组件的任意 DOM 更新后被调用,一般用来访问更新后的 DOM,不能在此做更新 DOM 的操作,可能导致循环。
  6. onBeforeUnmount() :在组件实例被卸载之前执行回调。
  7. onUnmounted() :在组件实例被卸载后执行回调。通常用于手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。
  8. onActivated() :当作为 keep-alive 组件被激活时执行回调。
  9. onDeactivated() :当作为 keep-alive 组件被取消激活时执行回调。
  10. onErrorCaptured() :在捕获了后代组件传递的错误时执行回调。通常用来更改组件状态来为用户显示一个错误状态。
  11. onRenderTracked() 仅开发模式使用 :当组件渲染过程中追踪到响应式依赖时执行回调。通常用于追踪依赖的调试。
  12. onRenderTriggered() 仅开发模式使用 :当响应式依赖的变更触发了组件渲染时执行回调。通常用于触发更新的调试。

以上就是所有的生命周期以及其可被调用的生命周期钩子,我们在上述钩子中传递回调,Vue 会在其所在的生命周期触发。

模板引用 ref

ref 用于注册元素或者子组件的引用。

模板引用将存储在与名字匹配的 ref 中,例如想在数据加载完之后,更改文字信息或描述信息:

当然我们也完全可以让上述的代码中 h2 变成响应式的,并在h2中使用 {{}} 模板语法实现。

组件引用ref

ref 也可以使用在子组件上,相对来说也是较为常见的用法。

首先解释什么是组件:

在此之前我们都是使用的一个单文件App.vue,如果一个项目将代码全写在这一个文件,那将非常难维护,于是我们把可复用等的页面组件化,页面和逻辑抽离,通过导入组件被其它页面引用。

使用 ref,父组件能获取子组件示例:

需要注意的是,子组件没有使用

如果使用

当然在大部分情况下,使用 props 和 emit 就能实现父子组件的交互,而无需使用 ref。

组件传值 props

组件之间传值的方式主要可以概括为这三类:父子组件传值、兄弟组件传值和远亲组件传值。

Vue 提供给我们组件传值的api有两种:props、emit。其中我们可以通过 props 进行父=>子组件传值。

在开发过程中,我们需要通过 defineProps() 明确子组件的 props。父组件可以像声明 HTML 参数一样传值,也可以使用 : (v-bind 简写) 动态传值:

defineProps() 声明之后,其中的数据就可以在子组件模板中使用。在 JavaScript 中访问则需要通过 defineProps() 返回的对象访问。

注意:

  1. props 是只读的,遵循单项数据流,当尝试修改 props 会警告 prop 只读。
  2. js 中定义数据与 props 中重名时,使用的是 js 中定义的。

const activeMsg = "hl";

activeMsg: {{ activeMsg }}

  1. props 提供校验选项,保证项目没有使用 TypeScript 进行类型检测,也可以确定一定的数据类型,避免不不满足类型要求的数据传入。(现在 Vue3 支持 TypeScript 可以说这个用处不大了)

interface DataProps {

msg: String;

activeMsg: String;

}

const props = defineProps();

组件监听事件 emit

子组件使用 emit() 向父组件传递数据。第一个参数是事件名称,其它额外参数都会被直接传向父组件的监听器函数。

父组件使用 @ (v-on)监听子组件时间,并且可以接收子组件传递的参数。

注意:

  1. 在模板中可以使用 $emit 的语法,js中只能使用 defineEmits 返回对象 emit 触发事件。
  2. 可以使用类型标注触发事件,对触发的事件有更精准的控制。

const emit = defineEmits<{

(e: 'response', msg: string): void

}>()

传递模板-插槽 slot

除了传递数据外,父组件还可以通过插槽 slot 的方式将模板传递给子组件。

默认内容

如果想设置默认内容的话,比如在父组件不向子组件传递模板字符,而使用子组件按钮内容有默认 content:

具名插槽

如果组件包含多个插槽出口,则需要使用具名插槽,用来给插槽一个唯一 ID,以确定不同出口要渲染的内容。

v-slot 可以简写为 # ,v-slot:header => #header。并且v-slot 也可以接受动态参数(动态插槽名): #[dynamicSlotName] 。

作用域插槽

上述的几种插槽,无法访问到子组件的状态,在某些场景中我们想要子组件传递数据给插槽,作用域插槽就可以满足这个需求。

准确来说,上述例子是具名作用域插槽。如果是普通的作用域插槽,即改变 template 的具名插槽为普通插槽就可以了。

实战

前几节讲到了组件的各生命周期钩子、组件传值的 props、触发事件的 emit 以及模板插槽 slot,用的例子比较简单,我们通过实战体验一下在实战开发中的运用,帮助我们更好的理解和运用。

博客的列表展示功能,提供分类功能。

  1. 分类的数据来源于父组件,其选择的类型通过回调告知父组件。
  2. 列表部分父组件做元素内容和样式的控制,子组件做列表的基础循环等的通用操作。

示例尽可能的包含到我们这章所学知识,如下述代码部分看不懂,代表你那部分知识还没理解清楚。

本节中的例子里包含部分ts的类型确认

父组件 App.vue

父组件涉及知识点:

  1. 与 ClassifyHeader 分类组件的传值。props 和 emit。
  2. 生命周期 onMounted。在组件挂载后请求数据。
  3. 组件引用 ref 。使用子组件的方法。
  4. 具名作用域插槽。

父组件和分类组件

在父组件中,初始的 tags.list 值通过 props 传递给子组件,同时监听了 select 事件。select 触发会执行 checkedTags 函数,父组件得到 checked 值,并做出重新请求列表数据的操作。

components/ClassifyHeader.vue 分类组件:

ClassifyHeader 分类组件主要就是记录所选类别,并通过 emit 传递数据给父组件。

父组件与列表组件

父组件通过 ref 获取到子组件示例,并且使用子组件暴露的 loadData 方法加载数据。子组件通过作用域插槽,传递数据给父组件,父组件控制内容布局。

components/Children.vue 列表组件:

子组件,提供具名插槽 slot 以及用 defineExpose 抛出 loadData 方法供父组件通过组件示例拿到。

上述例子,我们就把大部分的知识都重新实战复习了一遍,建议可以把这几个代码自己写一遍,加深印象。

其它传值方式

除了上述的 props、emit 父子组件传值之外。还可以使用依赖注入 API :provide、inject。

  1. provide() :提供一个值,可以被后代组件注入。使用方式:provide(/* 注入名 / 'message', / 值 */ 'hello!') 。
  2. inject() :注入上层组件提供的数据。使用方式:const message = inject('message') 。

兄弟组件,可以可以通过父组件控制数据传值。

对于跨组件通信,我们可以使用状态管理工具,比如我们后面要学的 Pinia,就是一个状态管理框架。

总结

本章中,我们首先结合 Vue 的生命周期的流程图,例举了各个生命周期钩子的触发时机,以及部分钩子的使用场景。然后讲到了 ref 的作用,最后讲完并实战了组件通信相关的 api。

至此 Vue3 的基础知识到这里已经结束了,还剩下一小部分,留在我们实战课程中探索。

如果未学习过TypeScript的话,我也准备了 TypeScript 相关的入门基础知识,课程不会很长,带大家了解 TypeScript 常用的一些知识,为实战做准备。

相关推荐
聪明的笨猪猪4 小时前
Java Redis “持久化”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
聪明的笨猪猪5 小时前
Java Redis “核心基础”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
DIY全栈开发6 小时前
《MCU职位》面试问题
单片机·嵌入式硬件·面试
大前端helloworld8 小时前
前端梳理体系从常问问题去完善-网络篇
前端·面试
绝无仅有10 小时前
某大厂跳动Java面试真题之问题与解答总结(二)
后端·面试·github
绝无仅有10 小时前
某大厂跳动Java面试真题之问题与解答总结(三)
后端·面试·架构
大前端helloworld10 小时前
前端梳理体系从常问问题去完善-框架篇(Vue2&Vue3)
前端·javascript·面试
Emrys_13 小时前
Redis 为什么这么快?一次彻底搞懂背-后的秘密 🚀
后端·面试
聪明的笨猪猪14 小时前
Java Spring “事务” 面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
zycoder.14 小时前
力扣面试经典150题day3第五题(lc69),第六题(lc189)
算法·leetcode·面试