Skip to content

this.$set(this.$slotsData, name, slotFunc) or this.$setSlotsData(name, slotFunc) #195

Open
@jiankafei

Description

@jiankafei

What problem does this feature solve?

It can solve the problem that JSX or H functions are needed to build complex components, which is as flexible as react.

The slot component can respond to external $slots (similar to props, not modifiable), and can also respond to the dynamically set $slots in the component, just like setting data (let's call it $slotsdata, although all $slots actually come from the outside, in essence, it provides a means to dynamically set $slots).

What does the proposed API look like?

<!-- MyComp -->
<template>
  <div>
    <!-- render aaa slot -->
    <slot name="aaa"></slot>
    <div v-for="(row, rowIndex) of daat" :key="rowIndex">
      <div v-for="(col, colIndex) of columns" :key="colIndex">
        <!-- render `label` slot -->
        <!-- In fact, the 'label' slot component renders the slots passed to the MyCompItem component -->
        <slot :name="col.label" :row="row" :rowIndex="rowIndex" :colIndex="colIndex">{{row[col.prop]}}</slot>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'MyComp',
  props: {
    daat: Array,
  },
  data() {
    return {
      columns: [],
    };
  },
  created() {
    // Get default slot vnode
    // It is actually the vnode of MyCompItem
    const MyCompItemVNode = this.$slots.default();
    this.columns = MyCompItemVNode.map((vnode) => {
      const componentProps = vnode.componentProps;
      const propsData = componentProps.propsData;
      // --------------- key point ------------------------
      // Slots should be mounted in` vnode.componentProps.$slots',
      // there's no need to hang it in` vnode.componentInstance.$slots',
      // because it's just a function that generates slots.
      // Mounting to componentinstance also has hidden dangers,
      // because you must instantiate slots before you can get the $slots function inside slots.
      // The hidden danger is that if the internal slots need to pass parameters,
      // developers must track the actual parameters passed and pass the default values for the instantiated slots.
      // If you don't have to instantiate the slots function inside the vnode of the slots,
      // then developers only need to instantiate slots where they use the slots function to avoid this hidden danger.
      // slots.xxx  In fact, it is equivalent to an attribute, but classified as slots, not props.
      const $slots = componentProps.$slots;
      // --------------- key point ------------------------
      // Set up new slots for the MyComp component through the following API, similar to setting data
      // The slot component can respond to both the external incoming $slots and the dynamically added $slotsdata
      $slots.default && this.$set(this.$slotsData, propsData.label, $slots.default);
      // or
      // $slots.default && this.$setSlots(propsData.label, $slots.default);
      return propsData;
    });
  },
};
</script>

<!-- MyCompItem -->
<template>
  <div></div>
</template>
<script>
export default {
  name: 'MyCompItem',
  props: {
    label: String,
    prop: String,
  },
};
</script>

<!-- usage -->
<template>
  <div>
    <my-comp :daat="daat">
      <!-- Render to 'aaa' slot of MyComp -->
      <template #aaa>
        <div>AAAAAAAA</div>
      </template>
      <!-- my-comp-item is not actually rendered -->
      <my-comp-item label="名称" prop="name"></my-comp-item>
      <my-comp-item label="描述" prop="desc">
        <!-- It will actually render to the description slot of the MyComp component -->
        <template #default={ row, rowIndex, colIndex }>
          <div>{{row}}{{rowIndex}}{{colIndex}}</div>
        </template>
      </my-comp-item>
    </my-comp>
  </div>
</template>
<script>
import MyComp from './MyComp';
import MyCompItem from './MyCompItem';

export default {
  components: {
    MyComp,
    MyCompItem,
  },
  data() {
    return {
      daat: [
        {
          name: 'vue',
          desc: 'mvvm framework'
        }
        {
          name: 'react',
          desc: 'mvvm framework too'
        }
      ],
    };
  },
};
</script>

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions