Vue3.0组合式API(四)-----组件化开发
目录
- 一、概述
- 二、组合API函数
- 2.1 路径配置
- 2.2 生命周期函数
- 2.3 监听函数
- 2.3.1 watch函数
- 2.3.2 watchEffect函数
- 三、父子组件通信(以setup语法糖写法)
- 3.1 基础组件
- 3.1.1 保持组件激活状态
- 3.1.2 动态选择展示组件
- 3.2 父传子数据
- 3.2.1 基本写法
- 3.2.2 props属性校验
- 3.2.3 ref、reactive、toref、toRefs浅析
- 3.3 子传父事件
- 3.4 父获取子变量、方法
- 四、插槽类子组件
- 4.1 插槽子组件
- 4.2 插槽父组件
- 4.2.1 啥都不传
- 4.2.2 全都传
- 4.3 作用域插槽
一、概述
- 指导思想:小组件组成大组件,大组件组成组件集,即页面
- 组件树:最后页面会构成一个组件树,根组件是App.vue,各个页面也为组件,注册在根组件下,并类似向下扩展
二、组合API函数
2.1 路径配置
- style中调用
- 引用外部样式文件:
@import '~@/assets/css/base.css'; - 注意:句尾有分号,引用有波浪线
- 引用外部样式文件:
- script中调用
- 写法:
import Home from '@/views/Home/Home.vue' - 注意:句尾无分号,引用无波浪线
- 写法:
- setup语法糖:
- 写法:
- 功能:默认将script标签里的所有函数和变量都导出给模板用,组件导入无需注册即可使用
- 写法:
2.2 生命周期函数
-
生命周期钩子
选项式 API beforeCreate setup内默认 created setup内默认 beforeMount onBeforeMount mounted onMounted beforeUpdate onBeforeUpdate updated onUpdated beforeUnmount onBeforeUnmount unmounted onUnmounted errorCaptured onErrorCaptured renderTracked onRenderTracked renderTriggered onRenderTriggered activated onActivated deactivated onDeactivated -
Home.vue演示代码(常规)
<template><button @click="test">{{ title }}</button> </template><script lang="ts"> import {defineComponent,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,ref, } from "vue";export default defineComponent({name: "home",setup() {const title = ref("标题");const test = () => {title.value = "更新";};onBeforeMount(() => {console.log("onBeforeMount执行");});onMounted(() => {console.log("onMounted执行");});onBeforeUpdate(() => {// 数据更新才会执行console.log("onBeforeUpdate执行");});onUpdated(() => {// 数据更新才会执行console.log("onUpdated执行");});onBeforeUnmount(() => {console.log("onBeforeUnmount执行");});onUnmounted(() => {console.log("onUnmounted执行");});return { test, title };}, }); </script> -
setup语法糖写法
<template><button @click="test">{{ title }}</button> </template><script lang="ts" setup>// 注意上面标签里加了setup,下面无return和exportimport {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,ref,} from "vue";const title = ref("标题");const test = () => {title.value = "更新";};onBeforeMount(() => {console.log("onBeforeMount执行");});onMounted(() => {console.log("onMounted执行");});onBeforeUpdate(() => {console.log("onBeforeUpdate执行");});onUpdated(() => {console.log("onUpdated执行");});onBeforeUnmount(() => {console.log("onBeforeUnmount执行");});onUnmounted(() => {console.log("onUnmounted执行");}); </script> -
效果图

-
具体调用时机

2.3 监听函数
- 功能:监控变量值,若变化,则做相应处理
2.3.1 watch函数
Home.vue演示代码
- 基本数据类型
<template><h1>{{ age }}</h1><button @click="add">增加年龄</button><button @click="stop">停止监控</button> </template> /****************监控ref对象(常规写法)***********************/ <script lang="ts">import { defineComponent, ref, watch } from "vue";export default defineComponent({name: "home",setup() {const age = ref(18);const add = () => age.value++;// watch传入:监控项、回调函数const stop = watch(age, (cur, pre) => {console.log("旧值:" + pre);console.log("现值:" + cur);});return { age, add, stop };},}); </script>/****************监控ref对象(setup语法糖写法)***********************/ <script lang="ts" setup>import { ref, watch } from "vue";const age = ref(18);const add = () => age.value++;const stop = watch(age, (cur, pre) => {console.log("旧值:" + pre);console.log("现值:" + cur);}); </script>/****************监控多个对象(setup语法糖写法)******************/ <script lang="ts" setup>import { ref, watch } from "vue";const Name1 = ref("preDuke");const Name2 = ref("preLuck");// 此处newValues是一个数组watch([Name1, Name2], (newValues, prevValues) => {console.log(newValues, prevValues);});// 终端输出: ["Duke", "preLuck"] ["preDuke", "preLuck"]Name1.value = "Duke";// 终端输出: ["Duke", "Luck"] ["Duke", "preLuck"]Name2.value = "Luck"; </script> - 复杂数据类型
<template><h1>{{ person.age }}</h1><button @click="add">增加年龄</button><button @click="stop">停止监控</button> </template> /****************精确监控reactive对象子项(setup语法糖)***********************/ <script lang="ts" setup>import { reactive, watch } from "vue";const person = reactive({ name: "duke", age: 18 });const add = () => person.age++;const stop = watch(// 监控项:复杂数据类型子项() => person.age,// 回调函数:新、旧值,也可不传()=>{}(cur, pre) => {console.log("旧值:" + pre);console.log("现值:" + cur);}); </script> /****************监控reactive对象:仅写watch函数******************/const stop = watch(// 监控对象:reactive() => person,// 回调函数:只能获取最新值,旧值无法获取(cur) => {console.log("现值:" + cur.age);},// 可选参数:开启监控嵌套子项、首次加载页面便执行一次(默认不执行)// { deep: true, immediate: true }{ deep: true }); - 效果

2.3.2 watchEffect函数
- Home.vue演示代码
<template>template> <script lang="ts" setup>import { watchEffect, ref, reactive } from "vue";const person = reactive({name: "duke",age: 18,});// watchEffect函数特点:// 1、自动检索回调函数中的ref、reactive变量进行跟踪// 2、页面首次加载即执行一次回调函数// 名字不改变,终端不输出任何信息const namewatch = watchEffect(() => console.log(person.name));// 每秒都会执行一次// const agewatch = watchEffect(() => console.log(person.age));// 函数功能:每隔一秒,年龄增加1setInterval(() => {person.age++;}, 1000); script>
三、父子组件通信(以setup语法糖写法)
3.1 基础组件
以下为父子组件的格式
- 父组件:Home.vue
<template><h1>这是父组件h1><Child>Child> template><script lang="ts" setup>// 这里导入组件import Child from "@/views/Child.vue"; script> - 子组件:Chile.vue
<template><h2>这是子组件h2> template><script lang="ts" setup> script>
3.1.1 保持组件激活状态
- 代码示例
<keep-alive><Child>Child > keep-alive>- 功能:只要不刷新页面,切换页面,原页面的输入和选择都会保持原样(放在缓存里)
- 应用场景:当组件内包含交互式操作时,保留页面当前操作有助于提高用户体验
3.1.2 动态选择展示组件
- 父组件@/src/Home.vue
<template><h1>这是父组件h1><component :is="num % 2 == 0 ? Child : Child1">component> template><script lang="ts" setup>import { ref } from "vue";// 这里导入组件import Child from "@/views/Child.vue";import Child1 from "@/views/Child1.vue";let num = ref(0);// 设置了一个定时器,每一秒num值增加1setInterval(() => num.value++, 1000); script> - 子组件
// 文件@/views/Child.vue <template><h2>展示组件A</h2> </template> // 文件@/views/Child1.vue <template><h2>组件B</h2> </template>
3.2 父传子数据
3.2.1 基本写法
单向传值:即子组件中改变值的话,不会影响到父组件值,父组件值变动,会实时改变子组件数据
-
Home.vue
<template><h1>这是父组件h1><Child :msg="test1">Child><Child msg="第二种">Child><Child :msg="test3.name + '的ID是' + test3.id">Child><Child :msg="test3">Child> template><script lang="ts" setup>import { ref, reactive, defineComponent } from "vue";import Child from "@/views/Child.vue";let test1 = ref("第一种");let test3 = reactive({id: 1,name: "第三种",}); script> -
Child.vue
<template><div style="border: 3px solid black"><h2>这是子组件h2><h3>接收到的字符串:{{ msg }}h3>div> template><script lang="ts" setup>// 见3.2.2节详解,每个组件只能有一个definePropsdefineProps({msg: [String, Object],}); script>- router-view标签:router-view也可以传参,可以把其看成vue中一个默认组件,
- :msg=***理解:
- 等号左边:父组件传给子组件的变量名,父组件不定义,子组件做接收校验等操作
- 等号右边:拼接的字符串或者父组件中的变量,父组件需要定义
- router-view标签:router-view也可以传参,可以把其看成vue中一个默认组件,
-
效果

3.2.2 props属性校验
- 验证写法
- 类型限定:如顺序1中写法,其他类型有
String,Number,Boolean,Array,Object,Function,Promise - 详细验证:
以下msg-*均为父组件传进来的变量名,大括号中均为验证条件// 多个可能的类型 msg-A: [String, Number], // 此项必须传值,否则控制台warn msg-B: {type: String,required: true }, // 带有默认值,即父组件无:msg=***语句时,这个语句才被激活 msg-C: {type: Number,default: 100 }, // 带默认值:默认值从一个函数获取,返回一个对象(类似Python的字典) msg-D: {type: Object, default: () => {return { message: 'hello' }} }, msg-E: {// 自定义验证函数,这个值必须匹配下列字符串中的一个t-fun: (value) => {// 当验证失败的时候,(开发环境构建版本的)Vue将会产生一个控制台的警告 return ['success', 'warning', 'danger'].indexOf(value) !== -1 }
- 类型限定:如顺序1中写法,其他类型有
3.2.3 ref、reactive、toref、toRefs浅析
- 响应式实现机制
proxy对象:接收两个参数(Handler,Target)前者实现监控数据功能,后者存储数据
- 函数解释
api函数 解释 ref() const ex_ref = ref(a);,语句表示ref()函数接收传入的数据a,并返回一个RefImpl对象(其value属性为a的值),RefImpl对象与a无关联,即ex_ref的value值变化,a不变化,反之亦然reactive() const ex_reactive = reactive(b);,语句表示reactive()函数接收传入的数据b,并返回一个Proxy对象(其Target属性为b的值),Proxy对象与b无关联,即ex_reactive的值变化,b不变化,反之亦然toRef() 较少用, const a = toRef(b,"title");,语句表示将reactive对象b的子项title包装成RefImpl对象并赋值a,修改a的值响应式修改b.title的值,常用于向外部定义的函数传参,函数修改a的值的时候会同步到b中toRefs() toRefs(b);,语句表示将reactive对象b的所有子项均转换为RefImpl对象,b.子项名均为RefImpl对象,常用于解构reactive对象,如下示例toRefs示例 <template><h1>数据:{{ title }}h1> template><script lang="ts" setup> import { reactive, ref, toRefs } from "vue";const a = reactive({ title: "duke", age: 18 }); // 第一种用法:解构a,name和age均为RefImpl对象 const { title, age } = toRefs(a); console.log(title.value); // console.log(title.value); //这种解构可以直接拿任意属性 // const { age } = toRefs(a); script>
3.3 子传父事件
事件传送:通常子组件向父组件传送的是事件,父组件需要知道子组件发生了什么事件,并做相应的反应
- 父组件
<template><h1>这是父组件h1><Child @change="log" @changearg="logarg">Child> template><script lang="ts" setup>import Child from "@/views/Child.vue";const log = () => console.log("捕获到子组件事件change");// 获取子组件带参数事件的参数方法:限定参数类型const logarg = (arg: string) =>console.log("捕获到子组件事件changearg,参数:" + arg); script> - 子组件
<template><button @click="send">发送事件button><button @click="sendarg">发送带参数的事件button> template><script lang="ts" setup>// 每个组件只能有一个defineEmitslet emit = defineEmits(["change", "changearg"]);const send = () => emit("change");const sendarg = () => emit("changearg", 123); script>
3.4 父获取子变量、方法
props的逆向
-
父组件
<template><h1>这是父组件</h1><!-- 此处son是一个变量 --><Child ref="son"></Child> </template><script lang="ts" setup>import { ref } from "@vue/reactivity";import Child from "@/views/Child.vue";import { onMounted } from "@vue/runtime-core";// 变量son赋初值const son = ref(null);// 只能在挂在后才有数据onMounted(() => {// 拿到子组件的数据console.log(son.value?.msg);// 拿到子组件的方法son.value?.test();}); </script> -
子组件
<template><h1>这是子组件</h1> </template><script lang="ts" setup> import { ref } from "vue";const msg = ref("good"); const test = () => {console.log("这是子组件方法"); }; defineExpose({ msg, test }); </script>
四、插槽类子组件
- 功能:针对布局(css)相同,内容(html)不同的结构,子组件留坑(占位符),父组件来填(传入数据)
- 示例:

4.1 插槽子组件
- 基本规则
- 默认值:父组件不传,即显示默认值
- 优先级:父组件中传给具名插槽的,直接对号入座,传给不具名插槽,按顺序送入子组件不具名插槽
- 子组件写法
<template><div class='childStyle'><h1>h1插槽子组件h1><slot>slot><slot><h3>h3不具名插槽默认值h3>slot><slot name="no_1"><h4>h4具名插槽默认值h4>slot><slot name="no_2">slot> div> template>
4.2 插槽父组件
4.2.1 啥都不传
- 父组件
<template><Son>Son> template><script lang="ts" setup>import Son from '../components/son.vue' script> - 显示

4.2.2 全都传
- 父组件
<template><Son><template v-slot:no_1> <h1>父组件传给具名插槽no_1h1> template><template v-slot:no_2> <h1>父组件传给具名插槽no_2h1> template><template> <h1>父组件传给不具名插槽h1> template>Son> template><script lang="ts" setup>import Son from '../components/son.vue' script> - 显示

不具名插槽:如果父组件不传值,则各自使用各自的默认值;若传值,则所有不具名插槽将会格式化为一样的,如显示第2、3行
向不具名插槽传多个值:会被集合起来作为一个不具名插槽发送值传给子组件
4.3 作用域插槽
- 功能:父组件需要获取插槽子组件的数据,以具名插槽和不具名插槽为基础
- 子组件
<template><div class="childStyle"><slot :def1="def">不具名插槽不传值则显示此信息slot><slot name="no_1" :no1="first">具名插槽no_1不传值则显示此信息slot><slot name="no_2" :no2="second" :no3="third">具名插槽no_2不传值则显示此信息slot>div> template><script lang="ts" setup>import { ref } from 'vue';// 子组件的值const def = ref('子组件默认插槽绑定的属性值def');const first = ref('子组件具名插槽值no_1绑定的属性值first');const second = ref('子组件具名插槽no_2绑定的属性值second');const third = ref('子组件具名插槽no_2绑定的属性值third'); script> - 父组件
<template><Son><template v-slot="defslot"><h5>{{ defslot.def1 }}h5>template><template v-slot:no_1="obj1"><h5>{{ obj1.no1 }}h5>template><template v-slot:no_2="{ no2, no3 }"><h5>{{ no2 }}h5><h5>{{ no3 }}h5>template>Son> template>传值:父传子在子组件首标签写,子传父在template标签写
- 效果

跳转至vue总篇目录
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
