前端防御性开发
1. 前端防御性问题
在实现某种css效果时,往往我们能很快的完成css的编写,如文档溢出、垂直居中、各种布局等效果。但是当一个或一组元素具有动态,或者面对无法预测内容形式的时候,可能会出现样式不兼容,不适配导致塌陷,甚至异常。
2. 前端防御性实际情况
实际情况远比想象的复杂。以下列举了一些用户在使用链路上可能存在的“危机点”,这也是防御性开发中着重要考虑的“防御点”。归纳为两大类问题:
1.UI的防御性
- 防白屏 – 白屏时间小于1秒(小于200ms最佳)
- 防布局错乱 – 布局被动态内容撑垮
- 防极端内容 – 缺失 / 超长 / 连续字符 / 未转义
- 防慢 – 网络慢 / 响应慢 / 渲染慢 / 执行慢
- 防卡 – 卡顿 / 假死
- 防一致性问题 – 不一致的交互方式、图标、 标准组件等
- 防UI状态不全 – 五层UI状态栈(加载状态/空状态/部分缺失状态/出错状态/理想状态)
- 防样式污染 – 样式冲突,局部模块的样式影响全局
- 防Chartjunk – 可读性差的图表用法
- 防误操作 / 危险操作 – 对不可逆的操作二次确认+强提示
2.代码的防御性
- 防报错 – 语法错误 / 逻辑错误
- 防兼容性问题
- 防安全性问题
- 防意外输入和交互
- 防数据 – 防极端数据 / 无效数据 / 接囗变更
- 防代码坏味道 / 防工程腐化 – 代码复杂度 / 重复率 / 反模式 / 死代码等
- 防语法风格不一致
- 防代码冲突
- 防代码冗余
3. 如果做防御性开发
人是代码的创作者,提高代码防御性,写出高质量的代码,最终靠人。团队中的CR是从外部反馈来优化开发,而作为开发者,则更需要自己对自身的约束。
那应该从哪些方面去做防御性考虑呢?
1. 需求
- 功能实现问题
- UI和交互问题
2. 开发
- JS防御性原则
防御点 | 原则 |
---|---|
逻辑问题 | 1. 判断条件有误 / 忽略了必要条件 2. 循环 / 递归的退出条件 3. 显隐逻辑和跳转逻辑控制 4. 缺少校验或错判参数类型 / 空值 / 边界条件 5. 缺少对默认值 / 缺省状态的校验 / 判断 / 处理 6. 接囗调用逻辑和组合关系 7. 忽略一些组件之间的联动关系 |
全局副作用 | 1. 变更公共代码,对其他部分产生影响 2. 变更配置文件 / 全局变量 3. 代码的冲突和污染 4. 基础库版本升级 |
容错问题 | 1. 错误输入 / 特殊字符 / 数据类型的容错 2. 接囗返回值的不确定性 3. 接囗请求失败的容错 4. 缺少error boundary,避免导致白屏 5. 错误要上报 |
表单校验问题 | 前端校验条件不全 |
编译 & 依赖问题 | 1. JS编译漏掉对一些语法的处理 2. 本地和发布构建有差异 3. 本地和线上依赖版本有差异 |
兼容性问题 | 1. Polyfill不全 2. CSS兼容性问题 |
文案问题 | 1. 文案错误 / 不准确 / 折行 2. 国际化不完整 |
- CSS防御性原则
防御点 | 原则 |
---|---|
避免用JavaScript控制布局 | JavaScript实现的布局组件不要用。它会多出很多层没用的嵌套,同时把布局定义的很死,难以再用CSS控制。 永远没有原生的流畅,同时增加代码的复杂,容易用问题。除非解决一些必要的兼容性问题。l |
避免用float / position: absolute / display: table等过时的布局技术 | 优先用Flexbox/Grids布局。你会说绝对定位还是有用的。你要强迫自己不用,经过反复尝试过发现绝对定位是最优的选择那就用。重要的是有这个“强迫自己”的过程。 |
避免定高/定宽 | 固定宽/高最容易出现的问题是内容溢出。没必要通过定宽高对齐,可以利用Flexbox的位伸/收缩特性。一般情况下用最小宽/高、calc()、相对单位替代。同上要“强迫自己”不用。 |
避免侵入性的写法 | 避免影响全局样式,如:* { … }、:root {…} 、div { ….}等。避免影响通用组件样式,如:.next-card {…},如果要定制单加一个class名。不要直接修改全局CSS变量,把自己的CSS变量定义在模块的范围内。不要写z-index:999。一般1~9,防止被遮挡10~99,绝对够用了。不要在标签上定义style属性。不要在JS代码中做样式微调,这样今后无法统一升级CSS样式。只有完全不可修改的样式才能用!important,利用选择器优先级调整样式。 |
避免CSS代码的误改 / 漏改 | 将选择器拆开写,如.card-a, .card-b { … },写时方便,改时难,修改时容易影响其它元素,不如分开写(除非像css reset这种特别确定的情况)。将样式集中在一起,容易改错。保持CSS代码和文件在相应的层级上,同一模块的放一起。避免混入通用样式中,为了避免改错,允许适当冗余。用@media时,会集中覆写一批元素的样式,更新样式时非常容易遗漏。所以必须拆开写,和对应模块的样式放在一起。不要集中放在文件底部,或是集中放在某一个文件里。及时清除“死代码”。定义样式要写全,微调样式要写具体,如:.mod { margin: 0; } /* 其它地方需要微调时 */ .biz-card .mod { margin-bottom: 16px; } |
避免CSS样式冲突 | 限定作用范围。如,.my-module .xxx { … }。业务代码中的样式要加前缀,或借鉴BEM命名方式。如:.overview-card-title { … }。用CSS Module也可以。注意选择器的精确性。级层过长过于复杂的CSS选择器会影响性能,但要注意:有时需要精确选择某些元素,如仅选择一级子元素,.overview-card-content > .item { … }。 |
防止内容不对齐 | 应该说Flexbox侧重“对齐”,Grids是专为布局设计的。受字体、行高等因素影响(如图),用Flexbox实现对齐最可靠: 1、height / line-height 不可靠。 2、display:inline-block / vertical-align:middle不可靠。 |
防止内容溢出 | 包括文字 / 图表等内容在宽度变化时或是英文版下容易出现溢出。 1、图表要支持自动 resize。 2、图片要限制大小范围,如:max-width、max-height min(100px, 100%)、max(100px, 100%)(注意:min() / max() 兼容性:chrome 79+ / safari 11 / firefox 75) 3、不要固定宽/高。(见规则3) 4、不要在容器元素定义overflow:hidden防止内容不可见。宁可难看关键信息也要显示全。 5、用min-width:0防止Flexbox项被内容撑开。例如:html如下,.canvas的style是JS写死的,不可避免的溢出了(如图)。 <div class="wrapper"><div class="item"> <div class="canvas" style="width:300px;height:200px;">canvas</div></div></div> 这种情况下.item可以定义为: display: flex; min-width: 0; |
防止内容被遮挡 | 定义负值时(负margin / top / left),小心内容被遮挡,避免这么定义。定义margin统一朝一个方向,向下和向右定义,再重置一下:last-child。position: relative 平时很常用,发生遮挡时会造成链接无法点击。 |
防止可点击区域过小 | 小于32x32像素的可点击元素,通过下面的方式扩大可点击区域: .btn-text { position: relative; } // 比 padding 副作用小 .btn-text::before { content: ‘’; position: absolute; top: -6px; left: -8px; right: -8px; bottom: -6px; } |
防止内容显示不全 / 被截断 | 在定义overflow:hidden时,就要考虑内容是否有被截断的可能。一般不要加在容器元素上。防止长文字被生生截断,加省略号。UI实现过程中要对内容做出判断,哪些是不应该折行的,哪些是不应该省略的,如: white-space: nowrap; overflow: hidden; text-overflow: ellipsis; |
防止图片变形 | 图片被置于特定比例的容器中时,固定宽/高和约束最大宽/高,都可能会导致图片变形。 .head img { width: 100%; height: 100%; object-fit: cover; } 在Flexbox容器内,图片高度会被自动拉伸。因为不要定义align-items,默认是stretch。 |
防止图片加载失败 | 需要考虑图片加载慢或加载失败的情景。在图片的容器上加边或加底色。 |
Flexbox常见防御性写法 | Flexbox的默认表现比较多,不能简单的定义display:flex,或是flex:1。 1. Flexbox容器元素通常要做如下定义:要支持多行(默认是单行),交叉轴上垂直居中(默认是stretch),主轴上采用space-between,将自由空间分配到相邻元素之间。一般都要写上: display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; 2. Flexbox的盒子元素要定义间距。 |
3. 数据
- 请求失败问题
- 字段问题问题
- 状态不全问题
4. 性能
- 显示性能
- 运行性能
- 交互性能