什么是防御性编程?
一个页面在呈现给用户之前需要经过静态资源加载、后端接口请求和渲染这三个过程,我们要做的就是在各个过程中防御可能出现的异常情况,保持流畅的用户体验,同时还要应对来自外部的攻击。
关于对象的操作
-
可选链操作符(?.) 简化 判断对象是否为null
如果可选链
?.
前面的值为undefined
或者null
,它会停止运算并返回undefined
。可选链
?.
不是一个运算符,而是一个特殊的语法结构-
将
?.()
用于调用一个可能不存在的函数。 -
可选链
?.
不能用在赋值语句的左侧。
let value = obj.first && obj.first.second; // 等于 ==》 let value = obj.first?.second;
-
-
空值合并操作符
??
, 可以在使用可选链时设置一个默认值:let customerCity = customer?.city ?? "暗之城";
如果第一个参数不是
null/undefined
,则??
返回第一个参数。否则,返回第二个参数。使用场景:
-
可以使用
??
序列从一系列的值中选择出第一个非null/undefined
的值。 -
默认值赋值
-
||
返回第一个 真 值。 -
??
返回第一个 已定义的 值。 -
如果没有明确添加括号,不能将其与
||
或&&
一起使用
-
-
使用解构赋值时,注意对象是否为undefined,尤其是嵌套的子对象,如果属性不存在为undefined,就会报错。
可以使用||
运算符:
// 使用
const {aa, bb} = obj || {}
- 解构默认值仅在 undefined 的情况下生效,null、false、0 不生效。
数组操作方面
-
取数组的最后一个元素:
除了用长度arr[arr.length-1]还可以:
arr.slice(-1)
-
当使用数组的方法时候,为了避免报undefined的错误,所以需要判断一下 再取值操作:
arr && arr?.length > 0
(有时候后端返回null,如果直接当成数组去调用map、filter、forEach 等方法时,页面会报undefined的错,从undefined读取属性就会导致这个错误(同理,null也一样)
) -
利用数组方法巧妙去重
const arr1=[1,3,4,5,6,7,5,4,3]
const arr2 = arr1.filter((item,index)=>{
return arr1.indexOf(item) === index;
})
console.log(arr2)
注意: 对象的数组中查找对象,
indexOf()
方法会在查找时比较引用
而不是值
。这意味着它会检查对象是否完全相同,而不是检查对象的属性值是否相同。
const array = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const obj = { id: 2, name: 'Bob' };
console.log(array.indexOf(obj)); // 输出: -1,因为这是一个新的对象,与数组中的对象不是同一个引用
console.log(array.indexOf(array[1])) // 输出: 1
在这样的情况下,可能需要使用 findIndex()
findIndex() : 第一个参数为比较函数,多用于非基本类型(例如对象)的数组索引查找,或比较复杂的查找条件
计算精度损失问题
由于JS的运算机制存在精度损失,导致 0.1+0.2 != 0.3
原因:计算机是二进制运算的,有限十进制小数 0.1 转化成了无限二进制小数 0.00011001100…,可以看到精度在转化过程中丢失了
解决方法:不要直接使用 运算符号进行加减, 可以引入插件:
npm install mathjs
封装:
import * as math from 'mathjs';
export default {
// 加
add(num1,num2){
return math.add(math.bignumber(num1),math.bignumber(num2));
},
// 乘
multiply(num1,num2){
return math.multiply(math.bignumber(num1),math.bignumber(num2));
},
// 减
subtract(num1,num2){
return math.subtract(math.bignumber(num1),math.bignumber(num2));
},
// 除
divide(num1,num2){
return math.divide(math.bignumber(num1),math.bignumber(num2));
}
}
使用:
import math from './math.js'
let addNum = math.add(3,2); // 5
let mulNum = math.multiply(3,2); // 6
let subNum = math.subtract(3,2); // 1
let divNum = math.divide(3,2); // 1.5
参考: https://juejin.cn/post/6980282248104771615
计算百分比时,为了精度,通常做法是,除法之后先乘法再除法:cnt/total *10000/100
超大number 精度丢失问题
前端大数字之间的运算,精度丢失问题无法解决;
因为number
的基本类型不能超过2^53
,不然就会精度丢失
,
为了解决这个限制,在ECMAScript
标准中出现了BigInt
;
BigInt可以表示任意大的整数;
前端的大数据,可以在第一步就先给大数据后加n
,直接写成BigInt类型
的就可以;
let result=124569875984123677888999n;
String(result);
接口错误捕获
虽然后端接口定义了返回值的类型和结构,但是也有可能存在接口异常,不按规定的结构返回的情况,这时候前端要注意代码中使用接口中值的情况,避免页面异常。
部分 UI 的 JavaScript 错误不应该导致整个应用崩溃,为了解决这个问题,React 16 引入了一个新的概念 —— 错误边界。
错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且,它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。
-
对于接口无数据、数据异常等情况、前端要做兼容处理。
-
页面做到可降级
对于一些刚上新的业务,或者有存在风险的业务模块,或者会调取不受信任的接口,例如第三方的接口,这个时候就要做一层降级处理,例如接口调用失败后,剔除对应模块的展示,让用户无感知的使用
页面状态感知-交互
-
巧用
loading和disabled
用户操作后,要及时loading和disabled确保不让用户进行重复,防止业务侧出现bug -
无网络的情况下,策略是什么。以及弱网情况下,策略是什么,等待加载 or 交互提示。
参考:https://blog.youkuaiyun.com/qq_43203586/article/details/120491359 -
文本过长溢出——省略号
.ellipsis{
overflow: hiden;
text-overflow: ellipsis;
white-space: nowrap;
}
- 善用
max-width \ min-width
的使用,防止样式因窗口大小而变形 - 必要时显示滚动条,
overflow: auto
参考:
https://blog.youkuaiyun.com/weixin_40906515/article/details/115364712
npm run build后如何本地运行build后的目录
全局安装本地服务器依赖
npm install serve -g
将指定目录作为本地服务器根目录启动服务
# 将当前目录下的dist文件夹作为应用根目录,并指定服务端口为5000
serve dist -p 5000
前端打包上线后,用户端的浏览器没有刷新缓存
原因:浏览器的缓存策略,这里需要知道浏览器有两种缓存模式:强缓存和协商缓存。
- 强缓存:
需要修改nginx配置信息
参考:
https://blog.youkuaiyun.com/y523006369/article/details/103614312