注册
web

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 + "+自定义" }}
</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>
import node from './node.js'
export default {
name: 'Tree',
components: {
node,

},
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>
import node from './node.js'
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


总结


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


作者:晓欲望
来源:juejin.cn/post/7222931700438138937

0 个评论

要回复文章请先登录注册