前端开发过程中不容忽视的问题

接口出现问题前端页面就会直接崩溃?

在前端开发中:当接口出现问题,页面就会直接崩溃。在JS开发应用中导致页面崩溃的多种边界情况:解构赋值、数组方法调用、遍历对象数组、使用对象方法、async/await捕获异常失败、JSON.parse解析报错、API兼容、内存溢出等。

解构失败报错

不做任何处理直接将后端返回的数据进行解构

const handlerData  = (data) => {
  const {user} = data
  const {id,name} = user
}

handlerData({}) // TypeError: Cannot destructure property 'id' of 'user' as it is undefined.

解构赋值的规则是:只要等号右边的值不是对象或数组,就先将其转化为对象(装箱)。由于undefined、null无法转换为对象,所有对他们进行解构赋值就会报错。

所以当data为undefined、null的时候,上述代码就会报错。

这个时候给data附一个默认值,但是会依然报错。

const handlerData  = (data) => {
  const {user = {}} = data
  const {id,name} = user
}
handlerData({user: null}) // TypeError: Cannot destructure property 'id' of 'user' as it is null.

ES6内部使用严格相等运算符(===)判断一个变量是否有值。所以当一个对象的属性值不严格等于undefined,默认值是不会生效的

当props.data为null的时候,那么 const {id,name} = user 就会报错!

正确处理方式

/* 正确处理 */
const handlerData  = (data) => {
  const {user} = data
  const {id,name} = user || {}
}

handlerData({user: null})

数组方法调用错误

从接口返回的数据直接当成数组来用

const handlerData = (data) => {
  const {userList} = data
  const newList = userList.map(item => item.name)
}

handlerData({userList: null}) // TypeError: Cannot read properties of null (reading 'map')
handlerData({userList: 123}) // TypeError: userList.map is not a function

正确处理方式

const handlerData = (data) => {
  const {userList} = data
  let newList = null
  // 判断是否是数组
  if(Array.isArray(userList)){
    newList = userList.map(item => item.name)
  }
  console.log(newList); // [ '王五', '李四' ]
}

handlerData({userList: [{name: "王五"}, {name: "李四"}]})

遍历对象数组报错

遍历对象数组时也要注意null或undefined的情况

const handlerData = (data) => {
  const {userList} = data
  const newList = userList.map(item => item.name)
}
handlerData({userList: [null, undefined]}) // TypeError: Cannot read properties of null (reading 'name')

一旦数组中的某一项值为null或者undefined,那么item.name必然会出错!

/* 使用可选链:? */
const handlerData = (data) => {
  const {userList} = data
  const newList = userList.map(item => item?.name)
}
handlerData({userList: [null, undefined]})

但是如果是以下情况的话不推荐使用可选链?:

const handlerData = (data) => {
  const {userList} = data
  const newList = userList.map(item =>`用户id是${item?.id},用户名字是${item?.name},用户年龄是${item?.age}`)
}
handlerData({userList: [null, undefined]})

?可选链操作符,虽然好用但是也不能滥用。item?.id会被编译成 item === null || item === void 0 ? void 0: item.id,滥用导致编译后的代码size体积变大。

正确处理方式:当可选链?操作较多的时候,选择使用或运算赋默认值

const handlerData = (data) => {
  const { userList } = data
  const newList = userList.map(item => {
    const { id, name, age } = item || {}
    return `用户id是${id},用户名字是${name},用户年龄是${age}`
  }
  )
}
handlerData({ userList: [null, undefined] })

使用对象方法报错

null、undefined无法被转化为对象,因此对其使用对象的方法就会报错

const handlerData = (data) => {
  const { user } = data
  const newList = Object.entries(user)
}
handlerData({ user: null }) // TypeError: Cannot convert undefined or null to object

正确处理方式:

const handlerData = (data) => {
  const { user } = data
  /* 使用|| */
  const newList = Object.entries(user || {})
}
handlerData({ user: null }) 
/**
 * 判断给定值类型或获取给定值的类型名称
 * @param {*} val-要判断类型的值 
 * @param {string} [type] - 可选,指定的类型名称 
 * @returns {string|boolean} -如果提供了type类型,返回一个布尔值表示val是否属于该类型;如果没有提供type,返回val的类型名称。
 * 
 * @example
 * 获取类型名称
 * console.log(judgeDataType(56)); // number
 * console.log(judgeDataType('yu')); // string
 * 
 * @example
 * 判断是否为指定的值
 * console.log(judgeDataType(78, "number")); // true
 */
function judgeDataType(val, type){
  const dataType = Object.prototype.toString.call(val).slice(8,-1).toLowerCase()
  return type ? dataType === type : dataType
}
const handlerData = (data) => {
  const { user } = data
  // 判断user是否是对象
  if(judgeDataType(user, "object")){
    const newList = Object.entries(user)
  }
}
handlerData({ user: null })

async/await报错未捕获

const List =  () => {
  let loading
  const getData = async () => {
      loading = true
      const res = await fectchListData()
      loading = false
  }
  getData()
}

正确处理方式:

const List =  () => {
  let loading
  const getData = async () => {
    try{
      loading = true
      // fectchListData()可能会报错,页面就会一直在加载,因此需要捕获异常,改变页面的加载状态
      const res = await fectchListData()
      loading = false
    }catch(err){
      loading = false
    }
  }
  getData()
}

JSON.parse解析报错

const handlerData = (data) => {
 const { userStr } = data
 const user = JSON.parse(userStr)
}
handlerData({ userStr: 'tdecuy' }) // SyntaxError: Unexpected token d in JSON at position 1

正确处理方式:

const handlerData = (data) => {
  const { userStr } = data
  try{
    const user = JSON.parse(userStr)
  }catch(err){
    console.log(`${userStr}不是一个有效JSON字符串`); // gceyu不是一个有效JSON字符串
  }
}
handlerData({userStr: "gceyu"})

动态导入某个模块失败报错

const loadModule = async () => {
  const module = await import('./index.js')
  module.getNum() // 34
}
loadModule()

如果导入的模块存在语法错误、网络或者跨域问题、文件不存在、循环依赖、甚至文件很大导致内存不足、模块内的运行错误等都有可能阻塞后续代码的执行。

正确处理方式:

const loadModule = async () => {
  try{
    const module = await import('./index.js')
    module.getNum() // 34
  }catch(err){
    console.log(err);
  }
}

API兼容性报错

fetch('/api/data').then(response => response.json()).then(data => {
  console.log(data);
}).catch(err => {
  console.log(err);
})

低版本的Node不支持fetch,需要更高兼容性的场景使用axios。

内存溢出崩溃

滥用内存缓存可能会导致内存溢出

/* 情况九: 内存溢出崩溃 */
const cache = {}
function addToCache(key, value){
  cache[key] = value
  // 没有清理机制,缓存会无限增长
}
  • 避免闭包持有大对象的引用
  • 记得清楚定时器和事件监听器
  • 避免深度递归超出栈内存

上述列举的js在运行时可能会出现的发生的错误而导致应用崩溃的一些边界情况,在书写代码的时候要额外注意!

边界场景的容错一定要做,原则上不相信任何的外部输入数据的存在性和类型!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值