vue-08(使用slot进行灵活的组件渲染)

使用slot进行灵活的组件渲染

作用域slot是 Vue.js 中的一种强大机制,它允许父组件自定义子组件内容的呈现。与仅向下传递数据的常规 props 不同,作用域 slot 为父级提供了一个模板,然后子级可以填充数据。这提供了高度的灵活性和可重用性,使您能够创建可适应各种上下文的组件,而无需修改其核心逻辑。本章将探索 scoped slot 的概念、它们的语法,以及如何使用它们来构建灵活且可重用的组件。

了解作用域插槽

作用域插槽 是一种特殊类型的插槽,它允许父组件访问子组件的数据。这是通过将数据作为 props 传递给插槽来实现的。然后,父组件使用此数据以自定义方式呈现插槽内容。

基本语法

使用作用域插槽的基本语法包括在子组件中定义一个插槽,然后在父组件中为该插槽提供模板。

子组件(例如 MyList.vue):

js 复制代码
<template>
  <div>
    <ul>
      <li v-for="item in items" :key="item.id">
        <slot name="item" :item="item">{{ item.name }}</slot>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
};
</script>

在这个例子中,<slot> 元素有一个 name 属性设置为 "item" 和一个 :item 属性,该属性将当前itemv-for 循环绑定到插槽的范围。{{ item.name }} 是父组件未为槽提供自定义模板时将呈现的回退内容。

父组件:

js 复制代码
<template>
  <div>
    <MyList :items="myItems">
      <template v-slot:item="slotProps">
        <strong>{{ slotProps.item.name }}</strong> - <em>{{ slotProps.item.description }}</em>
      </template>
    </MyList>
  </div>
</template>

<script>
import MyList from './MyList.vue';

export default {
  components: {
    MyList,
  },
  data() {
    return {
      myItems: [
        { id: 1, name: 'Apple', description: 'A crisp and juicy fruit' },
        { id: 2, name: 'Banana', description: 'A sweet and potassium-rich fruit' },
      ],
    };
  },
};
</script>

在这里, <template v-slot:item="slotProps"> 语法为 MyList 组件中的 "item" 插槽定义了一个作用域插槽。slotProps 变量是一个对象,其中包含从子组件(在本例中为 item 对象)传递的数据。然后,父组件使用此数据以自定义格式呈现插槽内容。

速记语法

Vue.js 使用 # 字符为作用域插槽提供简写语法。可以使用简写语法重写前面的示例,如下所示:

js 复制代码
<template>
  <div>
    <MyList :items="myItems">
      <template #item="slotProps">
        <strong>{{ slotProps.item.name }}</strong> - <em>{{ slotProps.item.description }}</em>
      </template>
    </MyList>
  </div>
</template>

#item="slotProps" 等同于 v-slot:item="slotProps"。 这种速记语法更简洁,通常是首选。

带有 Scoped Props 的默认插槽

作用域插槽也可以与默认插槽(没有 name 属性的插槽)一起使用。在这种情况下,父组件为默认插槽提供模板,并且可以访问从子组件传递的数据。

子组件:

js 复制代码
<template>
  <div>
    <slot :message="greeting"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      greeting: 'Hello from the child!',
    };
  },
};
</script>

父组件:

js 复制代码
<template>
  <div>
    <MyComponent>
      <template #default="slotProps">
        {{ slotProps.message }}
      </template>
    </MyComponent>
  </div>
</template>

<script>
import MyComponent from './MyComponent.vue';

export default {
  components: {
    MyComponent,
  },
};
</script>

在此示例中,子组件将 message 属性传递给 default 插槽。然后,父组件使用此 prop 来渲染插槽内容。

实际示例和演示

让我们探索一些实际的例子,说明如何使用作用域 slot 来构建灵活且可重用的组件。

示例 1:可自定义的表组件

作用域 slots 的一个常见用例是创建可自定义的 table 组件。table 组件可以提供表的基本结构,而父组件可以定义每个单元格的呈现方式。

表格组件 (MyTable.vue):

js 复制代码
<template>
  <table>
    <thead>
      <tr>
        <th v-for="header in headers" :key="header.key">{{ header.label }}</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in items" :key="row.id">
        <td v-for="header in headers" :key="header.key">
          <slot :name="header.key" :row="row">{{ row[header.key] }}</slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    headers: {
      type: Array,
      required: true,
    },
    items: {
      type: Array,
      required: true,
    },
  },
};
</script>

父组件:

js 复制代码
<template>
  <div>
    <MyTable :headers="tableHeaders" :items="tableData">
      <template #name="slotProps">
        <strong>{{ slotProps.row.name }}</strong>
      </template>
      <template #age="slotProps">
        <em>{{ slotProps.row.age }}</em>
      </template>
    </MyTable>
  </div>
</template>

<script>
import MyTable from './MyTable.vue';

