组件开发方案
npm组件化开发的背景
- 随着技术的发展,开发的复杂度也越来越高,传统开发模式总是存在着开发效率低,维护成本高等的弊端。(界面开发太多,风格样式随时都可能调整,如果要调整,可能所有的项目都需要调整,牵一发而动全身)
- 项目越来越多,针对项目进度以及时间要求每个人对项目样式的支持度不是很高,需要一个统一的模式进行管理,提升开发人员的工作效率以及减少bug的产生,让开发人员能够更好地投入到业务开发中,发现组件化开发非常必要
组件化开发的优点、缺点
- 前端的组件化开发,可以很大程度上降低系统各个功能的耦合性,数据相互独立,并且提高了功能内部的聚合性。这对前端工程化及降低代码的维护来说,是有很大的好处的,内部结构密封,不与全局或其他组件产生影响,特别是针对逻辑复杂的功能能够进行拆分,更好排查问题。(就像电脑零件一样,针对各个零件的问题进行调整)
- 耦合性的降低,提高了系统的伸展性,降低了开发的复杂度,提升开发效率,降低开发成本。
- 因为功能已经完善,项目中使用的组件能够让人员更少的接触组件内部通用的逻辑,更加投入到业务的开发,提升大家的开发效率,减少bug的生成,还能让新人员更好、更快的接入公司的开发任务。
- 目前很多公共组件和项目依赖很多相同的node_modules,如果对组件开发经验不足,中间配合起来,版本不一致可能会导致node_modules出现未知的问题,各个版本针对不同环境也要做区分。
组件化开发遵循的特性
1.标准性
- 任何一个组件都应该遵守一套标准,可以使得不同区域的开发人员据此标准开发出一套标准统一的组件。
2.专一性
- 设计组件要遵循一个原则:一个组件只专注做一件事,且把这件事做好。
3.拆分性(需要开发人员来把控)
- 一个功能如果可以拆分成多个功能点,那就可以将每个功能点封装成一个组件,当然也不是组件的颗粒度越小越好,只要将一个组件内的功能和逻辑控制在一个可控的范围内即可,大组件也就是页面又叫做视图,用起来更加方便(不必通信),小组件更加灵活(小组件是块砖哪里需要往哪搬)。
- 页面上有一个 Table 列表和一个分页控件,就可以将 Table 封装为一个组件,分页控件 封装成一个组件,最后再把 Table组件 和 分页组件 封装成一个组件。Table 组件还可以再拆分成多个 table-column 组件,及展示逻辑等。
4.可维护性
- 针对以后可能出现的功能要有预见,尽量做到向下兼容以及新功能的添加能够很好的进行完成开发,尽量使用一个组件完成功能,实在不行只能分一个新功能组件进行开发完成,如果有不可逆的问题需要调整,还需要严格按照npm版本号标准管理添加版本。
5.可配置性
- 一个组件,要明确它的输入和输出分别是什么。
- 组件除了要展示默认的内容,还需要做一些动态的适配,比如:一个组件内有一段文本,一个图片和一个按钮。那么字体的颜色、图片的规则、按钮的位置、按钮点击事件的处理逻辑等,都是可以做成可配置的。
- 要做可配置性,最基本的方式是通过属性向组件传递配置的值,而在组件初始化的声明周期内,通过读取属性的值做出对应的显示修改。还有一些方法,通过调用组件暴露出来的函数,向函数传递有效的值;修改全局 CSS样式;向组件传递特定事件,并在组件内监听该事件来执行函数等。
- 在做可配置性时,为了让组件更加健壮,保证组件接收到的是有效的属性、函数接收到的是有效的参数,需要做一些校验。
组件拆解
- 组件是相对独立的可复用逻辑单元,多个组件相互作用形成了完整丰富的页面,组件化的思想将前端工程提高到了一个新的高度,其内在表现是将页面进行分割与抽象,达到高复用,易维护的目的,同时,对组件的可靠性,可预测性也提出了要求。整体而言,组件是提高开发效率的利器,下面将探讨一下组件的构成;
- 组件可以看做一个简单的I/O模型,组件处于面对用户第一线,具有交互特性,因此能接收与展示相关的数据;组件与组件相互协作,数据流通以对外接口为通道。组件内部主要包含视图、逻辑、样式与状态四个部分,功能的差异也导致不同组件的具体构成不尽相同,例如纯展示组件只要视图层与属性,容器组件有逻辑、状态、属性,但是没有视图层,这里不区分具体组件的职能,统一讨论所有构成部分;
1.接口
- 接口对外提供具体功能与扩展,对内传递外部属性,对用户不可见,但承担着桥梁的作用,是各组件组合的粘合剂,因此接口的设计十分重要,可靠的输入输出是组件稳定的基石,功能丰富的接口也将提高组件的使用体验;
2.交互
- 组件负责与用户的交互工作,提供可操作的功能,同时收集用户输入,然后对数据进行加工,重新给用户呈现新的数据,因此对用户体验提出了高要求,快速响应,界面友好,操作便捷,是优秀组件需要包含的特性,此次组件的开发也将对以上几点做针对性的设计,为了满足快速响应,将组件的执行任务进行优先级的分解,保证组件响应的快速,同时整体对组件进行重新设计,全新的视觉元素与交互规范,势必会提高用户体验与开发效率;
3.视图
- 视图是用户可视化的界面图形,良好和统一的外观能提高用户的使用体验,通过公司组件化视觉规范来提高组件的视图层表现;
4.逻辑
- 逻辑是组件功能与外观的基础,对数据状态进行处理,呈现出组件的不同表现形式,可复用的逻辑将简化组件的结构,同时更加利于维护,此次将公司内部系统常见逻辑进行封装与抽取,实现逻辑层的独立;
5.样式
- 样式是属于视图层的范畴,单独将样式提取出来,是为了更好的维护,对目前样式进行系统化的整改,将减少日常开发对样式的依赖;
6.状态/属性
- 状态/属性是一系列I/O和逻辑处理的内在体现,状态/属性的结构设计也决定了组件功能与实现的难易程度,在后续组件设计过程也将具象化状态与属性值;
组件体系
- 根据组件层级与功能划分,形成如下的组件体系:
- 底层支撑上层的组件,底层组件提供基本功能,上层组件在此基础上扩展与组合,形成满足特定场景的高聚合组件,组件库的使用人员主要集中在页面和容器级别的组件层级上,从而减少对底层基础组件的依赖;
版本管理
分为三个版本
- 主版本号(A):当你做了不兼容的 API 修改,0表示处于开发阶段;
- 次版本号(B):当你做了向下兼容的功能性新增;
- 修订号(C):当你做了向下兼容的问题修正。
~允许小版本迭代
- 如果有缺省值,缺省部分任意迭代;
- 如果没有缺省值,只允许补丁即修订号(Patch)的迭代
eg.:
- ~1.2.3:>=1.2.3 <1.3.0
- ~1.2:>=1.2.0 < 1.3.0(相当于1.2.x)
- ~1:>=1.0.0 <2.0.0(相当于1.x)
- ~0.2.3:>=0.2.3 <0.3.0
- ~0.2:>=0.2.0 <0.3.0(相当于0.2.x)
- ~0:>=0.0.0 <1.0.0(相当于0.x)
^允许大版本迭代
- 允许从左到右的第一段不为0那一版本位+1迭代(左闭右开);
如果有缺省值,且缺省值之前没有不为0的版本位,则允许缺省值的前一位版本+1迭代
eg.: - ^1.2.3:>=1.2.3 <2.0.0
- ^0.2.3:>=0.2.3 <0.3.0
- ^0.0.3:>=0.0.3 <0.0.4
- ^1.2.x:>=1.2.0 <2.0.0
- ^0.0.x:>=0.0.0 <0.1.0
- ^0.0:>=0.0.0 <0.1.0
- ^1.x:>=1.0.0 <2.0.0
- ^0.x:>=0.0.0 <1.0.0
锁定(控制)版本
package-lock.json或是yarn.lock。
在npm的版本>=5.1的时候,package-lock.json文件是自动打开的,意味着会自动生成,
package-lock.json(官方文档)可以理解为/node_modules文件夹内容的json映射,并能够感知npm的安装/升级/卸载的操作。可以保证在不同的环境下安装的包版本保持一致。听上去很不错哈,实际使用中,大部分它的表现确实不错,可是如上述问题:我手动修改了package.json文件内依赖的版本,package-lock.json就没那么聪明(至少目前是,未来会不会变聪明就不可知了),且不会变化。
如果你真的想保证你的包版本在各个环境都是一样的话,请修改下package.json中的依赖,去掉默认前面的^,当然这样的话,你就没法自动享受依赖包小版本的修复了,问题来了,在什么情况下选择哪一种呢?
在依赖包严格按照版本规范来开发的,你可以使用^来享受包的最新功能和修复。这也是推荐的。
在你不可知或已知依赖包不是那么规范的情况下,或许它在一个小版本(patch)做出不兼容更改(不兼容更改在beta等先行版本中一定[墨菲定律]会发生),那么这个时候,你应该把这个依赖包的版本在package.json上锁住版本,而不应该把它交给package-lock.json来处理
记住一点,绝对不要在生成环境下使用beta等先行版本依赖包,因为如果那是你的私有项目,它会在未来的某一刻坑害了你,如果这是你的共有项目,那么,它一定会在未来的某一刻对你的所有用户做出致命的坑害行为
初期开发
实现如图:
1.划分组件
根据组件具体职能不同,可以划分为如下结构:
- 框架型组件:针对公司项目中组件特定的逻辑功能需要公用的组件可以提取为业务组件,只保证目前各项目框架的公共业务,不掺杂其他的项目的逻辑(fulu商户侧,fl-pro运营侧)
- 功能型组件(Table、Form,目前页面最能体现的组件):都是提供1个基类,其余进行派生扩展的机制针对实际场景进行开发
- 常规组件(Checkbox、Input、Radio等):根据定制化样式进行开发
2.提取项目公共页模版
由公共函数类、公共模块类、当前页面组成,所有页面引用公共组件,公共组件继承公共类,开发者不需要写过多公用的代码,开发类由被基类、继承类的公共组件,使用公共组件的的页面三块组成。
- 1.CommonPage:基类,将公共函数(初始化加载、显示隐藏弹框、查询等)与state(页面需要使用的变量)提取公用。
- 2.CommonPageDom:公共组件,继承公共类,可以建立多种组件,自适应各种页面。
- 3.TestPage:引用公共组件,可以通过传入配置文件进行动态加载
页面划分
- 1.普通页面不带增、删、改
- 2.普通页面带增、删、改、查、弹框
- 3.带tab切换的页面
- 4.其余待扩展的页面
3.提取公共函数
很多函数在我们项目中都会频繁使用,所以需要提取公共出来,方便使用
- 1.验证函数
- 2.数字文字的处理,数组的合并,类型的检验等
- 3.其余可提取的公共函数
4.初期能达到的情况
- 1.UI方面:公共组件样式、交互完全和公司需要的风格一致
- 2.功能开发:开发者只需要在当前引用组件,传递相应参数就能生成想要的页面
中期开发
一键配置开发
- 1.创建页面1键生成对应页面的组织结构,包括页面文件夹还有models,services,routers等
- 2.通过传递的配置参数给组件,直接生成页面
后期开发
通过拖拉拽直接进行开发
- 1.通过开发的工具直接托拉拽进行生成页面
持续优化
脚手架持续优化
- 1.依赖包的更新,打包的持续优化
- 2.项目使用技术的持续更新
各个流程的优化
- 1.单元测试,自动化测试
- 2.前端自动化部署,动态修改线上代码
- 3.项目性能优化(jsperf、yslow、pagespeed)
- 4.组件功能优化
- 5.公共函数库持续迭代