vue3学习基础之组件

发布时间 2023-12-26 15:58:51作者: carol2014

组件

Comp.vue

<script setup>
import { ref, provide, readonly, defineAsyncComponent } from "vue";
import DemoComp from "../../components/simple/DemoComp.vue";
import ErrorComponent from "../../components/simple/ErrorComp.vue";
import LoadingComponent from "../../components/simple/LoadingComp.vue";

const count = ref(5);
const isActive = ref(true);
const postFontSize = ref(1);
const dynamicSlotName = "slot4";

// 组件后代提供数据,避免“prop 逐级透传”
provide("module_name", "ad");
const location = ref("North Pole");

function updateLocation() {
  location.value = "South Pole";
}

//允许组件后代修改数据
provide("location", {
  location,
  updateLocation,
});
//不允许组件后代修改数据
provide("read_only_count", readonly(count));

//异步组件
const HelloComp = defineAsyncComponent(() => import("../../components/simple/HelloComp.vue")); //简单用法
// 定义一个耗时执行的函数,t 表示延迟的时间, callback 表示需要执行的函数,可选
const time = (t, callback = () => { }) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      callback();
      resolve();
    }, t);
  });
};
const Hello = defineAsyncComponent({
  // 加载函数
  loader: () => {
    return new Promise((resolve, reject) => {
      (async function () {
        await time(2000);
        const res = await import("../../components/simple/HelloComp.vue");
        resolve(res);
      })();
    });
  },
  loadingComponent: LoadingComponent, // 加载异步组件时使用的组件
  delay: 1000, // 展示加载组件前的延迟时间,默认为 200ms
  errorComponent: ErrorComponent, // 加载失败后展示的组件
  timeout: 3000,
});
</script>

<template>
  <div>
    <div :style="{ fontSize: postFontSize + 'rem' }">
      <el-row :gutter="5">
        <el-col :span="12">
          <el-card class="box-card">
            <template #header>
              <div class="card-header">
                <span>组件</span>
              </div>
            </template>
            <demo-comp :class="{ active: isActive }" title="Vue3" greeting-message="hi" :likes="42" :is-published="false"
              :comments="[
                { id: 234, content: 'ok' },
                { id: 266, content: 'nice' },
                { id: 273, content: 'not good' },
              ]" :author="{ name: 'Veronica', company: 'Veridian Dynamics' }"
              @enlarge-text="postFontSize += 0.1">渲染default slot

              <template #slot2>
                <span>渲染slot2</span>
              </template>
              <template v-slot:slot3>
                <span>渲染slot3</span>
              </template>
              <template v-slot:[dynamicSlotName]>
                <span>渲染slot4</span>
              </template>
              <!-- 作用域插槽,父组件接收子组件传过来的值itemProps,父组件可以根据子组件传过来的值决定插槽的内容 -->
              <template #item="itemProps">
                {{ itemProps.id + "|" + itemProps.content }}
              </template>
            </demo-comp>
          </el-card>
        </el-col>
        <el-col :span="12">
          <el-card class="box-card">
            <template #header>
              <div class="card-header">
                <span>组件</span>
              </div>
            </template>
            <demo-comp :class="{ active: isActive }" title="test" greeting-message="test" :likes="30" :is-published="true"
              :comments="[
                { id: 234, label: 'ok', num: 10 },
                { id: 266, label: 'nice', num: 5 },
                { id: 273, label: 'not good', num: 18 },
              ]" :author="{ name: 'Veronica', company: 'Veridian Dynamics' }" @enlarge-text="postFontSize += 0.1">
              <!-- 作用域插槽,父组件接收子组件传过来的值itemProps,父组件可以根据子组件传过来的值决定插槽的内容 -->
              <template #item="itemProps">
                {{ itemProps.id + "|" + itemProps.label + "|" + itemProps.num }}
              </template>
            </demo-comp>
          </el-card>
        </el-col>
      </el-row>
    </div>

    <div>
      <p>异步组件</p>
      <hello-comp></hello-comp>
      <p>延时3s渲染</p>
      <hello></hello>
    </div>
  </div>
</template>

<style scoped>
span {
  margin-left: 1rem;
}
</style>

 DemoComp.vue

<script setup>
import { ref, inject } from "vue";

defineOptions({
  inheritAttrs: false, //禁用 Attributes 继承
});

const props = defineProps({
  title: String,
  greetingMessage: String,
  likes: Number,
  isPublished: Boolean,
  author: Object,
  comments: Array,
});
const emits = defineEmits(["enlargeText"]);

const count = ref(0);

// 注入上层组件提供的数据
const api_url = inject("api_url");
const module_name = inject("module_name");
const { location, updateLocation } = inject("location");
let read_only_count = inject("read_only_count");

function updateCount() {
  read_only_count.value++;
  alert("read_only_count的值为:" + read_only_count.value);
}
</script>

<template>
  <div>
    <p :class="$attrs.class">
      <span>msg:{{ title }}</span>
      <span>greetingMessage:{{ greetingMessage }}</span>
      <span>likes:{{ likes }}</span>
      <span>is-published:{{ isPublished ? "是" : "否" }}</span>
      <span>author:{{ author.name + "|" + author.company }}</span>
    </p>
    <button type="button" @click="count++">count is {{ count }}</button>
    <button @click="$emit('enlargeText')">Enlarge text</button>
    <p>
      <slot><span>default slot </span></slot>
      <slot name="slot2"><span>slot2</span></slot>
      <slot name="slot3"><span>slot3</span></slot>
      <slot name="slot4">
        <span>slot4</span>
      </slot>
    </p>

    <!-- 作用域插槽,子组件传值item给父组件 -->
    <p>
      comments:
      <span v-for="(item, index) in comments" :key="index">
        <slot name="item" v-bind="item"></slot>
      </span>
    </p>

    <p>
      App.vue注入:
      <span>{{ api_url }}</span>
    </p>
    <p>
      父级组件注入(允许修改):
      <span>{{ module_name }}</span>
      <button @click="updateLocation">{{ location }}</button>
    </p>
    <p>
      父级组件注入(不允许修改):
      <span>{{ read_only_count }}</span>
      <button @click="updateCount">update count</button>
    </p>
  </div>
</template>

<style scoped>
span {
  margin-left: 1rem;
}

button,
a {
  border: 1px solid blue;
  margin-left: 1rem;
}
</style>