前端组件人人都在用,人人都在封装。目前已知的几个大型开源组件库也并没有给出具体的封装规范。但是根据其开放的组件样式、使用规则等我们也可以参考出一些具体的封装要求。延伸到业务和组件的使用场景,其要求可能又不完全一样。
本文章旨在以StoryBook为展示背景条件下,规范出一套基本的前端组件封装要求,提供一套更加适合大众使用的前端组件封装细则,供大家参考。
组件的分类、命名、文件结构
组件的分类
基础组件
基础组件可以供多个项目使用,可配置项更多,展示形式及功能更灵活、丰富。
作为基础组件,可以是业务组件封装的基础,默认的UI可能不能够直接用于项目,但是其功能及提供的配置参数,可以满足我们在此基础组件上封装业务组件。
展示组件
SrollView、盒子模型、Swiper、提示语等功能组件
confirm弹窗、toast弹窗、全页面蒙层、滚动至顶部、tips、tab、swiper等
业务组件
业务组件主要是为了能在更少时间更少配置的情况下,快速搭建同一业务条线下相同风格的的页面。减少开发成本,组件的利用率可能不会很高,但是使用成本却大大降低,因为它更贴近业务,包括接口数据格式和字段名称、UI样式等,都可以不单独配置。
后台页面组件
后台组件一般设计风格都高度统一,对业务条线区分不细致。封装的组件利用率更高。但是,一般而言,目前开源的组件库,例如element-UI就已经很大部分基础组件可以支持后台系统使用。
封装主要针对具有业务特性的组件
例如:类目选择、图片上传、视频上传、toast提示、confirm提示等组件ToC页面组件
首页组件 金刚区、瀑布流、公告位、轮播图等
单品页组件 图片缩放、楼层盒子、流程图、推荐位、详情、视频、倒计时等
列表页组件 搜索项、地址、多选、单选、价格滑块、地址回填、卡片列表、分页加载等
其他页面 信息填写、表单提交、地址选择等
公共业务组件 搜索框、底部tab、空提示、错误提示d等
组件的命名
凸显作用
如果作为基础组件,命名可以大众,例如:Button、 Toast、Confirm、 Tips、 srollView等
如果作为业务组件,命名需要有特定开头,例如 :- 首页组件-IndexConfirm、IndexSwiper等
- 单品页组件-ItemSwiper、ItemRecommend、ItemVideo等
- 如果组件不通用,又分了特定的业务条线,则需要增加业务前缀;例如:PMItemSwiper、PMItemRecommend、PMItemVideo、FangItemSwiper等
首字母大些
组件的命名需要统一风格,都以大些英文开头
文件结构
基于组件的分类和功能,文件结构清晰可以大大提高组件的查找及定位成本。所以,基本的文件结构需要构建
1 | ├── .storybook # 入口文件 |
ClassName命名
CSS 命名使用 BEM 命名规范,BEM 代表 “块(block),元素(element),修饰符(modifier)”
命名模式
BEM 约定的命名模式为:
namespace-block__element–modifier
- 中划线: 仅作为连字符使用,表示某个块或者某个子元素的多单词之间的连接记号;
- __ 双下划线:用来连接块和块的子元素;
- – 双中划线:用来描述一个块或者块的子元素的一种状态;
命名空间(namespace)
命名空间用来区分组件类别:
- yp: yolkpie缩写,表示通用组件;
- pm: 拍卖业务组件(太长了,要不要换个短点的);
- fang: 房产业务组件(更长…);
…
块(block)
一个块就是一个组件。 块的命名规则如下:
块的名称使用类名,不能是 ID;
块的名称与组件类名一致,小写,多个英文单词用小驼峰(如 topSwiper);
块的命名要语义化,如果不是通用的组件,尽量不要占用通用命名(如 list、 item等);
…
元素(element)
块中的子元素是块的子元素,并且子元素的子元素在 BEM 里也被认为是块的直接子元素
元素的命名规则如下:
使用 js 控制的元素要以 js 开头;
元素的命名也要语义化(比如按钮,要尽量说明功能,推荐使用confirmBtn 而不是 btn)
小写,多个英文单词用小驼峰(如 confirmBtn、jsCountdown)
…
常见元素命名约定如下:
wrap / container: 包裹容器
header: 头部
footer: 底部
content: 内容
title: 标题
label: 标签
btn: 按钮
text: 文本
img: 图片
icon: 图标
input: 输入框
…
修饰符(modifier)
一个修饰符可以理解为一个块的特定状态,标识着它持有一个特定的属性,如按钮的大小及选中状态等
修饰符的命名规则如下:
修饰符尽量使用形容词
…
常见状态命名约定如下:
- 尺寸:small / normal / big
- 选中状态:selected
- 激活状态:active / focus
- 禁用状态:disabled
…
书写原则
原则上不要出现两层以上选择器嵌套
出现多层嵌套时,有两种处理办法,一种是用命名来解耦,所有类名都为一层,另一种是检查下是否需要创建新块
(1)用命名来解耦
1 | <div class="yp-block"> |
(2)创建新块
1 | <section class="paimai-comments"> |
组件与组件嵌套时通过在子组件上增加 BEM 类名控制子组件样式
1 | // 推荐 |
样式书写
- 最外层盒子不写宽高,使用自适应 (自适应)
- 使用父套子的方式,给类名加权重和作用域,以保证绝对的样式‘内部性’
- 尽可能使用flex布局,不要写死宽高
- 避免使用!important写死样式
- 必要时限制max或者min
- 避免使用标签选择器
- 有暴露外部的样式书写入口
- 统一使用scss书写
- 遵循先盒子主体样式,后渲染样式,即遵循先定位、宽高、display等,再font-size、color、缩进,边框,背景色等书写顺序
- 可以做适配,例如IPhoneX
- 避免为0值制定单位,例如用 margin: 0; 而避免使用 margin: 0 px;
- 对于小于1的值,省略前面的0,例如 .5 代替 0.5
- 避免过量的使用简写,防止样式覆盖,例如 使用 background-color: red; 代替 background: red;
- 对于 z-index 的值,在可控范围内,尽量写的小一点,分层级时区分数量级,同一层级,使用同一数量级值,(除弹窗外,不高于100;弹窗类最高999)
- 所有的声明都要以分号结尾,最后一个是可选项,但是也要书写,避免出错
- 每条声明之后,都要有一个空格
- 声明的花括号应该单独成行,单行声明除外
- 分区块增加注释分割,书写顺序符合文档流顺序
- 书写托底的默认样式
外部传参数
- 1. 数据结构标准化
即数据类型符合一般渲染逻辑;例如:轮播图使用数组结构、基本信息使用对象、图片地址使用字符串等 - 2. 容错处理
内部数据结构要紧凑、稳固,接收外部传入参数时,做各种格式化处理,此处包括(托底,容错,类型判断,防止因外部输入格式不对导致组件内部报错), - 3. 可扩展
为后续组件扩展留余地,不把数据结构写的太死。例如二位数组处理,数据分组格式化等 - 4. props需要指定类型
视情况添加默认值及必填
1 | props: ['name', 'style'] |
- 5. 避免出现不同属性控制相同功能、样式
1 |
|
- 6. 以Object代替基础类型传参数
1 |
|
内部变量
语意化(例如:size,表示组件的大小)
建议以_开头 变量名用小驼峰命名法; 如_tabList
要有默认值,需要外部必传的变量可以无默认值,但使用前必须做校验,且给出必要提示:如vue的el属性
组件内使用的定义组件级变量,function 内使用变量只定义在function内
常量 以const 定义,可变变量以 let定义,尽量少用var,防止变量提升引起的问题
对同一变量的操作,逻辑尽量放到一起,易读易理解
方法命名
遵从 动-谓-宾 原则
常用动词
单词 | 含义 | 单词 | 含义 |
---|---|---|---|
get | 获取 | set | 设置, |
add | 增加 | remove | 删除 |
create | 创建 | destory | 移除 |
start | 启动 | stop | 停止 |
open | 打开 | close | 关闭, |
read | 读取 | write | 写入 |
load | 载入 | save | 保存, |
create | 创建 | destroy | 销毁 |
begin | 开始 | end | 结束, |
backup | 备份 | restore | 恢复 |
import | 导入 | export | 导出, |
split | 分割 | merge | 合并 |
inject | 注入 | extract | 提取, |
attach | 附着 | detach | 脱离 |
bind | 绑定 | separate | 分离, |
view | 查看 | browse | 浏览 |
edit | 编辑 | modify | 修改, |
select | 选取 | mark | 标记 |
copy | 复制 | paste | 粘贴, |
undo | 撤销 | redo | 重做 |
insert | 插入 | delete | 移除, |
add | 加入 | append | 添加 |
clean | 清理 | clear | 清除, |
index | 索引 | sort | 排序 |
find | 查找 | search | 搜索, |
increase | 增加 | decrease | 减少 |
play | 播放 | pause | 暂停, |
launch | 启动 | run | 运行 |
compile | 编译 | execute | 执行, |
debug | 调试 | trace | 跟踪 |
observe | 观察 | listen | 监听, |
build | 构建 | publish | 发布 |
input | 输入 | output | 输出, |
encode | 编码 | decode | 解码 |
encrypt | 加密 | decrypt | 解密, |
compress | 压缩 | decompress | 解压缩 |
pack | 打包 | unpack | 解包, |
parse | 解析 | emit | 生成 |
connect | 连接 | disconnect | 断开, |
send | 发送 | receive | 接收 |
download | 下载 | upload | 上传, |
refresh | 刷新 | synchronize | 同步 |
update | 更新 | revert | 复原, |
lock | 锁定 | unlock | 解锁 |
check out | 签出 | check in | 签入, |
submit | 提交 | commit | 交付 |
push | 推 | pull | 拉, |
expand | 展开 | collapse | 折叠 |
begin | 起始 | end | 结束, |
start | 开始 | finish | 完成 |
enter | 进入 | exit | 退出, |
abort | 放弃 | quit | 离开 |
obsolete | 废弃 | depreciate | 废旧, |
collect | 收集 | aggregate | 聚集 |
驼峰式命名
使用英文拼写
不要自创缩写,不会可以全拼上去,不怕名字长
避免误导,同一组件中不要使用过于相似的命名
方法暴露
尽可能多的暴露方法,关于内部的一些方法定义,如果可以使组件更具灵活性,可以考虑将内部方法暴露出来。例如:功能强大的swiper组件,就暴露了很多内部定义的方法,供用户灵活的定制自己的组件
暴露的方法如果接收多个入参,就将入参以对象的方式进行传入,方便扩展
暴露的方法名最好不要与公共方法名重复了,以免混淆
readme里面,写明使用方法
公共资源
工具类库的方法,使用统一引入方式,即安装引入
公共样式也使用统一引入,不再单独书写
使用工具类库的两种方案
使用目前已有的 @yolkpie/utils npm包安装到组件库
1 | // 安装依赖包 |
可能产生的问题是,由于是将各个类型的公共类方法统一打包到utils里面(通过Rollup 将小块代码编译成大块复杂的代码),可读性不高
直接在组件库里建立utils文件,阴影各个类型的公共方法
工具类库方法介绍
1 | utils |
1)array.js 数组相关处理函数
2)axios-jsonp.js jsonp跨域数据请求方法
3)bom.js浏览器或系统相关处理函数包含isIphoneX、isSupportWebp、getEnv(获取环境信息)等方法
4)cache.js,缓存,本地存储处理操作封装(sessionStorage和localStorage
5)cookies.js,获取cookie、设置cookie和删除cookie
6)dom.js,dom相关处理函数
7)event.js,包括事件监听器、绑定事件等
8)fixMask.js,移动端浮层内滚动窗体不滚动的JS处理
9)floatCalculate.js ,浮点数计算
10)formatDate.js 日期格式化
11)functional.js 函数式编程相关函数
12)global.js 对象相关处理函数,包括根据键值获取键名、深拷贝等
13)math.js 数字相关处理函数
14)rem.js 移动端尺寸适配
15)string.js 字符串相关处理函数
16)tools.js 一些公共方法,包括throttle和debounce
17)url.js 链接相关处理函数,包括获取链接参数方法等
公共样式
1)base.scss,一些基础样式
2)flex.scss,flex布局相关样式
3)border.scss,1px像素问题方案
4)font.scss,字体的引入
5)animation,动画