hello,你们好!我是lin
。vue
今天又想和你们分享封装组件这件事。在开发业务需求的过程当中,咱们可能会遇到一个全新的业务需求,也可能会遇到一个原来已有的功能去迭代新的功能。node
显然,如何去设计一个全新的业务需求值是值得咱们去思考的。git
一样,在一个原有的功能基础上去开发迭代也是值得去思考的。github
而不只仅只是command+c
和command+v
了!web
这篇文章也算是复盘总结本身对于需求如何去设计组件。element-ui
写文总结不易,如有更好的设计和思想,期待一块儿碰撞。数组
需求的拟稿:markdown
当我拿到需求拟稿的时候,我先看了element-ui
的级联组件不能知足业务,在此基础上改造也相对麻烦。因此最终我仍是打算本身封装一个级联组件。数据结构
接下来,我根据需求思考要实现的功能以下:函数
1.支持用户经过搜索类目,进行选择。
2.支持用户经过展开类目点击选择。(ps:点击第一个面板时将它子节点的第一级面板所有展开)
3.初始化状态,将默认展开第一个节点的第一位的因此节点。
4.支持正选和反选节点。
5.面板的每一个搜索框可控制该面板的搜索。
明确好本身要实现的需求功能以后,我所认为的是先不要急于编写代码,应该再作一个深刻的分析思考。
我先从数据结构入手,这个数据结构是树形的。相似这样的:
const data=[
{
text: "指南",
id: 2,
children: [
{
text: "设计原则",
id: 2.1,
children: [
{
text: "导航1",
id: 2.112,
},
{
text: "导航2",
id: 2.113,
},
{
text: "导航3",
id: 2.114,
},
],
},
{
text: "设计原则2",
id: 2.2,
children: [
{
text: "导航2",
id: 2.21,
},
],
},
],
},
]
复制代码
依据这样的树形数据格式,我能够明确这须要一个递归组件来实现。
可是如今有个关键的问题出现了,模板如何渲染这样的数据格式?
方案1:在设计这个递归组件时,是要把每个node
节点当成一个递归组件,好比遍历一个node
节点,如有children
属性的话,那么就一直递归下去,直到遇到节点没有children
属性时中止。而后再遍历下一个节点...
方案2:把递归组件拆成左右板块,左边先遍历同级的node
节点,右边的数据渲染是经过选中上一级的node
节点来控制下一级的node
节点。(ps:同级的node
节点遍历)
其实总结起来就是:要深度遍历仍是广度遍历呢?(ps:最终的方案留个疑问,下文讲述...)
这里是经过一个递归组件CascaderItem
和搜索过滤filterItem
组件以及一个父组件Cascader
来完成需求。
拟稿组件的布局是左右面板,开发这个组件我是使用flex
布局,我最终是使用方案2来遍历数据渲染。 方案1在功能的实现上是没有问题的,可是就布局来看会有必定的缺陷的,由于方案1是根据一个node
节点做为一个面板。
方案2的组件模板:
left
面板是同级节点的循环遍历,right
部分是组件自身引用自身,那么就成了一个递归组件。
这个组件的属性设计,我是经过props
属性进行传递的。
递归组件CascaderItem
的属性有:options
、level
(ps:表示当前是哪一级)、changeValToStr
、isCheckbox
、isInput
、reverseChoice
而父组件Cascader
的属性除了上面的属性以外还有:filterable
、value
(ps:双向绑定,下文讲述)、iptProps
。
具体各个属性的含义可查看源码注释,这里再也不作讲述了...
这里是经过一个计算属性showRightItem
来控制是否须要显示隐藏子节点的面板(ps:递归的终止的条件)。而这个计算属性依赖于一个响应式数据selected
数组(ps:被选中的节点)。经过判断选中的该级节点是否有children
属性来控制showRightItem
返回值。
这个selected
是经过父组件Cascader
传给子组件CascaderItem
。在这里我是经过selected.sync
传递属性,而且当用户点击node
节点时能够触发update:selected
事件让父组件的数据发生改变。
当用户点击node
的节点时,不只仅要触发update:selected
事件的。要在这个用户行为的函数上处理一些其余的逻辑。等因此的逻辑都完成以后再触发事件通知父组件。
涉及到的逻辑以下:
1.判断处理用户点击行为是正选仍是反选。
2.操做selected
数组,该级节点的下一级节点数据删除。由于上一级节点被切换了,那么下一级节点要被删除,实现切换数据视图一致的效果。
3.选中的该级节点是否有children
属性,若是有则须要将下级的第一位push
到selected
数组,直到下级的第一位没有children
属性为止。
当用户在每一级进行搜索的时,触发的是input
事件。要在这个用户行为上作当前级下的数据过滤。
搜索当前级下的数据时,下一级的面板的数据是没法预知的。为了解决搜索的父节点的数据与当前子面板的数据不相关联(ps:它可能还停留在上一个节点的子节点数据),让用户感到困惑。
这里是经过一个开关进行控制,在用户搜索时将下一个面板隐藏。等到用户肯定搜索的目标时再进行展开。
好了,到这里递归组件大体的逻辑完成了。具体的实现可查看源码...
父组件是暴露给用户使用的组件。它相似总池子将任务分配给不一样的子组件。这里经过用户传来的filterable
属性来肯定是不是使用递归组件CascaderItem
仍是filterItem
组件。
秉承着高内聚低耦合的思想,组件对外暴露onChange
事件以及v-model
属性。
当选中的节点发生变化时触发onChange
事件。v-model
属性实现数据的双向绑定,内部触发input
事件及时更新用户绑定的数据。
父组件相似总池子,它还负责处理一些数据。主要涉及到的逻辑以下:
1.初始化数据,将树形结构数据新增pathObj
和checked
属性。(ps:pathObj
属性是用来记录上下级节点的关系,checked
这个属性是服务于递归组件的checkbox
框)
2.将树形数据扁平化,为搜索时提供总数据。
3.初始化selected
数组,根据用户传入的值初始化selected
数据。
具体的数据处理逻辑如何实现可查看源码以及源码注释...
以下图:
原本想录个操做组件的小视频,无奈我还不知道哪款软件😢 ̄□ ̄||(ps:更可能是由于懒😝) 烦请各位大佬下载源码自行体验哈~
完成业务需求并不难,可能有不少种的方式能够达到目的。我以为更多的是在开发过程当中的思考以及如何设计是写代码一开始很重要的一部分。
在开发这个组件的时候,和小伙伴有个小插曲碰撞:就是用户选中节点数据合不合理的问题?
怎么理解呢? 我所理解的开发组件是给用户操做的,选中节点数据是否符合业务需求我认为是另一个层面上业务逻辑问题了,应该另作判断,另外封装规则。(ps:我想你们都有本身的理解和想法吧~)
不足:目前只支持单选功能,组件还能够再开发多选功能(ps:后续有时间迭代,敬请期待...)
团队协做,避免不了你须要在别人的代码基础上去实现新的功能。谈谈在已有的功能基础上去迭代新的需求,平常你是如何实践的呢?
在开发的过程中,我仍是以为封装组件设计组件是有必要的,为项目管理、后续的迭代添加不同的色彩。
这篇简文是lin
年前最后一篇的总结了,对各大掘友有帮助的话但愿赏个 star ~
最后,提早祝你们新年快乐,新的一年万事顺意~