共计 2813 个字符,预计需要花费 8 分钟才能阅读完成。
需求
写一个简单的列表组件,每一列包含标题、其他信息、按钮点击事件、该项隐藏与否等配置信息。
设计基本的数据结构 – 初始思路
第一次写的时候写了个 map 结构的。emmmm…
写这样的数据结构,因为初始组件设计思考不足。
目标是展示的所有文案信息都是直接在默认配置的基础上混合就可以,按钮的 callback 函数由于不了解业务的情况,误以为各个引入此公共组件的模块都不一样。所以每个都单独加了回调配置项。好处可能是灵活一点,但是后来看来,对这个需求不是特别契合。
const defaultList = {
item1: {
title : '发布',
btnDisabled: false,
btnCallback: null,
showItem:true,
},
item2: {
title : '收藏',
btnDisabled: false,
btnCallback: null,
showItem:true,
},
...
}
设计基本的数据结构 – 改进思路
实际上,发现每个按钮回调完成的功能大同小异。似乎可以先去掉回调函数传参。不然每个用到这个组件的还得传一遍重复的回调函数(实际上函数功能是一样的)。所以,列表 list,组件 ul li 基本可以放在一个页面里。这个组件应该写得更重一点。因此结构替换成数组。用 type 属性 来充当遍历时候的辨别节点判断依据,而非上面的对象 key 值那么麻烦。前面用showItem 属性:true/false 来控制项的隐藏与否也可以去除,换一种方式做筛选:
const defaultList = [
{
title : '发布',
btnDisabled: false,
btnCallback: null,
showItem:true, // 这个不需要了
type: '1'
},
{
title : '收藏',
btnDisabled: false,
btnCallback: null,
showItem:true,
type: '2'
},
...
]
基本的组件结构 – 初始思路
一开始考虑的是每个回调函数都不一样,那组件里只写 li,在父组件做遍历,把每项的 Item 传进来。这样的话,实际组件的功能还是比较简单的。定制化需求都放在父组件,父组件需要配置的地方比较多。
<template>
<li>
<div>{{item.title}}</div>
<button @click="() => { item.btnCallback && item.btnCallback() }"></button>
</li>
</template>
<script>
expport default {props: ["item"]
}
</script>
使用的时候比较麻烦:
<ul>
<List v-for = "(item, index) in list" :item="item"></List>
</ul>
import defaultList from 'config/defautList'
data () {
// 个性化配置
list:{
item1: {
title : '发布',
btnDisabled: false,
btnCallback: this.item1Callback,
showItem:true,
},
item2: {
title : '收藏',
btnDisabled: true,
btnCallback: item2Callback,
showItem:true,
}
}
},
created () {
// 混合配置
this.list = $.extend(defaultList, this.list)
}
methods: {item1Callback () { },
item2Callback () {}
}
基本的组件结构 – 改进思路
老实说,也没有那么多需要定制的东西,改进后把所有的东西都基本放在了组件内部,使用的时候只需要引入组件基本就行了。
并且利用数组的 filter,incluedes,sort,indexOf 实现数据的筛选与排序:
<template>
<ul>
<li v-for="(item, i) in filtedList">
<div>{{item.title}}</div>
<button @click="clickHandle(item)"></button>
</li>
</ul>
</template>
<script>
import defaultList from 'config/defautList'
export default {
props: {
itemTitles: {default: function () {return ['发布', '关注', '收藏', '订单']
}
}
},
created () {this.initList()
},
data (){
return {filtedList: []
}
},
methods: {
// * 根据传入的标题删选出列表项 *
initList () {
let includedList = defaultList.filter(item => {return this.itemTitles.includes(item.title)
})
let sortedList = includedList.sort((a, b) => {
// * 按照传入标题的顺序排序 *
return this.itemTitles.indexOf(a.title) - this.itemTitles.indexOf(b.title)
})
return sortedList
},
clickHandle (item) {switch (item.type) {
case "1":
this.item1Callback()
break
case "2":
this.item2Callback()
break
}
},
method: {item1Callback(){},
item2Callback(){},
}
}
}
</script>
现在使用就很简单了,直接引入组件,并且可通过传一个带标题的数组,自定义顺序和需要被展示的项:
<List :itemTitles="[' 收藏 ',' 订单 ',' 发布 ',' 关注 ']"></List>
还可以利用 h5<template> 元素对整个模块进行显示隐藏
h5 的 <template> 元素自带 display:none 功能。显示隐藏有许多方法,可以试试这个。如果要显示隐藏的内容包含很多标签,且不希望额外增加一个空标签:
<li v-for="(item, i) in filtedList">
<div>{{item.title}}</div>
<template v-if="enabled">
<button @click="clickHandle(item)">xxx</button>
<button >xxx</button>
<button >xxxx</button>
</template>
</li>
props: {
...
enabled: {default: function () {return true}
}
},
当然,该组件还有很多不足,比如回调函数还是不能个性化传递等等。但是优化成什么样最终还是要和业务需求挂钩。