BUG总结

本文是基层开发工程师在业务开发中遇到的耗时较长问题汇总。涵盖数组filter错误用法、react异步dom问题、上传文件算法出错、移动端iOS 9以下白屏、Object.assign兼容、控制台报错、路由跳转报错及URL传参问题等,并给出相应解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最新更新时间:2019年05月21日17:51:48
《猛戳-查看我的博客地图-总有你意想不到的惊喜》

本文内容:日常bug汇总

概述

在我的工作经历中,作为一名基层开发工程师,开发业务过程中,会遇到大大小大的问题,这里主要记录耗时较长的问题

数组filter错误的用法

问题时间:2019年05月17日
问题描述:用户管理系统,用户类型分为,一级用户,二级用户,三级用户,每个级别用户看到的左侧树目录不一样,管理权限越高,看到的目录越多,因此需要过滤处理。但是如下方案一,出现的错误是权限越低的用户登录过,高级别权限的用户看到的是较少的目录。原因:引用类型问题,返回的结果没有切断和原数组的引用

//不应该对原始数组userPermissionArr进行操作
  function filterArr(userPermissionArr, roles) {
    //这条语句没有问题,filter方法不对原始数组进行操作,并且返回新数组
    const res = userPermissionArr.filter(item => {
      if (allow(roles, item)) {
        if (item.children && item.children.length) {
          //下面这句递归语句有问题,虽然filter方法返回新数组,不对原数组进行操作,但是route.children还是原始数组的引用,因此原数据被改变了,导致后续逻辑出错
          item.children = filterArr(item.children, roles)
        }
        return true
      }
      return false
    })
    return res
  }
//如下双层forEach方法,完全切断了引用
  function filterArr(userPermissionArr, roles) {
    const res = []
    userPermissionArr.forEach((item, idx1) => {
      if (allow(roles, item)) {
        if (item.children && item.children.length) {
          res[res.length] = { ...item }
          res[res.length - 1].children = []
          item.children.forEach((child, idx2) => {
            if (allow(roles, child)) {
              res[res.length - 1].children.push({ ...child })
            }
          })
        } else {
          res.push({ ...item })
        }
      }
    })
    return res
  }
  • 注意:api好用,但是弊端需要掌握
react异步dom问题

问题时间:2019年05月21日
问题描述:react移动端的项目,图片预览功能,使用react-cropper组件,点击图片打开预览,点击预览区域任意地方可以关闭预览,但是第三方组件在iOS设备上不支持关闭,猜测react-cropper组件dragMode='move’的属性,阻止了click事件,综合考虑后,在预览界面右上角加关闭icon,解决了iOS不兼容的问题。

//场景一
componentDidMount() {
    let _this = this;
    let dom = document.getElementsByClassName('cropper-drag-box')
    console.log(dom);
    //控制台打印如下,看上去是一个空数组,即没有拿到'cropper-drag-box'元素
    //HTMLCollection []
    
    //展开之后,如下,确实存在元素,可以得出react页面在异步渲染,'cropper-drag-box'元素是异步生成的,首次打印的时候还没有生成真实dom
    //HTMLCollection []
		//0: div.cropper-drag-box.cropper-move
		//length: 1
		//__proto__: HTMLCollection
}
//场景二:如下报错,也是异步渲染原因
componentDidMount() {
	let _this = this;
	let dom = document.getElementsByClassName('cropper-drag-box')[0]
	console.log(dom);//undefined
	dom.addEventListener('click',function(){},false)
	//Uncaught TypeError: Cannot read property 'addEventListener' of undefined
}
//上面两种错误的解决方案
componentDidMount() {
	let dom = null
	setTimeout(function(){
		dom = document.getElementsByClassName('cropper-drag-box')[0]
		console.log(dom);
		dom.addEventListener('click',function(){},false)
	},1000)
}
  • 扩展:vue中处理异步dom的方案,this.$nextTick(function(){console.log(1)})
上传文件剔除非法格式文件算法出错