export default {
  components: {
    MyTable,
  },
  data() {
    return {
      tableHeaders: [
        { key: 'name', label: 'Name' },
        { key: 'age', label: 'Age' },
        { key: 'city', label: 'City' },
      ],
      tableData: [
        { id: 1, name: 'John Doe', age: 30, city: 'New York' },
        { id: 2, name: 'Jane Smith', age: 25, city: 'Los Angeles' },
      ],
    };
  },
};
</script>

在此示例中,MyTable 组件基于 headersitems 属性呈现一个表。父组件使用作用域插槽来自定义 "name" 和 "age" 列的呈现。"city" 列将使用 MyTable 组件中定义的默认内容。

示例 2:可自定义的列表组件

另一个常见用例是创建可自定义的列表组件。列表组件可以提供列表的基本结构,而父组件可以定义每个项目的呈现方式。

列表组件 (MyList.vue):

js 复制代码
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <slot name="item" :item="item">{{ item.name }}</slot>
    </li>
  </ul>
</template>

<script>
export default {
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
};
</script>

父组件:

js 复制代码
<template>
  <div>
    <MyList :items="myItems">
      <template #item="slotProps">
        <a :href="slotProps.item.url">{{ slotProps.item.name }}</a>
      </template>
    </MyList>
  </div>
</template>

<script>
import MyList from './MyList.vue';

export default {
  components: {
    MyList,
  },
  data() {
    return {
      myItems: [
        { id: 1, name: 'Google', url: 'https://www.google.com' },
        { id: 2, name: 'Facebook', url: 'https://www.facebook.com' },
      ],
    };
  },
};
</script>

在此示例中,MyList 组件根据 items 属性呈现项目列表。父组件使用一个有范围的插槽来自定义每个项目的渲染,将其转换为链接。

示例 3:具有验证的表单输入组件

范围插槽可用于创建高度可定制的表单输入组件。该组件可以处理输入逻辑和验证,而父组件可以自定义输入的外观和错误消息。

输入组件 (MyInput.vue):

js 复制代码
<template>
  <div>
    <label :for="id">{{ label }}</label>
    <input
      :id="id"
      :type="type"
      :value="value"
      @input="$emit('update:value', $event.target.value)"
    />
    <div v-if="error">
      <slot name="error" :error="error">{{ error }}</slot>
    </div>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  props: {
    id: {
      type: String,
      required: true,
    },
    label: {
      type: String,
      required: true,
    },
    type: {
      type: String,
      default: 'text',
    },
    value: {
      type: String,
      default: '',
    },
    rules: {
      type: Array,
      default: () => [],
    },
  },
  emits: ['update:value'],
  setup(props) {
    const error = ref('');

    watch(
      () => props.value,
      (newValue) => {
        for (const rule of props.rules) {
          const validationResult = rule(newValue);
          if (validationResult) {
            error.value = validationResult;
            return;
          }
        }
        error.value = '';
      }
    );

    return {
      error,
    };
  },
};
</script>

父组件:

js 复制代码
<template>
  <div>
    <MyInput
      id="name"
      label="Name"
      type="text"
      :value="name"
      @update:value="name = $event"
      :rules="[requiredRule, minLengthRule]"
    >
      <template #error="slotProps">
        <span style="color: red;">{{ slotProps.error }}</span>
      </template>
    </MyInput>
  </div>
</template>

<script>
import MyInput from './MyInput.vue';

export default {
  components: {
    MyInput,
  },
  data() {
    return {
      name: '',
    };
  },
  setup() {
    const requiredRule = (value) => {
      if (!value) {
        return 'This field is required.';
      }
      return null;
    };

    const minLengthRule = (value) => {
      if (value.length < 3) {
        return 'This field must be at least 3 characters long.';
      }
      return null;
    };

    return {
      requiredRule,
      minLengthRule,
    };
  },
};
</script>

在此示例中,MyInput 组件处理输入逻辑和验证。父组件使用 scoped slot 来自定义错误消息的渲染。

相关推荐
狼性书生1 分钟前
uniapp实现的简约美观的星级评分组件
前端·uni-app·vue·组件
书语时4 分钟前
ES6 Promise 状态机
前端·javascript·es6
拉不动的猪32 分钟前
管理不同权限用户的左侧菜单展示以及权限按钮的启用 / 禁用之其中一种解决方案
前端·javascript·面试
西陵43 分钟前
前端框架渲染DOM的的方式你知道多少?
前端·javascript·架构
小九九的爸爸44 分钟前
我是如何让AI帮我还原设计稿的
前端·人工智能·ai编程
海的诗篇_1 小时前
前端开发面试题总结-JavaScript篇(一)
开发语言·前端·javascript·学习·面试
江城开朗的豌豆1 小时前
eval:JavaScript里的双刃剑,用好了封神,用不好封号!
前端·javascript·面试
Forever Nore1 小时前
前端技能包
前端
江城开朗的豌豆2 小时前
JavaScript篇:前端定时器黑科技:不用setInterval照样玩转循环任务
前端·javascript·面试
书中自有妍如玉2 小时前
.net 使用MQTT订阅消息
java·前端·.net