vue3-局部组件/全局组件/递归组件/动态组件

142 阅读2分钟

局部组件

直接引入的方式
<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>

image.png

动态组件

  • 根据选择自动渲染对应组件
方式一
  • 性能较好
  • 注意使用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>