问题时间:2019年05月25日
问题描述:本地上传文件,需要校验文件格式,以读取到的文件名来剔除非法文件

//方案一:当文件名中包含格式名的时候会出错
let isFormat = false
const arrList = ['pdf', 'ppt', 'pptx', 'doc', 'docx']
for (let i = 0; i < arrList.length; i++) {
	//如果文件名包含 pdf 则判断失效:比如'这不是一个pdf文件.rmvb'
	if (file.name.toLowerCase().indexOf(arrList[i]) !== -1) {
		isFormat = true
	}
}
//方案二:取文件名末尾,不会出错
let isFormat = false
const arrList = ['pdf', 'ppt', 'pptx', 'doc', 'docx']
	for (let i = 0; i < arrList.length; i++) {
	if (file.name.split('.').pop() === arrList[i]) {
		isFormat = true
	}
}
react项目在移动端iOS 9 以下打开白屏&&空白页面

问题时间:2019年06月13日
问题描述:用目前市面上的所有react脚手架构建项目,都会出现在iOS 9的手机上无法打开页面,出现白屏,包括官方的create-react-app脚手架。这个问题耗时半个月,最终定位到核心问题是,babel 7没有配置好。

//.babelrc文件 失败的配置
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-function-bind",
    "@babel/plugin-proposal-class-properties"
  ]
}
//.babelrc文件 成功的配置
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "browsers": ["safari >= 8"]
        }
      }
    ],
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-function-bind",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-runtime"
  ]
}
Object.assign is not a function

移动端项目开发过程中,低版本手机系统(iOS 9 以下)的浏览器不兼容Object.assign语法,因此需要做兼容处理

//场景一
let a = {a:2,b:2};
let b = {c:3,d:3,a:5};

let c = Object.assign(a,b);//将b对象合并到a对象上,并返回a对象

console.log(c);//{ a: 5, b: 2, c: 3, d: 3 }
console.log(c === b);//false
console.log(c === a);//true
console.log(a === b);//false

console.log(c == b);//false
console.log(c == a);//true
console.log(a == b);//false
//场景二
let a = {a:2,b:2};
let b = {c:3,d:3,a:5};

let c = Object.assign({},a,b);//将b对象合并到a对象上,再将a对象合并到新对象上,返回新对象

console.log(c);//{ a: 5, b: 2, c: 3, d: 3 }
console.log(c == b);//false
console.log(c == a);//false
//场景三 Object.assign is not a function 兼容处理
//全局定义 Object.assign
if(typeof Object.assign != 'function'){
    Object.assign = function(target) {//target 最终输出的结果
        'use strict';
        if (target == null) {//undefined == null true   null == null true
            throw new TypeError('Cannot convert undefined or null to object');
        }
        console.log(target == Object(target))
        target = Object(target);//target == Object(target) true
        for(var i = 1;i<arguments.length;i++) {
            var source = arguments[i];
            if (source != null) {
                for (var key in source) {
                    if (Object.prototype.hasOwnProperty.call(source, key)) {
                        target[key] = source[key];
                    }
                }
            }
        }
        return target;
    };
}
控制台报错“SyntaxError: Unexpected end of input”

前端代码本地实时编译打包,webpack启动本地服务没有问题;通过命令scp -r /Users/wanshaobo/project/xdf-middle-school-webview/old/ root@172.22.31.45:/root/project/old将包发布到服务器,此时服务器报错磁盘已满,看上去包已经发布成功,但是通过域名访问这个网站报错SyntaxError: Unexpected end of input。经过排查,重启nginx修复了这个报错。

经过排查,导致这个报错不是nginx的问题,是因为本地重新安装了node包,导致语法格式变换;

//修复前
//a.js
export const options = []
export const subjectInfo = arr[2]
export const requestTips = {
  timeout: '哎呀,加载超时了,请刷新重试~'
}
//在b.js中使用a.js
import { requestTips } from '../../common/dic'

