最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 如何优雅地分析和防范前端 BUG?

    正文概述 掘金(ELab)   2021-02-25   432

    两个公式

    开发效率 = 1 - (思考时间+编码时间+debug时间+改bug时间) / 迭代总时长

    bug率 = bug数 / 测试用例数

    八种问题

    • 需求理解不透彻 视觉交互没过细
    • 语言基础待加强 错误异常未处理
    • 代码逻辑太混乱 边界情况欠考虑
    • 后端接口常变动 二次修改漏东西

    需求评审

    需求理解

    bug原因:

    • 开发和产品对需求的理解不一致
    • 前期经过沟通达成一致的需求,在开发后期忘做了
    • 多个模块关联同一份代码,需求文档只提到某一模块的改动,错估改动影响范围

    方案:

    • 同一功能有多种实现方案,思考尽可能多的可能性,给产品出选择题,在理解上达成一致。比如可做可不做的功能,交互文档中未提到的细节
    • 写Q&A list,根据自己对需求的理解,以提问的方式写下Q,在自己思考到解决方案或和产品,交互,UI确认后,写下对应的A,每一个问题尽可能单一明确,在开发过程中既当作实现方案,又可当成用例进行自测
    • 充分理解现有系统,评估需求改动的影响范围,对相似模块提高警惕性,找熟悉系统的人了解

    示例1:

    思考实现一个自定义输入框的删除文字功能 如何优雅地分析和防范前端 BUG? 有以下几种方式:

    • 退格键
    • 框选文字+退格键
    • 框选文字+Ctrl+X
    • Ctrl+A+退格键

    示例2:

    假设实现一个可根据题型进行排序的题目列表,以下是一个简单的QA list:

    • Q:题型排序需要实时保存吗?(和产品讨论)
    • A:实时保存。
    • Q:题型排序的实时保存方案?(思考方案)
    • A:首次进入按默认题型排序,经过题型排序后将顺序保存在本地,不走接口。
    • Q:本地缓存和接口中的题目顺序信息同时存在,题型顺序以哪个为准?(先讨论,后思考方案)
    • A:接口中的信息优先(讨论),先通过接口导出实际题目的题型顺序,再对本地缓存中存在的题型进行排序,更新本地缓存(方案)。

    在不断的讨论+思考实现方案的循环下,需求和思路会越来越清晰。

    技术实现

    根据需求难易程度区分为简单需求,复杂需求

    简单需求

    • 简单的增删改查
    • 展示多,交互少

    这类需求出bug率较低,且易解决,不做讨论

    复杂需求

    • 逻辑复杂

    bug原因:

    逻辑理解不清楚,思维混乱,导致写代码出错

    方案:

    写伪代码,将逻辑以代码的形式写出来,然后逐个去实现伪代码中的需求,每一个if里面尽量只有1个条件,方便理解

    示例:

    if(是作文){         
        if(在第一面的第一列){            
            if(当前面的总列数 - 1 >= col){                
                放到下一列        
            }else{                
                放到下一面的第一列        
            }    
        }else{            
            var 作文已占用的列数 = 0;        
            if(当前列只有这一个题目questionBox){                
                作文已占用的列数 = 1;            
            }        
            if(当前面的总列数 - 题目所在的列数 >= col - 作文已占用的列数){                
                正常跨页        
            }else{                
                放到下一面的第一列        
            }    
        } 
    }
    
    • 功能复杂

    bug原因:

    需要实现的功能比较复杂,不能一步到位直接想出实现的思路

    方案:

    • 正逆向思维推导,从易实现的功能和源数据开始正推,也可从结果开始逆推,然后去逐个思考每个功能点的实现方式
    • 构思出mvp版本,在此基础上不断完善功能
    • 拆分功能点,形成todolist,并附上优先级,在完成阶段性功能时, 逐一检查git提交文件
    • 和团队一起讨论解决方案,自己想出来的不一定是最好的,不成熟的方案会导致后期维护成本高,隐藏bug多

    示例1:

    录入编辑器功能比较复杂,大致流程是 OCR识别的题目结构 --> 编辑器结构 --> 编辑后的题目结构,可通过正向推导思考怎么从题目结构转化为编辑器结构,也可以通过逆向推导思考什么样的编辑器结构可以转化为题目结构,在推导过程中将功能做拆分,逐个实现 如何优雅地分析和防范前端 BUG?

    正向推导

    如何优雅地分析和防范前端 BUG?

    逆向推导

    示例2:

    假如项目的实现功能点较多,可以先完成mvp版本,在其基础上去拆分功能点,列出todolist,有以下2种方式:

    1. 按类似思维导图的层级结构方式列出,一个功能点可以接着拆出更多子功能点。
    2. 通过数据表列出,并打上标签(比如优先级、功能模块等),优点是分类、排序比较方便,更加清晰。
    • 基于第三方库的使用

    bug原因:

    • 库本身的缺陷
    • 多个开发者对库的使用方式的差异,导致后期维护成本增加,出现bug

    方案:

    • 使用第三方库前先看下issue list,看作者维护是否积极,大致浏览下前两页的issue,看自己用到的功能是否有提及
    • 看changelog日志是否规范,文档是否更新及时
    • 在多个库都能实现相同功能的前提下综合考虑前两项
    • 根据业务需要,可以对库进行二次封装,写成业务需要的api或组件。好处是业务相关的api或组件更容易被开发者所理解,并且统一了使用方式,减轻维护成本
    • 在综合考量实现成本和维护成本下,也可以选择自己实现

    示例:

    • @byted-edu/formula-mathjax 组件对 MathJax做了封装,免于繁琐的配置

    码前准备

    • 放松心态,专注防打扰
    • 多方业务同时进行时,列出每日计划,排优先级,设deadline提醒,来一件事情记一项,有条不紊
    • 如果被打扰太多次,白天处理动脑少的事,晚上处理动脑多的事,或者带回家做

    编程习惯

    语言基础

    this指向

    示例:

    var person = {    
        age: '12',    
        say: function(){        
            console.log(this.age);    
        } 
    }  
    var foo = function(func){    
        this.age = 15;    
        func(); 
    };  
    foo(person.say); // 15
    

    内存

    常见bug:

    • 递归死循环,导致堆栈溢出;内存泄露

    同步异步

    常见bug:

    • 执行顺序不清楚

    方案:

    了解事件循环的机制,清楚promise,setTimeout等的执行顺序

    引用传递

    示例:

    var obj = {    
        a: {        
            b: 1,    
        } 
    }; 
    var obj2 = obj.a; 
    obj2.b = 2; 
    console.log(obj.a.b); // 2
    

    运算符

    常见bug:

    • 对使用运算符后的结果计算出错

    示例:

    ++a; a++;
    相等比较和类型转换:'0' 0 false null undefined NaN
    

    错误异常

    SyntaxError(语法错误)

    常见提示:

    • Unexpected token

    方案:

    • 检查一下是不是少了括号,或者变量定义不符合规范

    Uncaught ReferenceError(引用错误)

    常见提示:

    • xxx is not defined

    方案:

    • 一般eslint可以检查出来,检查一下变量所在的作用域

    TypeError(类型错误)

    bug原因:

    • 常出现在函数的返回值或参数中,由于参数或返回值可能是多种类型导致使用的错误
    • 没有给参数默认值,参数变成undefined

    常见提示:

    • xxx is not a function

    方案:

    • 给函数的参数默认值
    • 对函数的参数和返回值在使用时先做类型校验,或者统一类型

    代码逻辑

    bug原因:

    • 重复代码太多,在后期修改同一个功能时需要重复改多份,容易漏改
    • 一个函数包含的代码太多,阅读困难,难以理解
    • 一个函数包含的逻辑太多,做了多个操作,耦合性强,难以维护
    • if else的分支太多,写起来容易出错
    • 前人挖坑自己填,自己挖坑坑自己

    方案:

    • 给每一个函数加上注释,标明输入输出的值的含义
    • 尽量写成纯函数,幂等设计
    • 减少重复代码,提炼成公共的函数,提炼需注意,如果提炼出来的函数不能给出一个合理的注释,就不要提
    • 如果单个函数不能用一段简单的描述表达,则可能需要将其拆分成多个函数
    • 如果单个函数代码行数超过100行,则可能需要将函数内部的一些逻辑写成函数提出来
    • 单个函数尽量只做一个操作,如果单个函数做了多个操作,可以将其修改成主操作+回调的形式,这样可以解耦多个操作
    • 如果if else分支过多,可分为2种情况处理:
      • 单层级的if else,可以用switch或hash代替
      • 嵌套型的if else,将易判断的逻辑放在前面,处理完后使用return退出后续判断,减少嵌套
    • 不断自我review,自我质疑:
      • 如果下次我要改这块东西好不好改?
      • 如果这段代码给别人看能不能看懂?
      • 如果我3个月后再来看自己的代码,能不能看懂?
      • 现在的需求是否已考虑了大部分情况,好不好扩展?
      • 这个组件好不好复用,是否逻辑耦合,是否可以抽象?

    示例:

    • 函数内部减少代码行数,提炼公共函数
    // 将获取a,b,c的逻辑写成函数的形式调用,减少代码体积 
    function foo1() {    
        var a = getA();    
        var b = getB();    
        var c = getC();    
        return a + b + c; 
    }  
    function foo2() {    
        var a = getA();    
        var b = getB();    
        var c = getC();    
        return a - b - c; 
    }  
    // 是否需要一个函数getABC,取决于该函数的功能是否能用一段简单的描述表达 
    function getABC() {    
        var a = getA();    
        var b = getB();    
        var c = getC();    
        return { a, b, c }; 
    }
    
    • 多个操作拆成主操作+回调
    // getAndSetData函数获取数据,并且处理数据,渲染UI,getAndSaveData函数获取数据,但是只保存数据,这时函数变得耦合
    function getAndSetData(){    
        http.get('/list').success(res => {              
            const data = this.dealData(res);        
            this.render(data);    
        }) 
    }  
    function getAndSaveData(){    
        http.get('/list').success(res => {        
            this.saveData(res);    
        }) 
    }  
    // 改成回调参数的形式,getData只做请求数据的操作,其他操作以回调参数传入 
    function getData(callback){    
        http.get('/list').success(res => {              
            callback.call(this, res);    
        }) 
    }  
    getData(res => {    
        const data = this.dealData(res);    
        this.render(data); 
    });  
    getData(this.saveData);  
    // 改成链式,代码更清晰 
    function getData(){          
        return new Promise(resolve => {        
            http.get('/list').success(res => {                      
                resolve(res);            
            });    
        }); 
    }  
    getData().then(res => {    
        const data = this.dealData(res);    
        this.render(data); 
    });  
    getData().then(res => this.saveData(res));
    
    • if else单层级优化
    // 原始写法 
    if(number == 1){    
        deal1(); 
    }else if(number == 2){    
        deal2(); 
    }else if(number == 3){    
        deal3(); 
    }else{    
        deal4(); 
    }  
    // switch优化 
    switch (number) {    
        case 1:        
            deal1();        
            break;    
        case 2:        
            deal2();        
            break;    
        case 3:        
            deal3();        
            break;    
        default:        
            deal4();        
            break; 
        }  
    // hash优化 
    const obj = {    
        1: deal1,          
        2: deal2,          
        3: deal3,          
        default: deal4, 
    }; 
    obj[number] ? obj[number]() : obj['default']();
    
    • if else多层级嵌套优化
    if(a){    
        deal1(); 
    }else {    
        if(b){        
            deal2();    
        }else{        
            if(c){            
                deal3();        
            }else{            
                deal4();        
            }    
        } 
    }  
    // 优化 
    if(a){    
        deal1();          
        return; 
    } 
    if(b){    
        deal2();          
        return; 
    } 
    if(c){    
        deal3();          
        return; 
    } 
    if(d){    
        deal4();          
        return; 
    }
    

    测试方法

    数据层面

    bug原因:

    • 数值的边界情况没有考虑,常见于接口无数据时的显示,组件应有的状态值没考虑全

    方案:

    • 数值的变化会引起视图的变化,则去尝试数值的所有可能性

    说明:

    • 数据来源一般是接口或者自己创造,数值不一定是指纯数字,也可以是单一状态的不同值

    如何优雅地分析和防范前端 BUG? 示例1:

    • 给出一个列表数据,实现一个分页选择器,一页10条,就需要考虑不同数据量下的展示与交互:

    如何优雅地分析和防范前端 BUG?

    • 无数据(无数据的展示)
    • 3条数据(只有1页)
    • 10条数据(边界情况,测试是否也是1页)
    • 11条数据(出现2页,点击页码跳页)
    • 20条数据(边界情况,测试是否也是2页)
    • 21条数据(出现3页,测试从第1页直接跳到第3页)
    • 100条数据(分页处的UI改变,会出现省略号)

    示例2:

    • 文案展示,UI一般只会给一种理想情况,这里需要综合考虑多个数值的变化:

    如何优雅地分析和防范前端 BUG?

    • 文字容器宽度是定宽还是根据文字长度自适应
    • 无文字,少量文字,文字过长下的展示
    • 浏览器屏幕宽度缩放下的文案展示

    交互层面

    bug原因:

    • 未考虑某些非习惯性交互或者组合交互的情况

    方案:

    • 如果多个交互会影响到同一数据或视图,则去尝试这些交互的组合

    说明:

    • 一次交互是指一次操作(click, hover, drag)或一次数据自动变化(延时, 异步, socket)会引起数据或视图的改变

    示例: 如何优雅地分析和防范前端 BUG?

    • 以题型筛选项作为视图来看,其交互只有点击题型1个
    • 以题目列表作为视图来看,其交互有题型选择、难度选择、知识点切换、教辅切换、分页切换、加入作业、页内全选、作业篮子清空8个
    • 反复快速的切换精品题库和本校题库,看右侧列表数据是否是最后一次点击后的题目数据,测试race condition
    • 组合切换知识树,教辅和题库,看右侧列表数据是否正确

    如何优雅地分析和防范前端 BUG?

    思维方式

    产品思维

    思考为什么要做,为什么其他产品不做,理解需求的意义 用四象限法评估需求价值:必要性,难易度

    • 非必要且容易,和产品讨论,让产品说服自己,可以妥协
    • 非必要且复杂,据理力争,别给自己挖坑
    • 必要且容易,那就直接做
    • 必要且复杂,觉得有必要做的一定是自己已经充分理解了的,合理就应该做,可以参考功能复杂和逻辑复杂下的处理方式

    如何评估必要性:

    • 对现有系统的破坏程度,影响范围
    • 站在用户的角度,这个功能对我有没有用
    • 是否会破坏用户体验

    学生思维

    用解数学题的逻辑思维去写代码

    • 看到题目(需求)就能意识到注意的点,比如要做分类讨论(数值变化测试),反证法(逆向推导)
    • 有解题思路再下笔(先充分理解需求,再写代码)

    将每一次迭代开发都当作一场考试,提测是交卷,改完bug再次提测是补考

    • 在考试完后看到成绩进步时,动力会更大,更期待下一次的检验
    • 小学考满分次数多,其次初中,高中;能力提高,写法更高级,bug越难发现,但解决完bug后自我提升更明显,程序设计能力和开发效率都会提高
    • 提高第一次考试的成绩,减少补考的次数

    把 bug 记录当成错题本

    • 每次解决完bug,在评论中记录bug原因
    • 分析归纳bug原因
    • 反思总结,挖掘更深层级的原因,避免在同一个地方摔倒两次
    • 定时回顾,下次遇到类似需求提高警惕意识

    ❤️ 谢谢支持

    1. 喜欢的话别忘了 分享、点赞、在看 三连哦~。

    起源地下载网 » 如何优雅地分析和防范前端 BUG?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元