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

367次阅读  |  发布于3年以前

两个公式

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

bug率 = bug数 / 测试用例数

八种问题

需求评审

需求理解

bug原因:

方案:

示例1:

思考实现一个自定义输入框的删除文字功能 有以下几种方式:

示例2:

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

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

技术实现

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

简单需求

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

复杂需求

bug原因:

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

方案:

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

示例:

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

bug原因:

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

方案:

示例1:

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

正向推导

逆向推导

示例2:

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

  1. 在技术方案文档中列出,优点是有层级结构,一个功能点可以接着拆出更多子功能点。
  2. 通过数据表列出,并打上标签(比如优先级、功能模块等),优点是分类、排序比较方便,更加清晰。

bug原因:

方案:

码前准备

编程习惯

语言基础

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(语法错误)

常见提示:

方案:

Uncaught ReferenceError(引用错误)

常见提示:

方案:

TypeError(类型错误)

bug原因:

常见提示:

方案:

代码逻辑

bug原因:

方案:

示例:

// 将获取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(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]( "number] ? obj[number") : obj['default']( "'default'");
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原因:

方案:

说明:

示例1:

示例2:

交互层面

bug原因:

方案:

说明:

示例:

思维方式

产品思维

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

如何评估必要性:

学生思维

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

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

把 bug 记录当成错题本

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8