//修复后
//a.js
const options = []
const subjectInfo = arr[2]
const requestTips = {
  timeout: '哎呀,加载超时了,请刷新重试~'
}
module.exports =  {
  options,
  subjectInfo,
  requestTips
}
react项目控制台报错“Can’t perform a React state update on an unmounted component”

A路由跳转到B路由,执行如下路由跳转方法,
this.props.history.replace({pathname:’/B’,search: ‘a=1&type=1’})

报错如下:
react-dom.development.js:506
Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

可以看出,错误的原因是A路由的页面在执行跳转方法之后,又执行setState方法了,经过排查,A路由的页面中有一个setInterval定时器没有卸载

解决方案:在A路由页面生命周期方法componentWillUnmount中卸载定时器

componentWillUnmount() {
  clearInterval(this.Timer);
  this.allTimer = null;
}
URL传参的两个问题
  • 参数中包含汉字、数组等格式的数据,导致浏览器无法解析报错

此时需要用到 encodeURI() 函数 和 decodeURI() 函数,该函数把字符串作为 URI 进行编码和解码

对以下在 URI 中具有特殊含义的 ASCII 标点符号,encodeURI() 函数是不会进行转义的: , / ? : @ & = + $ # (可以使用 encodeURIComponent() 方法分别对特殊含义的 ASCII 标点符号进行编码)

//没有做处理的场景
//URL中将要包含如下的数组和汉字
let params1 = [1,2,3]
let params2 = '万少博'
//如果不用JSON.stringify()对数组进行转化,在拼接URL的时候,会调用数组的toString方法
let url = 'www.baidu.com?params1=' + params1 + '&params2=' + params2;//www.baidu.com?params1=1,2,3&params2=万少博

//做过处理之后的场景
let url = 'www.baidu.com?params1=' + JSON.stringify(params1) + '&params2=' + params2;//www.baidu.com?params1=[1,2,3]&params2=万少博
//用 encodeURI 函数对URL进行编码
let encode = encodeURI(url);//www.baidu.com?params1=%5B1,2,3%5D&params2=%E4%B8%87%E5%B0%91%E5%8D%9A
//用 decodeURI 函数对URL进行解码
let decode = decodeURI(encode);//www.baidu.com?params1=[1,2,3]&params2=万少博
  • 参数中包含 & 导致浏览器无法解析报错,或导致接收端解析异常

此时需要用到 escape() 函数 和 unescape() 函数,该函数可对字符串进行编码和解码

该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / (其他所有的字符都会被转义序列替换)

//没有做处理的场景
//传递对象的值虽然是字符串类型的数据,但是包含了&
let params = {name: 'a&b',age:18}
let url = 'www.baidu.com?params1=' + JSON.stringify(params) + '&params2=zhongguo'
let kv = url.split('?')[1];//params1={"name":"a&b","age":18}&params2=zhongguo
//解析期望得到的数据是有两个元素的数组[ 'params1={"name":"a&b","age":18}', 'params2=zhongguo' ],但是实际得到了三个
let value = kv.split('&');//[ 'params1={"name":"a', 'b","age":18}', 'params2=zhongguo' ]

//做过处理之后的场景
let params = {name: 'a&b',age:18}
//用 escape 函数对字符串进行编码
let url = 'www.baidu.com?params1=' + escape(JSON.stringify(params)) + '&params2=zhongguo'
let kv = url.split('?')[1];//params1={"name":"a&b","age":18}&params2=zhongguo
let value = kv.split('&');//['params1=%7B%22name%22%3A%22a%26b%22%2C%22age%22%3A18%7D','params2=zhongguo']
let p1 = value[0];//params1=%7B%22name%22%3A%22a%26b%22%2C%22age%22%3A18%7D
//用 unescape 函数对字符串进行解码
let v1 = unescape(p1.split('=')[1]);//{"name":"a&b","age":18}
let obj = JSON.parse(v1);//{ name: 'a&b', age: 18 }
参考资料

感谢阅读,欢迎评论^-^

打赏我吧^-^

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值