详细介绍Vue3 tsx中插槽的使用,和React进行横向对比

445 阅读4分钟

Vue3 tsx中插槽的使用

在 Vue 3 中使用 TSX 时,插槽(slots)依然是一个重要的功能。以下是如何在 Vue 3 + TSX 中使用插槽的详细说明和示例。

基本使用

假设你有一个 Button 组件,希望它能够接受插槽内容:

Button 组件

import { defineComponent, PropType } from 'vue';

const Button = defineComponent({
  name: 'Button',
  props: {
    label: String,
  },
  setup(props, { slots }) {
    return () => (
      <button>
        {slots.default ? slots.default() : props.label}
      </button>
    );
  }
});

export default Button;

使用插槽

在父组件中,你可以通过 TSX 语法来使用插槽:

父组件

import { defineComponent } from 'vue';
import Button from './Button';

const ParentComponent = defineComponent({
  name: 'ParentComponent',
  setup() {
    return () => (
      <div>
        <Button>
          <span>Click Me!</span>
        </Button>
      </div>
    );
  }
});

export default ParentComponent;

具名插槽

如果你需要使用具名插槽,可以使用以下方式:

Button 组件(具名插槽)

import { defineComponent, PropType } from 'vue';

const Button = defineComponent({
  name: 'Button',
  props: {
    label: String,
  },
  setup(props, { slots }) {
    return () => (
      <button>
        {slots.icon && slots.icon()}
        {slots.default ? slots.default() : props.label}
      </button>
    );
  }
});

export default Button;

使用具名插槽

在父组件中使用具名插槽:

父组件(具名插槽)

import { defineComponent } from 'vue';
import Button from './Button';

const ParentComponent = defineComponent({
  name: 'ParentComponent',
  setup() {
    return () => (
      <div>
        <Button>
          {{
            icon: () => <span>🔍</span>,
            default: () => <span>Search</span>,
          }}
        </Button>
      </div>
    );
  }
});

export default ParentComponent;

作用域插槽

对于作用域插槽,可以使用以下方式:

List 组件(作用域插槽)

import { defineComponent, PropType } from 'vue';

const List = defineComponent({
  name: 'List',
  props: {
    items: {
      type: Array as PropType<string[]>,
      required: true,
    },
  },
  setup(props, { slots }) {
    return () => (
      <ul>
        {props.items.map((item, index) =>
          slots.default ? slots.default({ item, index }) : <li>{item}</li>
        )}
      </ul>
    );
  }
});

export default List;

使用作用域插槽

在父组件中使用作用域插槽:

父组件(作用域插槽)

import { defineComponent } from 'vue';
import List from './List';

const ParentComponent = defineComponent({
  name: 'ParentComponent',
  setup() {
    const items = ['Item 1', 'Item 2', 'Item 3'];

    return () => (
      <div>
        <List items={items}>
          {({ item, index }) => <li>{index}: {item}</li>}
        </List>
      </div>
    );
  }
});

export default ParentComponent;

总结

  • 默认插槽:使用 slots.default() 渲染默认插槽内容。
  • 具名插槽:通过对象解构语法传递具名插槽。
  • 作用域插槽:在插槽函数中传递参数,并在父组件中使用这些参数。

和react进行横向对比

Vue 3 的插槽和 React 的 children/props 功能有很多相似之处,但也有一些关键的区别。这些区别主要体现在语法、功能和使用方式上。以下是详细的对比和说明。

基本概念

React

在 React 中,组件之间的内容传递主要通过 children 和 props 完成。children 是一个特殊的 prop,用于传递嵌套的 JSX 元素。

Vue

在 Vue 中,插槽(slots)是组件内容分发的一种方式。插槽允许在父组件中定义模板内容,并将这些内容传递给子组件。

基本用法对比

React

定义子组件
const Button = ({ children }) => {
  return <button>{children}</button>;
};
使用子组件
const App = () => {
  return (
    <Button>
      <span>Click Me!</span>
    </Button>
  );
};

Vue 3 (使用 TSX)

定义子组件
import { defineComponent } from 'vue';

const Button = defineComponent({
  setup(props, { slots }) {
    return () => <button>{slots.default ? slots.default() : ''}</button>;
  },
});

export default Button;
使用子组件
import { defineComponent } from 'vue';
import Button from './Button';

const App = defineComponent({
  setup() {
    return () => (
      <Button>
        <span>Click Me!</span>
      </Button>
    );
  },
});

export default App;

具名插槽与自定义 prop

React

React 通过传递自定义的 prop 来实现具名插槽的效果:

定义子组件
const Button = ({ icon, children }) => {
  return (
    <button>
      {icon && <span>{icon}</span>}
      {children}
    </button>
  );
};
使用子组件
const App = () => {
  return (
    <Button icon={<span>🔍</span>}>
      <span>Search</span>
    </Button>
  );
};

Vue 3 (使用 TSX)

Vue 3 通过具名插槽实现类似的效果:

定义子组件
import { defineComponent } from 'vue';

const Button = defineComponent({
  setup(props, { slots }) {
    return () => (
      <button>
        {slots.icon && slots.icon()}
        {slots.default ? slots.default() : ''}
      </button>
    );
  },
});

export default Button;
使用子组件
import { defineComponent } from 'vue';
import Button from './Button';

const App = defineComponent({
  setup() {
    return () => (
      <Button>
        {{
          icon: () => <span>🔍</span>,
          default: () => <span>Search</span>,
        }}
      </Button>
    );
  },
});

export default App;

作用域插槽与 render prop

React

React 通过 render prop 模式实现作用域插槽:

定义子组件
const List = ({ items, renderItem }) => {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item, index)}</li>
      ))}
    </ul>
  );
};
使用子组件
const App = () => {
  const items = ['Item 1', 'Item 2', 'Item 3'];

  return (
    <List items={items} renderItem={(item, index) => `${index}: ${item}`} />
  );
};

Vue 3 (使用 TSX)

Vue 3 通过作用域插槽实现类似的功能:

定义子组件
import { defineComponent, PropType } from 'vue';

const List = defineComponent({
  props: {
    items: {
      type: Array as PropType<string[]>,
      required: true,
    },
  },
  setup(props, { slots }) {
    return () => (
      <ul>
        {props.items.map((item, index) =>
          slots.default ? slots.default({ item, index }) : <li>{item}</li>
        )}
      </ul>
    );
  },
});

export default List;
使用子组件
import { defineComponent } from 'vue';
import List from './List';

const App = defineComponent({
  setup() {
    const items = ['Item 1', 'Item 2', 'Item 3'];

    return () => (
      <List items={items}>
        {({ item, index }) => <li>{index}: {item}</li>}
      </List>
    );
  },
});

export default App;

主要区别总结

  1. 语法

    • React 使用 children 和自定义 prop 传递内容。
    • Vue 使用 slots 传递内容,具名插槽和作用域插槽通过特定的语法实现。
  2. 功能

    • React 的 children 是一个特殊的 prop,可以是任意的 React 元素或组件。
    • Vue 的插槽系统更为强大和灵活,支持具名插槽和作用域插槽,能够更加直观地处理复杂的内容分发。
  3. 使用方式

    • React 更加依赖于组件的 props 来传递内容。
    • Vue 提供了更直接的插槽系统,使得内容分发和传递更加结构化和语义化。

通过这些对比,你可以更清楚地了解在 Vue 3 中如何使用 TSX 来实现插槽,以及与 React 中 children 和 render props 的差异。