组件使用
父子组件传参
父组件通过v-bind绑定一个数据,然后子组件通过defineProps接受传过来的值
父传子
模板上直接使用
typescript
复制代码
<template>
<div class="main">
<div class="vt-test">子组件</div>
<div>{{ title }}</div>
</div>
</template>
<script setup lang="ts">
//接受父组件传过来的值
defineProps({
title: {
type: String,
default: '默认值',
},
});
</script>
js里面使用
typescript
复制代码
// 接受父组件传过来的值;
const props = defineProps({
title: {
type: String,
default: '默认值',
},
});
console.log('父组件传过来的值', props.title);
ts组件里面泛型接收
typescript
复制代码
//ts模式下接收采取泛型更方便
const prop = defineProps<{
title: string;
}>();
console.log('父组件传过来的值', prop.title);
ts泛型里面设置默认值
typescript
复制代码
//ts模式下设置默认值
const prop = withDefaults(
defineProps<{
title: string;
arr: number[];
}>(),
{
arr: () => [1, 2, 3],
}
);
console.log('父组件传过来的值', prop.title);
子传父
方式一,采用defineEmits
typescript
复制代码
<template>
<div class="main">
<div class="vt-test">子组件</div>
<button @click="send">点击给父组件传值</button>
</div>
</template>
<script setup lang="ts">
//给父组件传值采用defineEmits
const emit = defineEmits(['on-click','on-getname']);
const send = () => {
emit('on-click', '花神');
emit('on-getname', '花神');
};
</script>
typescript
复制代码
<template>
<div class="vt-parents">父组件</div>
<hr />
<Helloworld :title="name" @on-click="getclick" @on-getname='getName'></Helloworld>
</template>
<script setup lang="ts">
import Helloworld from './components/HelloWorld.vue';
const name = '花神';
const getName = (value) => {
console.log(value, '子组件传的值');
};
</script>
typescript
复制代码
//给父组件传值采用defineEmits
const emit = defineEmits(['on-click', 'on-getname']);
const send = () => {
emit('on-click', '花神');
emit('on-getname', '花神2');
};
const getname = () => {
emit('on-getname', '花神2');
};
方式二,采用ts的泛型
typescript
复制代码
<script setup lang="ts">
//给父组件传值采用defineEmits
//有没有返回值,没有返回值定义void,不加也不报错
const emit = defineEmits<{
(e: 'on-click', value: string): void;
(e: 'on-getname', value: string): void;
}>();
const send = () => {
emit('on-click', '花神');
};
const getname = () => {
emit('on-getname', '花神2');
};
</script>
方式三,采用ref获取子组件内部暴露的数据和方法
子组件先暴露
defineExpose
父组件接收
类型检测<InstanceType>不写也行
全局组件
页面上直接使用即可
发现有报错,找不到模块"./App.vue"或其相应的类型声明
解决思路:在项目根目录 env.d.ts 文件中,加入以下内容:
typescript
复制代码
/// <reference types="vite/client" />
declare module "*.vue" {
import type { DefineComponent } from "vue"
const vueComponent: DefineComponent<{}, {}, any>
export default vueComponent
}
批量导入组件
想一次性全部导入模块的所有变量就可以使用 * as 代表全部
Object.entries() ,将一个对象中可枚举属性的键名和键值按照二维数组的方式返回
可选链
单问号,没有值返回undefined
双问号,只判断undefined和null,0和false不处理
递归组件
简单来说就是在组件中内使用组件本身
一般用来实现树形菜单,多级菜单
注意的是:要防止无限递归,造成调用栈溢出
步骤一,建立子组件Tree.vue
typescript
复制代码
<template>
<div class="main" v-for="(item, index) in data" :key="index">
<input type="checkbox" v-model="item.checked" /><span>{{ item.name }}</span>
<Tree v-if="item?.children?.length" :data="item?.children"></Tree>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
//定义接收的数据类型
interface Tree {
name: string;
checked: boolean;
children?: Tree[];
}
defineProps<{ data?: Tree[] }>();
</script>
<style scoped>
.main {
margin-left: 30px;
}
</style>
步骤二,建立父组件展示最终页面
typescript
复制代码
<template>
<div class="main"></div>
<TreeVal :data="data"></TreeVal>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import TreeVal from './components/Tree.vue';
interface Tree {
name: string;
checked: boolean;
children?: Tree[];
}
//造自定义数据传递给子组件
const data = reactive<Tree[]>([
{
name: '1',
checked: false,
children: [
{
name: '1-1',
checked: false,
children: [
{
name: '1-1-1',
checked: false,
children: [],
},
],
},
],
},
{
name: '2',
checked: false,
children: [],
},
]);
</script>
<style scoped></style>
最终展示页面
如何设置递归组件名name
方式一:可以采用文件名本身
方式二:可以页面内再建script里自定义组件名
方式三:通过插件
防止点击冒泡和传递event对象
动态组件
让多个组件使用同一个挂载点,并动态切换
在挂载点使用component标签,然后使用v-bind :is="组件"
方式一
typescript
复制代码
<template>
<div v-for="(item, index) in com" :key="index">
<div>{{ item.name }}</div>
<component :is="item.com"></component>
</div>
</template>
<script lang="ts">
import A from './components/A.vue';
import B from './components/B.vue';
import C from './components/C.vue';
export default {
components: {
A,
B,
C,
},
};
</script>
<script setup lang="ts">
import { ref, reactive } from 'vue';
//区别,此时使用的时候是字符串形式
const com = reactive([
{ name: 'A组件', com: "A" },
{ name: 'B组件', com: "B" },
{ name: 'C组件', com: "C" },
]);
</script>
方式二。Vue3写法
typescript
复制代码
<template>
<div v-for="(item, index) in com" :key="index">
<div>{{ item.name }}</div>
<component :is="item.com"></component>
</div>
</template>
typescript
复制代码
<script setup lang="ts">
import { ref, reactive } from 'vue';
import A from './components/A.vue';
import B from './components/B.vue';
import C from './components/C.vue';
//这边直接使用
const com = reactive([
{ name: 'A组件', com: A },
{ name: 'B组件', com: B },
{ name: 'C组件', com: C },
]);
</script>
虽然展示,但有报错
使用markRaw,到时候会多一个skip属性,reactive碰到这个属性,会跳过proxy代理
typescript
复制代码
<script setup lang="ts">
import { ref, reactive, markRaw } from 'vue';
import A from './components/A.vue';
import B from './components/B.vue';
import C from './components/C.vue';
const com = reactive([
{ name: 'A组件', com: markRaw(A) },
{ name: 'B组件', com: markRaw(B) },
{ name: 'C组件', com: markRaw(C) },
]);
</script>
Teleport 传送组件
Teleport是一种能够将我们的模板渲染至指定Dom节点,不受父级style,v-show等属性影响
但data,prop数据依旧能够共用的技术
主要为了解决,使Teleport节点挂载在其他指定的节点下,完全不受父级style样式的影响
例如:想要将子组件的这个弹窗挂载到外层
使用Teleport ,to挂载到想要展示的层级
typescript
复制代码
<template>
<div class="main">
<h3>子组件</h3>
<!-- disabled设置为true,则to不生效,否则生效 -->
<Teleport to="body" :disabled="true ">
<div class="box">弹窗</div>
</Teleport>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
</script>
<style scoped lang="less">
.main {
position: relative;
width: 500px;
height: 30vh;
margin-left: 30px;
background: #888;
}
.box {
background: yellow;
width: 200px;
height: 200px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
keep-alive缓存组件
注意,keep-alive里面只能有一个子节点,不能放多个,多个的话需要if条件判断只显示一个
有时候不希望组件被重新渲染影响使用体验,或者处于性能考验,避免多次重复渲染
目的,想要切换组件的时候,将我之前输入的值保留
include
缓存name匹配上的
在vue3中,组件内部的组件name需要重新写,setup的语法糖不支持
子组件
typescript
复制代码
<template>
<div class="main">
<h3>子组件</h3>
<Teleport to="body" :disabled="true">
<div class="box">弹窗</div>
</Teleport>
<input type="text" />
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
</script>
//重写name,为了在父组件可以使用include缓存Avue
<script lang="ts">
export default {
name: 'Avue',
};
</script>
typescript
复制代码
<template>
<div class="main">
<!-- include,只缓存名字匹配的,例如,这次只缓存Avue,Bvue不缓存-->
<keep-alive :include="['Avue']">
<Avue v-if="istrue"></Avue>
<Bvue v-else></Bvue>
</keep-alive>
<button @click="change">切换</button>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import Avue from './components/A.vue';
import Bvue from './components/B.vue';
let istrue = ref('true');
const change = () => {
istrue.value = !istrue.value;
};
</script>
<style scoped></style>
enclude
max,缓存最多数
超出max,会剔除旧的,不活跃的,保留新组件进行缓存
keep-alive生命周期onActivated和onDeactivated
typescript
复制代码
<template>
<div class="main">
<h3>子组件</h3>
<Teleport to="body" :disabled="true">
<div class="box">弹窗</div>
</Teleport>
<input type="text" />
</div>
</template>
<script setup lang="ts">
import {
ref,
reactive,
onMounted,
onUnmounted,
onActivated,
onDeactivated,
} from 'vue';
onMounted(() => {
console.log('初始化');
});
onActivated(() => {
console.log('keeplive缓存生命周期初始化');
});
onDeactivated(() => {
console.log('keeplive缓存生命周期卸载');
});
onUnmounted(() => {
console.log('卸载');
});
</script>
<script lang="ts">
export default {
name: 'Avue',
};
</script>
切换后
切回来
异步组件
defineAsyncComponent加载异步组件
大型应用中,需要将应用分割成小一些的代码块,并且减少主包的体积
typescript
复制代码
<template>
<div class="main">
<Suspense>
<template #default>
<Syncvue></Syncvue>
</template>
<template #fallback>
<!-- 放置骨架屏等 -->
</template>
</Suspense>
</div>
</template>
<script setup lang="ts">
import { ref, reactive,defineAsyncComponent} from 'vue';
const Syncvue =defineAsyncComponent(()=》{import('@/components/sync.vue')})
</script>
<style scoped></style>
Suspense
它帮助我们处理异步组件,但它的作用远不止于此
我们可以将异步组件的加载状态和占位内容进行统一管理。当异步组件加载完成时,会自动替换占位内容,从而实现平滑的过渡效果
Suspense 允许我们协调整个应用程序的加载状态,而不是一个页面上到处都是 loading
Suspense,把异步组件放入 default 槽,把回退加载状态放入 fallback 槽。
Suspense 机制还提供了错误处理的能力。当异步组件加载出错时,可以显示自定义的错误信息,以便及时通知用户
typescript
复制代码
<template>
<Suspense>
<template v-slot:default>
<div>Loading...</div>
</template>
<template v-slot:error>
<div>Failed to load component.</div>
</template>
<Syncvue />
</Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue';
const Syncvue =defineAsyncComponent(()=》{import('@/components/sync.vue')})
export default {
components: {
Syncvue ,
},
};
</script>