前端组件设计之实践篇
前言
在前端组件设计之理论篇中已经对前端组件设计的原则进行了介绍,本文将在实际开发过程中使用这些原则,根据自己学习理解后总结出来的组件开发的步骤进行开发实战。
前端组件设计系列:
一、组件开发流程/步骤
- 1、确定组件的功能;
- 2、确定组件接收的数据;
- 3、确定组件自身维护的状态;
- 4、确定组件的操作功能;
- 5、确定组件输出的数据;
亲身实践标明,根据上述的5个步骤进行开发,能够有效的规避很多不必要的坑,同时使开发的组件更加“健康”;
接下来我将以开发一个Tree组件为例,进行实践的应用和讲解;
技术栈:Vue
二、组件解析
本实例实现 Tree树形组件 并非涵盖所有能力(排序、选择···),主要是通过 前端组件化 实践让读者能够熟悉组件开发中的原则和步骤。
按照『组件开发流程』对组件进行解析,方便后续代码层面的开发;
1、确定组件的功能
分析Tree树形组件应该具有以下两个核心功能:
- 1)展示树状数据;
- 2)父节点展开/折叠;
2、确定组件接收的数据;
组件接收的数据通常用Props表示,数据本身只用于数据的展示,所以组件内部不应该改变传入的数据本身,需要遵循数据的单向传递**的原则。
-
展示数据:
以每个节点进行分析,节点属性:节点名称(title)、节点唯一标识(key),节点的子节点数据(children);
treeData = [ { title: '0-0', key: 1, children: [ { title: '0-0-0', key: 2, children: [ { title: '0-0-0-0', key: 3 }, { title: '0-0-0-1', key: 4 }, { title: '0-0-0-2', key: 5 }, ··· ], }, ··· ], }, ··· ];
-
默认展开的节点:
初始加载组件时,控制哪些父节点默认呈展开状态,数据为一个数组,数组中元素为节点唯一标识(key)
expandedKeys = [1, 2, 3,···]
3、确定组件自身维护的状态
组件自身状态可以用States表示,其用来维护组件自身的状态变化,也就是说组件状态的维护应该在组件内部,与组件外部传入什么数据、怎么使用组件没有丝毫关系。例如,点击父节点是否展开和折叠其子节点树的状态是组件本身就具有的,无论你传入的数据内容如何不同,都不会影响这个能力。
通过组件功能可以确定组件自身有以下状态:
- 每个节点的否展开和折叠状态(isFold);
4、确定组件的操作功能
组件的操作功能可以使用Methods表示,一般都需要依托以数据和状态,例如Tree树形组件中点击节点(onClick),打开或者折叠其子节点,是对组件自身状态的改变。
5、确定组件输出的数据
组件的输出可以用Emit表示,本质是组件向父组件进行通信。
首先,Tree树形组件更偏向于数据展示类组件,有别于按钮、输入等数据操作类组件;
数据展示类组件
一般只会对传入的数据做展示,或者更改数据的展示形式;
数据操作类组件
除了有数据展示的能力外,一般都具有主动向引用组件的父组件输出数据的能力,比如输入框能将输入的数据回传给父组件,按钮组件能够通知父组件执行某些callback操作等。
三、组件实现
首先看看最终实现效果:
根据『组件解析』可以画出组件UML图,如下:
代码实现
index组件
引入的父组件,如下图所示:传入树形组件展示数据treeData
、默认展开的父节点expandedKeys
Tree组件
在设计中,Tree.vue组件维护组件自身的状态,即每个节点的展开和折叠状态isFlod
,同时对数据进行传入参数Props
处理,生成结合了组件自身状态的用于渲染的数据。
组件代码如下:
在设计中,Tree.vue
只会对当层的数据进行处理,然后对处理的数据进行渲染,父节点中的子节点数据处理让子节点自己执行,这也算是单一职责
的一种体现;
-
setCurrentRenderData
函数:遍历传入的树形组件展示数据的当层数据,确定当层中每个节点的状态isFold
,最后返回一个全新的渲染数据renderData
,该数据用于当层节点的渲染,操作renderData
并不会对Props
数据产生影响,保证了数据单向传递
的原则。 -
组件的自我调用。为了让组件自己能够调用自己,我们给予组件一个确定的名称
name:'tree-node'
,然后组件就可以通过<tree-node></tree-node>
方式调用自己本身了; -
通过组件自身状态
isFold
对组件展示样式和操作进行控制,操作只会改变自身状态,不会对传入参数进行修改;<div :class="node.isFold ? 'node-item node-color' : 'node-item'" @click="() => node.isFold = !node.isFold" >{{ node.title }} </div>
-
无论是在引用组件
index.vue
中,还是在Tree.vue
自身调用,组件都保持了一致性,这个一致性体现在传参的一致。从侧面也是对于封装
原则的实践:// index.vue import TreeVue from './component/Tree.vue'; <tree-vue :tree-data="treeData" :expanded-keys="[1, 6]" /> // Tree.vue <div v-show="node.isFold"> <tree-node :tree-data="node.children" :expanded-keys="expandedKeys" /> </div> ··· export default defineComponent({ name: 'tree-node', ··· })
-
无论是组件名称、还是变量名、函数名,基本上都遵循着
富有意义
原则,让代码一目了然、清晰易懂;
总结
上述代码只是对『树形组件』的简单实现,一个完整的『树形组件』也并非上面呈现的那么简单,主要目的并不是写一个组件,而是通过组件的开发让大家能够理解 前端组件化设计和开发的原则和细节。
同时也让大家熟悉一些组件化开发的方法和思想,比如开发前的组件分析、组件的UML结构设计等,看完本篇文章希望都能有所收获!