vue 递归组件 作用域插槽

开头

这里主要是根据 vue 递归组件 作用域插槽 代码的理解和el-tree是如何写的。

代码

父组件

<template>





  <div>

    <Tree :data="data">

      <template #default="{ title }">
        <div class="prent">
          {{ title + "+自定义" }}
        </div>
      </template>
    </Tree>
  </div>
</template>
<script>
import Tree from "./tree.vue";
export default {
  components: {
    Tree,
  },
  data() {
    return {
      data: [{
        title: "父1",
        children: [{
          title: "子",
          children:[{title:"孙",}]
        }],
      },{
        title: "父2",
        children:[{title:"子"}]
      }]
    };
  }
};
</script>

子组件

<template>





  <div class="tree">



    <div v-for="item of data" :key="item.title">



      <!-- 显示title标题 -->



      <div class="title">



        <!-- 插槽,这里也是把title传出去, A插槽 -->

        <slot :title="item.title">



          <!-- {{ item.title }} -->



        </slot>



      </div>



      <!-- 如果存在子项则调用本身组件 递归 -->



      <Tree v-if="item.children" :data='item.children'>



            <!-- B 插槽 -->

          <slot :title='item.title' />

      </Tree>

    </div>

  </div>

</template>



<script>

export default {

  name: 'Tree',

  props: {

    data: Array,

  },

};

</script>



<style scoped>

.tree {

  padding-left: 10px;

}



ul,

li {

  list-style: none;

  margin: 0;

  padding: 0;

}

</style>

理解步骤,始终知道 -> 递归就是把最里面的放到最外面来,你就当 A插槽最后会被 B 插槽替代
所以,父组件的 default 插槽用的是 B 插槽,因此 B 插槽就暴露出一个 title 给父组件使用。

删掉 A 的title :

<template>





  <div class="tree">



    <div v-for="item of data" :key="item.title">



      <!-- 显示title标题 -->



      <div class="title">



        <!-- 插槽,这里也是把title传出去, A插槽 -->

        <slot :title="item.title">



          <!-- {{ item.title }} -->



        </slot>



      </div>



      <!-- 如果存在子项则调用本身组件 递归 -->



      <Tree v-if="item.children" :data='item.children'>



            <!-- B 插槽 -->

          <slot :title='item.title' />

      </Tree>

    </div>

  </div>

</template>



<script>

export default {

  name: 'Tree',

  props: {

    data: Array,

  },

};

</script>



<style scoped>

.tree {

  padding-left: 10px;

}



ul,

li {

  list-style: none;

  margin: 0;

  padding: 0;

}

</style>

结果:

image.png

由于可能只有一层,所以走不到 B 插槽,因此 A 插槽也需要暴露一个 title 给外面使用。

el-tree 的原理

父组件

<template>





  <div>

    <Tree :data="data">

        <!-- C -->
      <template #default="{ title }">
        <div class="prent">
          {{ title + "+自定义11" }}
        </div>
      </template>
    </Tree>
  </div>
</template>
<script>
import Tree from "./tree.vue";
export default {
  components: {
    Tree,
  },
  data() {
    return {
      data: [{
        title: "父1",
        children: [{
          title: "子",
          children:[{title:"孙",}]
        }],
      },{
        title: "父2",
        children:[{title:"子"}]
      }]
    };
  }
};
</script>

子组件

<template>





  <div class="tree">



    <div v-for="item of data" :key="item.title">



      <!-- 显示title标题 -->



      <div class="title">



        <!-- 插槽,这里也是把title传出去, A -->
        <slot :title="item.title">



          <!-- {{ item.title }} -->



        </slot>



      </div>



      <!-- 如果存在子项则调用本身组件 递归 -->



      <Tree v-if="item.children" :data='item.children'>



        <!-- B -->
        <template #default="{ title }">
          <div class="prent">
            {{ title + "+自定义22" }}
          </div>
        </template>
      </Tree>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Tree',
  props: {
    data: Array,
  },
  data() {
    return {
      tree: null,
    }
  },
  created() {
    if(!this.$parent.$scopedSlots.default) {
      this.tree = this
    }else {
      this.tree = this.$parent.tree
    }
  },
};
</script>

<style scoped>
.tree {
  padding-left: 10px;
}

ul,
li {
  list-style: none;
  margin: 0;
  padding: 0;
}
</style>

结果:

image.png

这里可以看到,父组件的 C 和 子组件中的 B 都是使用到了 A 这个插槽。

这里我们只要能把 B 替换成父组件的 C 就完成了递归插槽。(递归是使用父类的插槽模版)

子组件的代码转变

<template>





  <div class="tree">



    <div v-for="item of data" :key="item.title">



      <!-- 显示title标题 -->



      <div class="title">



        <!-- 插槽,这里也是把title传出去 -->
        <slot :title="item.title">



          <!-- {{ item.title }} -->



        </slot>



      </div>



      <!-- 如果存在子项则调用本身组件 递归 -->



      <Tree v-if="item.children" :data='item.children'>



        <template #default="{ title }">
          <node :title="title">
          </node>
        </template>
      </Tree>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Tree',
  components: {
    node: {
      props: {
        title: String,
      },
      render(h) {
        const parent = this.$parent;
        const tree = parent.tree
        const title = this.title
        return (tree.$scopedSlots.default({ title }))
      }
    }
  },
  props: {
    data: Array,
  },
  data() {
    return {
      tree: null,
    }
  },
  created() {
    if (!this.$parent.$scopedSlots.default) {
      this.tree = this
    } else {
      this.tree = this.$parent.tree
    }
  },
};
</script>

<style scoped>
.tree {
  padding-left: 10px;
}

ul,
li {
  list-style: none;
  margin: 0;
  padding: 0;
}
</style>

这里搞了一个 node 的函数组件,node 函数组件拿到 子组件的 tree, tree也是一层层的保存着 $scopedSlots.default 其实就是 C 的那些编译节点。 然后把 title 传给了 C。

el-tree 源码贴图

image.png

tree

image.png

tree-node

image.png

image.png

image.png

image.png

总结

写的有点乱啊,这个只是辅助你理解 递归插槽,其实一开始都是懵逼了,多看下代码理解还是能看的懂的。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYvwDIdN' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片