局部组件
直接引入的方式
<template>
<div>
<test ref="TestRef" />
</div>
</template>
<script setup lang="ts">
import Test from './test.vue';
</script>
<style scoped></style>
全局组件
- 找到项目的main.ts,引入组件
- 挂载到app实例上
import { createApp } from 'vue';
import App from './App.vue';
import Test from '@/pages/apply-device/order-aws/test.vue';
const app = createApp(App);
// 第一个参数:自定义名字,必须是首字母大写的驼峰且包含2个单词的写法
// 第二个参数:需要全局注册的组件
app.component('TestDiv', Test);
app.mount('#app');
递归组件
- 在A组件里递归使用A组件,无需导入,直接用组件名(文件名)
- 使用export default起别名,然后使用别名
- 使用unplugin-vue-define-options插件,这个需要配置一下,然后调用插件提供的defineOptions方法注册别名
父组件
<template>
<div>
<test :data="data" />
</div>
</template>
<script lang="ts">
export default {
name: 'ApplyAWSIndex',
};
</script>
<script setup lang="ts">
import { reactive } from 'vue';
import Test from './test.vue';
interface TreeApi {
name: string;
checked: boolean;
children?: TreeApi[];
}
const data = reactive<TreeApi[]>([
{
name: '1',
checked: false,
children: [
{
name: '1-1',
checked: false,
},
],
},
{
name: '2',
checked: false,
},
{
name: '3',
checked: false,
children: [
{
name: '3-1',
checked: false,
children: [
{
name: '3-1-1',
checked: false,
},
],
},
],
},
]);
</script>
<style scoped></style>
递归组件
文件名字:test.Vue
<template>
<div v-for="item in data" :key="item.name" style="margin-left: 10px" @click.stop="onClick(item, $event)">
<input v-model="item.checked" type="checkbox" /><span>{{ item.name }}</span>
<!-- 方式一 -->
<test v-if="item?.children?.length" :data="item.children" />
<!-- 方式二 -->
<!-- <OtherName v-if="item?.children?.length" :data="item.children" />-->
</div>
</template>
<script lang="ts">
// 方式二定义别名
export default {
name: 'OtherName',
};
</script>
<script setup lang="ts">
interface TreeApi {
name: string;
checked: boolean;
children?: TreeApi[];
}
withDefaults(defineProps<{ data?: TreeApi[] }>(), { data: undefined });
const onClick = (item, e) => {
console.log(item);
console.log(e);
};
</script>
<style scoped></style>
动态组件
- 根据选择自动渲染对应组件
方式一
- 性能较好
- 注意使用shallRef & markRaw方法包裹组件对象
<template>
<div>
<div style="display: flex">
<div v-for="item in data" :key="item.name" style="border: 2px solid black; margin-right: 2px">
<a @click="choice = item.comp">{{ item.name }}</a>
</div>
</div>
<component :is="choice" />
</div>
</template>
<script lang="ts">
export default {
name: 'ApplyAWSIndex',
};
</script>
<script setup lang="ts">
import { markRaw, reactive, shallowRef } from 'vue';
import AVue from './A.vue';
import BVue from './B.vue';
import CVue from './C.vue';
const choice = shallowRef(AVue);
const data = reactive([
{
name: 'A组件',
comp: markRaw(AVue),
},
{
name: 'B组件',
comp: markRaw(BVue),
},
{
name: 'C组件',
comp: markRaw(CVue),
},
]);
</script>
<style scoped></style>
方式一注意问题
- 需要控制ref响应范围。这个报错大概意思就是把组件做成了响应式变量后,会导致组件内诸多属性/信息也被劫持了,这是不必要的性能浪费,所以要避免。通过‘markRaw’(使用reactive时配合)或‘shallowRef’(使用ref时替换)方法包裹组件,控制响应范围。
[Vue warn]: Vue received a Component which was made a reactive object.
This can lead to unnecessary performance overhead,
and should be avoided by marking the component with `markRaw`
or using `shallowRef` instead of `ref`.
Component that was made reactive:
方式二
- 字符串方式,性能低于方式一
- 区别在于定义组件的位置,需要通过export default提前声明,做好映射关系
- 此时不需要markRaw, shallowRef方法了
<template>
<div>
<div style="display: flex">
<div v-for="item in data" :key="item.name" style="border: 2px solid black; margin-right: 2px">
<a @click="choice = item.comp">{{ item.name }}</a>
</div>
</div>
<component :is="choice" />
</div>
</template>
<script lang="ts">
import AVue from './A.vue';
import BVue from './B.vue';
import CVue from './C.vue';
export default {
name: 'ApplyAWSIndex',
components: { AVue, BVue, CVue },
};
</script>
<script setup lang="ts">
import { reactive, ref } from 'vue';
const choice = ref('AVue');
const data = reactive([
{
name: 'A组件',
comp: 'AVue',
},
{
name: 'B组件',
comp: 'BVue',
},
{
name: 'C组件',
comp: 'CVue',
},
]);
</script>
<style scoped></style>
A、B、C组件内容
// 组件A
<template>
<div style="border: 1px solid black; height: 500px">我是A</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
// 组件B
<template>
<div style="border: 1px solid black; height: 500px">我是B</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
// 组件C
<template>
<div style="border: 1px solid black; height: 500px">我是C</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>