写在前面
这一篇记录一下在使用vue库手写级联选择组件遇到的一个问题,当中涉及的知识点有组件通信,v-if与v-for的使用,props的使用,以及提供一些些组件设计的思路。
场景描述
简单描述:当前层级["浙江"、"广东"、"黑龙江"],需要根据用户点击相关省份来显示下一层。
这里涉及的难点在于显示组件的设计,也就是结构的设计,
这里有两种思路:
// 嵌套型,第二层是嵌套在第一层里面的。
<div class="level1">
<div class="level2">
</div>
// 独立型,第一层和第二层都独立,非嵌套
<div class="level"></div>
<div class="level2"></div>
直观上来选,肯定第一种。那现在就来试试。
解决过程
<template>
<div class="level1"
v-for="item1 in source"
@click="selectLevel1 = item1">
{{item1.name}}
<div class="level2"
v-for="item2 in for level2Items">
{{item2.name}}
</div>
</div>
</template>
<script>
export default{
level2Items(){
return selectLevel1.children?selectLevel1.children:[]
}
}
</script>
需要交代一下,此时的数据结构大概如下:
// source数据结构
[{name:"浙江",children:[{name:"杭州"},{name:"嘉兴"}],
{name:"广东",children:[{name:"惠州"}]
]
然后发现显示,结果有问题:
当点击“浙江”时,["杭州","嘉兴"]这个整体会被打印两次。
会出现这样的结果也正常,因为在第二层循环时没有加判断条件
<div class="level1"
v-for="item1 in source"
@click="selectLevel1 = item1">
{{item1.name}}
<!--这里没有v-if判断语句去获取到底父元素的值为某值时才应该显示-->
<div class="level2"
v-for="item2 in for level2Items">
{{item2.name}}
</div>
</div>
然后尴尬的是,level2Items这个计算属性并不能获取到父元素的标识。
[{name:"杭州"},{name:"嘉兴"}]
因此,我决定修改一下数据结构:
{parent_id:null,name:"浙江",id:1},
{parent_id:1,name:"杭州",id:2},
{parent_id:1,name:"嘉兴",id:3},
{parent_id:null,name:"广东",id:4},
{parent_id:4,name:"惠州",id:5}
再加上v-if判断条件
<template>
<div class="level2"
v-if="item2.parent_id === selectLevel1.id"
v-for="item2 in level2Item"
:key = item2.name
>
{{item2.name}}
</div>
</template>
照样失败,还是和原来一样的结果。
查看一下vue文档写着这么一句:
当
v-if
与v-for
一起使用时,v-for
具有比v-if
更高的优先级。请查阅列表渲染指南以获取详细信息
建议的方式是独立把v-if放在v-for之前
<template>
<!--提示item2未被定义,获取不成功-->
<div v-if="item2.parent_id === selectLevel1.id">
<div class="level2"
v-for="item2 in level2Item"
:key = item2.name
>
{{item2.name}}
</div>
</div>
</template>
所以,方法一,嵌套组织方式宣告失败。
最终解决
采取了第二种组织方式:第一层和第二层分别独立。
<div class="level1">
<div class="label"
v-for="item1 in source"
@click="level1Selected = item1"
:key="item1.name">
{{item1.name}}
</div>
</div>
<div class="level2">
<div class="label"
v-for="item2 in level2Items"
@click="level2Selected = item2"
:key="item2.name">
{{item2.name}}
</div>
因为不需要出现v-for和v-if混用还是抉择的问题。
compued:{
level2Items(){
// 通过level1Selected接受到第一层选择了什么,于是第二层也不需要判断了
if(this.level1Selected){
return this.level1Selected.children;
}else{
return []
}
},
}
唯一还需要注意的就是计算属性的初始化问题,在第一层未被点击时level2Items是没有值的状态。
总结
1.v-if和v-for同时使用时,v-for有优先级。若需要先判断,需将v-if往上推多一层。
2.设计上下级显示有通信关联的组件时,并且上下级有一对多关系的组件,应该考虑将父子元素分开,写成单独的形式,而不是嵌套。