前端代码0bug经验分享
- 如何写好代码其实是一个很大的话题,本次仅分享一些自己的经验之谈,有其他idea的欢迎交流
- 在我看来,写好代码更多的是逻辑思维的体现,而这,紧靠一两次分享是无法提高的,我只能尽可能的将可能可以复制的经验进行分享
- 本次分享不会涉及太多你使用的框架的具体使用细节,技术选型总是千变万化的,我们需要的是抓住不变的部分
- 更希望大家多去锻炼逻辑思维和学习一些编程方法论,毕竟编程语言只是工具而已
- 当然,经济基础决定上层建筑,技术基础和上层框架本身也是很重要的,这个就留待各位自行求索了
开发前
一、项目
1.掌握(注意是掌握而不是了解)你所开发的产品的主要功能点和核心功能点,作为研发中与客户最为接近的环节,可以称得上是公司门面的一部分,前端务必要对产品有深刻的理解 ——
(1)产品定义
(2)各个主要功能点和核心功能点的数据的来龙去脉(后台的时序图及前端对应模块涵盖哪些)
2.了解后台架构,若是微服务的话,思考或和后台同事探讨各个服务的职责范围,了解一些常见中间件的作用
问一个小问题,你知道项目中的图片是如何存储的吗
3.掌握项目主要功能模块的代码
-
- 路由设计
- 登录、用户权限设计
- 数据分层 —— 如请求层(各个interceptor作用)、适配层、业务层
- 通用业务代码 —— 是否有通用组件、通用hooks、共用type等
- 配置项代码
- 各个业务模块代码
4.技术栈
-
- 至少了解每一个技术出现是为了解决什么问题、怎么解决的(不需要看源码,看它暴露的api就好了)
- 了解每一个技术的核心思想,如react就是一个data驱动的UI框架、redux就是一个单向数据流的状态管理工具
二、需求评审
1.尽可能早的介入
-
- 可以实现的能否做得更好
- 可能可以实现的提前预研、出demo等
- 不能实现的或者交期无法满足的商量让步方式
2.剖析需求
-
- 了解产品(非产品经理)真正的需要是什么 —— 现场(客户)的需求是什么、是在什么场景下提出的、最终是要解决什么问题,同时可以帮助产品经理和测试经理梳理该需求的核心到底是什么 —— 有时候确实三方理解会出现偏差,但核心把握住,一般后期修改也相对容易些,不至于重构
- 结合项目实际情况,分析需求的来龙去脉 —— 数据需要怎么进来(一般是在设置\任务中)、进来后怎么展示(数据展示页、是否有实时消息等)、是否需要增加权限相关边缘功能等、考虑该数据增删改可能带来的影响有哪些
- 该需求是否会对原有的功能有影响,可调和的 —— 可能可以做合并操作,不可调和的 —— 需要提前同产品经理、测试经理确定如何处理,替代or保留
- 感兴趣的话,可以和后台同事聊一聊他们的设计、表结构、数据流
3.UI评审
-
- 从上到下,从左到右,事无巨细的浏览一遍,对于其要实现的主体功能有个把握
- 基于主体功能在脑海中快速过一下可能的开发设计方案
- 再考虑分支功能可能需要往刚刚的设计方案中增加些什么,是否需要重构设计方案
- 是否存在矛盾或者难以实现的功能点,及时提出调整
- 在屏幕适配上是否满足目前产品的主要需要(满足1366 * 768及1920 * 1080),当前设计稿基本都是基于1920 * 1080来的,对于一些内容繁多的页面来说,可能不好适配1366 * 768,需要及时商量如何调整或进行额外布局适配工作
- 上述过完后,需要过一遍用户如何使用的场景 —— 评估功能易用性、评估未显示在页面上的额外交互的空白地带、同时看是否需要修正自己的设计方案
三、开发设计
1.组件设计
(1)组件拆分
非必要不拆分,大部分情况下建议先写代码,然后视情况做拆分工作 —— 在如下几种情况下建议拆分
-
- 多处共用 非UI相关的,建议抽成通用hook进行维护
- 业务逻辑较为复杂导致占据该文件的大部分代码,证明该部分代码内聚性较高,考虑抽离
- 单文件代码超过400 - 600行
(2)可否复用现有组件或hooks
-
-
DRY —— Don't Repeat Youself
-
完全一样功能的,直接复用,后续也便于统一修改
-
有点出入的,考虑略微修改以实现调用方的兼容
经过多次扩展后,若一个组件出现了10个以上props或者内部已经一堆的if else了,务必及时再次审阅拆分开来
-
有较大不同的,但又有不少相似的,建议copy一份另外做个组件
-
api请求函数,见过太多反复编写的了,对于接口层,动手前搜一下,看是否存在了,若存在,优先复用,一是节省时间,二是中心化便于后续可能的修改
2.数据设计
当代前端应用开发的核心就是做好数据的管理 —— 增、删、改、查
(1)state
(2)props
(3)衍生数据 —— 保证state或者props的精简和纯粹性
(4)redux or context —— 多组件共用的,优先考虑提升到父组件,只有当共同的父组件不属于该业务范畴或者很远时才考虑使用
(5)受控 vs 非受控,对于antd form组件,非必要不受控、直到某个form item不受控就无法满足业务需求时再考虑受控
可能的话,统统单一数据来源,尽量避免某个渲染状态是多个相似数据的复合结果,状态越少越好
3.交互设计
-
- 多浏览一些交互比较OK的网站,包括但不限于各个知名UI组件库、各个大公司相关产品的官网,培养自己的审美及增加对于何谓好交互的积累
- 多站在用户角度进行考虑,他们会如何使用这个功能点,是否需要增加一些提示性的交互,如
cursor: not-allow
提示某些功能暂时被禁用(这种的话需要同时注意是否只实现了css,漏了js的事件了)
开发中
一、自顶向下编程
先主干,后枝干
import ModuleStyle from './index.module.scss'; function Page() { const [shareData, setShareData] = useState(0); return ( <div className={ModuleStyle['page']}> <Component1 data={shareData} /> <Component2 data={shareData} /> <Component3 data={shareData} /> </div> ); }
这里可以把外层框架搭好,如整体layout,不同组件间需要共享的数据管理等
提一嘴,css建议务必采用module css或类似方案(如styled components),否则容易覆盖
import ModuleStyle from './index.module.scss'; function Component1(props: {data: number}) { const [data, setData] = useState([]); return <div className={ModuleStyle['component1']}>...</div>; }
联调前,可以先把静态页面做好,有时间的话,增加处理下加入异步请求(可以考虑自行使用setTimeout模拟或者自建mock服务)后的四种常见状态 ——
-
- 请求前
- 请求时
- 请求后
主干完成后进一步处理分支功能 —— 如是否需要根据props做一些重新请求或者重置的操作、或者要查询条件变更的处理之类的
联调时依旧要把主干功能和分支功能过一遍,同时注意下后台数据的校验处理,防止reference报错
二、react等
如下几个核心个人认为需要完全掌握
-
官方文档
-
近一两年的一些比较重要的版本更新我都会去拜读一遍
-
工作流及类组件的生命周期
-
setState
- 同步、异步,建议了解下底层原理
-
合成事件
- 原理及可能的坑
-
hooks心智模型
“忘记你已经学到的。” — Yoda
-
Hook API 索引 – React (reactjs.org)
useEffect 完整指南 — Overreacted dan大神的博客,建议订阅
-
类组件何时实例化、何时会调用render、函数组件何时被调用 —— 重点,建议深入理解
-
警惕反模式,若使用中出现了反模式,那大概率会出现问题
凡是可能出错的事就一定会出错。 —— 墨菲定律
举两个例子
-
破坏react props不可变更的原则,在子组件中自行修改props
-
破坏redux的数据更新必须通过dispatch action来进行的原则,自行随意修改某个属性
三、良好的命名和注释习惯
代码是写给人看的,不要以为自己能看懂就行,大部分人在两个月后将不知道自己曾经写了些什么鬼东西
-
优先考虑命名和类型即注释,不要担心名称过长,react还用了getDeriveStateFromProps这么长的名字,但通俗易懂
export function getEventList(cameraIds: number[]) { // balabala }
警惕,不仅是函数名,变量名也要注意,见过太多data1、data2这种你不深入看都不知道干啥的,还有尽量避免leftList、rightList,这种可能跟UI布局挂钩的,有一天左右切换,直接让你疯狂
推荐名称与业务挂钩,如getExconvictUnregisteredList —— 获取未登记前科人员列表
- 对于名称较难解释,或者还需要有必要说明的,使用JSDoc规范注释,vscode原生支持,简单的输入/**回车即可
/** * 该函数的功能描述 * 可以附带上大致的实现说明或者使用的注意事项 * @param a xxx参数 */ export async function somethingHardToSay(a: number[]) { // balabala }
-
对于一些不符合常识或者难以理解的务必要注释
-
TODO WARN 等标记
再结合第三方插件(如Todo Tree - Visual Studio Marketplace),在转测前可以过一遍自己开发的功能中是否有相关标记需要注意的
四、善用工具
1. 第三方库包
前端react社区中已经有很多轮子,建议优先搜索使用,需要注意通过多个信息来决定是否使用,即使最后定论不使用转而自己实现,也可以学习下它的设计思路并规避其缺陷
比如说你需要一个拖拽排序组件
(1)搜索及选择
google search first,最好英文搜索,react drag and sort
查看前几个一般就够了,到对应仓库查看更多信息,如react-dnd
重点关注如下几个信息:
1)项目使用数,个人觉得1000+就值得考虑尝试了
2)版本发布信息,最好是还在持续维护的(如近一年还在更新的)
(2)加分项
1)文档健全
2)使用ts编写(可以免于下载@type文件,或者类型文件与源代码版本不匹配问题)
1)善用npm trends: Compare NPM package downloads,极力推荐
特别是当遇到一些搜到的库包质量不是很高时,可以考虑用这个搜下,然后有一个related的列表,里面一般是一些相似工具的比较,比较容易挖到好东西。
2)直接在github中搜索
3)持续关注一些高质量的前端输出者,如关注一些公众号或订阅其博客等
5)不仅仅是UI组件,hooks生态也在逐步完善,多从streamich/react-use: React Hooks — 👍 (github.com)或ahooks - React Hooks Library等知名hooks库中挑选,一些常见场景早就有比较完善的解决方案了,不要再造可能有不少bug的轮子了。推荐一波ahooks的useRequest。
2.宇宙第一IDE
实用功能推荐
-
-
查找引用,shift + alt + f,甚至类型的属性的引用也能找出,对于没有严格lint的项目而言,简直就是福音
-
插件
-
-
上文提及的TODO Tree
-
Search node_modules,对于一些可能是源码的错误,且源码开源的话,能很方便找到
-
GitLens — Git supercharged,快速查阅行、文件变更,行提交记录实时显示
-
浏览器插件
-
-
使用React Developer Tools、redux-devtools-extension进行数据跟踪、性能优化分析
强烈推荐React Developer Tools中的profiler功能(Introducing the React Profiler – React Blog (reactjs.org)),能帮助你庖丁解牛般跟踪到每一次commit及其render的信息,从而建立更为完善的对于react模型的理解
-
2.单组件在本次profile中被记录了几次commit
3.这次update可能是由什么引起的(试验性功能,可能有部分错误,但已经基本可用了)
五、开发后
代码审阅
- 自己审阅已开发完的代码
- 是否存在功能性缺陷、是否存在性能性缺陷
- 前者评估其严重性,若是严重类,需要在转测前将其改掉,否则视交期安排酌情考虑是否变更
- 后者若可以快速优化的,则立即优化,否则在相关代码上加个TODO/WARN之类的标识,便于后续优化
- 没有问题的前提下,看是否有更好的方案,若该变更可在转测前完成,建议修改,否则可以自行总结在小本本上,下次类似功能尽量避免
- 各类边界条件审阅
- 控制语句审阅(switch case、if else),是否有未考虑到的case,该case是否会影响功能
- 是否存在功能性缺陷、是否存在性能性缺陷
- 有条件的话,同事间相互审阅
冒烟用例
尽可能详尽的过一遍冒烟用例
- 冒烟用例一般是一些主体功能的提炼,若不能通过的话,本次开发就不算成功的,需要修改
- 代码最终实现可能和测试的测试方法有出入,需要三方对齐,如果你觉得自己的更有道理,完全不用改,尝试去说服产品跟你站在同一阵营,人人都是追求更好的交付的;若自己确实做得有问题,时间允许的情况下建议及时修改,否则需要同步相关信息到产品、测试、开发经理,评估严重性,再做进一步的计划
bug解决
- 定位bug原因
- 评估影响范围
- shift + alt + f,查找相关引用,可能还有引用的引用等,需要查找全
- 评估对使用方的影响,需要兼容处理的做兼容处理,需要当bug处理需要进行变更,所有有变更的地方做个记录
- 禅道单点解决时,附上影响的功能点及建议测试方法,或者私聊对应的测试同事也可,务必要cover all
- 扩展
- 横向 —— 在其他地方是否有类似问题,若有类似问题,并且是产品的核心功能点,建议协同测试一并解决掉并做记录
- 纵向 —— 引发该问题的更深层次的原因是什么 —— 如深入源码、推动第三方库包解决(有时间的话甚至可以尝试解决并提个pr)等
- 未来 —— 以后如何避免、是否要做成典型在组内分享