首页 > 其他分享 >接口一异常你的前端页面就直接崩溃了?

接口一异常你的前端页面就直接崩溃了?

时间:2024-12-20 22:28:27浏览次数:6  
标签:const name handleData 前端 接口 报错 null data 页面

解构失败报错不做任何处理直接将后端接口数据进行解构

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

VM244:3 Uncaught TypeError: Cannot destructure property 'id' of 'user' as it is undefined.

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

所以当 data 为 undefined 、null 时候,上述代码就会报错。
第二种情况,虽然给了默认值,但是依然会报错

const handleData = (data)=> {
  const { user = {} } = data;
  const { id, name } = user;
}
handleData({user: null})

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

所以当 props.data 为 null,那么 const { name, age } = null 就会报错!good:

const handleData = (data)=> {
  const { user } = data;
  const { id, name } = user || {};
}
handleData({user: null})

数组方法调用报错
从接口拿回来的数据直接用当成数组来用

const handleData = (data)=> {
  const { userList } = data;
  const newList = userList.map((item)=> item.name)
}
handleData({userList: null})
handleData({userList: 123})

VM394:3 Uncaught TypeError: userList.map is not a function

那么问题来了,如果 userList 不符合预期,不是数组时必然就报错了,所以最好判断一下
good:

const handleData = (data)=> {
  const { userList } = data;
  if(Array.isArray(userList)){
    const newList = userList.map((item)=> item.name)
  }
}
handleData({userList: 123})

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

const handleData = (data)=> {
  const { userList } = data;
  const newList = userList.map((item)=> item.name)
}
handleData({userList: [ null, undefined ]})

VM547:3 Uncaught TypeError: Cannot read properties of null (reading 'name')

一旦数组中某项值是 undefined 或 null,那么 item.name 必定报错,可能又白屏了。
good:

const handleData = (data)=> {
  const { userList } = data;
  const newList = userList.map((item)=> item?.name)
}
handleData({userList: [null]})

但是如果是这种情况就不good了

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

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

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

当可选链操作符较多的情况时无论是性能还是可读性都明显上面这种方式更好。
复习一下装箱
大家可以思考一下,以下代码会不会报错

const handleData = (data)=> {
  const { userList } = data;
  const newList = userList.map((item)=> item.name)
}
handleData({userList: ['', 123]})

是不会报错的,因为在 JavaScript 中,当你在一些基本类型上直接访问属性时这些类型会被自动临时转换成它们对应的对象类型。这种转换称为“装箱”(boxing)。例如:

  • (‘’).name

空字符串被临时转换成一个字符串对象。由于没有名为 name 的属性,所以它返回 undefined,但不会报错。

let str = "hello";
console.log(str.length); // 5

在这里,str.length 实际上是在字符串对象上调用的,而不是直接在基本类型字符串上。JavaScript 引擎在幕后将字符串 “hello” 装箱为 String 对象,因此可以访问 length 属性。

  • (123).name

数字 123 被临时转换成一个数字对象。由于没有名为 name 的属性,所以它返回 undefined,但不会报错。

let num = 123;
console.log(num.toFixed(2)); // "123.00"

num.toFixed(2) 调用了数字对象的 toFixed 方法。JavaScript 将数字 123

  • 装箱为 Number 对象。 (null).name

null是一个特殊的基本类型,当尝试访问其属性时会报错,因为 null 不能被装箱为对象。

 try {
  const name = (null).name; // TypeError: Cannot read property 'name' of null
} catch (error) {
  console.error(error);
}
  • (undefined).name

undefined 也不能被装箱为对象。

try {
  const name = (undefined).name; // TypeError: Cannot read property 'name' of undefined
} catch (error) {
  console.error(error);
}

JavaScript 中的基本类型包括:string

number
boolean
symbol
bigint
null
undefined

对应的对象类型是:

String
Number
Boolean
Symbol
BigInt

装箱的工作原理:

当你访问基本类型的属性或方法时,JavaScript
会自动将基本类型装箱为其对应的对象类型。这个临时的对象允许你访问属性和方法,但它是短暂的,一旦属性或方法访问完成,这个对象就会被销毁。

需要注意的是,null 和 undefined 没有对应的对象类型,不能被装箱。所以访问它们的属性或方法会直接报错!所以时刻警惕 null 和 undefined 这俩坑。
使用对象方法时报错
同理,只要变量能被转成对象,就可以使用对象的方法,但是 undefined 和 null 无法转换成对象。对其使用对象方法时就会报错。

const handleData = (data)=> {
  const { user } = data;
  const newList = Object.entries(user);
}
handleData({user: null});

VM601:3 Uncaught TypeError: Cannot convert undefined or null to object

下面这两种优化方式都可
good:

const handleData = (data)=> {
  const { user } = data;
  const newList = Object.entries(user || {})
}
handleData({user: null})

good:

/**
 * 判断给定值的类型或获取给定值的类型名称。
 *
 * @param {*} val - 要判断类型的值。
 * @param {string} [type] - 可选,指定的类型名称,用于检查 val 是否属于该类型。
 * @returns {string|boolean} - 如果提供了 type 参数,返回一个布尔值表示 val 是* 否属于该类型;如果没有提供 type 参数,返回 val 的类型名称(小写)。
 *
 * @example
 * // 获取类型名称
 * console.log(judgeDataType(123)); // 输出 'number'
 * console.log(judgeDataType([])); // 输出 'array'
 *
 * @example
 * // 判断是否为指定类型
 * console.log(judgeDataType(123, 'number')); // 输出 true
 * console.log(judgeDataType([], 'array')); // 输出 true
 */
function judgeDataType(val, type) {
  const dataType = Object.prototype.toString.call(val).slice(8, -1).toLowerCase();
  return type ? dataType === type : dataType;
}

const handleData = (data)=> {
  const { user } = data;
  // 判断是否为对象
  if(judgeDataType({}, "object")){
    const newList = Object.entries(user || {})
  }
}
handleData({user: null})

async/await 报错未捕获
这个也是比较容易犯且低级的错误

import React, { useState } from 'react';

const List = () => {
  const [loading, setLoading] = useState(false);
  const getData = async () => {
    setLoading(true);
    const res = await fetchListData();
    setLoading(false);
  }
}

如果 fetchListData() 执行报错,页面就会一直在加载中,所以一定要捕获一下。
good:

const List = () => {
  const [loading, setLoading] = useState(false);
  const getData = async () => {
    setLoading(true);
    try {
      const res = await queryData();
      setLoading(false);
    } catch (error) {
      setLoading(false);
    }
  }
}

当然如果觉得这种方式不优雅,用 await-to-js 库或者其他方式都可以,记得捕获就行。
JSON.parse报错
如果传入的不是一个有效的可被解析的 JSON 字符串就会报错啦。

const handleData = (data)=> {
  const { userStr } = data;
  const user = JSON.parse(userStr);
}
handleData({userStr: 'fdfsfsdd'})


16:06:57.521 VM857:1 Uncaught SyntaxError: Unexpected token 'd', "fdfsfsdd" is not valid JSON

这里没必要去判断一个字符串是否为有效的 JSON 字符串,只要利用 trycatch 来捕获错误即可。
good:

const handleData = (data)=> {
  const { userStr } = data;
  try {
    const user = JSON.parse(userStr);
  } catch (error) {
    console.error('不是一个有效的JSON字符串')
  }
}
handleData({userStr: 'fdfsfsdd'})

动态导入模块失败报错
动态导入某些模块时,也要注意可能会报错

const loadModule = async () => {
    const module = await import('./dynamicModule.js');
    module.doSomething();
}

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

const loadModule = async () => {
  try {
    const module = await import('./dynamicModule.js');
    module.doSomething();
  } catch (error) {
    console.error('Failed to load module:', error);
  }
}

API 兼容性问题报错

fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

低版本 Node 不支持 fetch,需要更高兼容性的场景使用 axios 等更好。
其他包括小程序开发,web开发等也同理,如果使用了某些不支持的 es 新特性或者较新版本的平台才支持的api也会导致直接报错,使用时做好判断或直接用兼容性更好的写法。

框架在编译时已经帮我们解决了大部分的兼容性问题,但是有些场景还需额外注意。

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

const cache = {};

function addToCache(key, value) {
  cache[key] = value;
  // 没有清理机制,缓存会无限增长
}

避免闭包持有大对象的引用

function createClosure() {
  const largeData = new Array(1000000).fill('x');

  return function() {
    console.log(largeData.length);
  };
}

const closure = createClosure();
// largeData 现在被闭包引用会一直存活在内存中,即使不再直接使用

closure = null; // 手动解除引用

记得清除定时器和事件监听器

// React
useEffect(() => {
  const timeoutId = setTimeout(() => {
    // 一些操作
  }, 1000);

  return () => clearTimeout(timeoutId);
}, []);
function setupHandler() {
  const largeData = new Array(1000000).fill('x');
  const handler = function() {
    console.log(largeData.length);
  };

  document.getElementById('myButton').addEventListener('click', handler);

  return function cleanup() {
    document.getElementById('myButton').removeEventListener('click', handler);
  };
}

const cleanup = setupHandler();
// 在适当的时候调用
// cleanup();

还有深度递归,JSON.parse() 解析超大数据等都可能会对内存造成压力。

标签:const,name,handleData,前端,接口,报错,null,data,页面
From: https://blog.csdn.net/qq_16242613/article/details/144620500

相关文章

  • 【软件测试】前端测试分析
    目的1.从前端角度来发现Web系统的问题。2.熟悉Web前端测试方法。3.掌握应用层协议HTTP的结构和工作过程。4.熟悉GET和POST请求过程(一)对新浪首页进行前端分析使用Chrome打开新浪首页https:/www.sina.com.cn/,打开开发者工具模式进行监控首页的加载过程,对加载过程进......
  • 使用Vue创建前后端分离项目的过程(前端部分)
            前端使用Vue.js作为前端开发框架,使用VueCLI3脚手架搭建项目,使用axios作为HTTP库与后端API交互,使用Vue-router实现前端路由的定义、跳转以及参数的传递等,使用vuex进行数据状态管理,后端使用Node.js+express,连接Mysql数据库。1.确定电脑上已经安装了Node.js......
  • C++ 的头文件怎么给我一种接口的感觉?
    C++中的头文件确实可以被看作是一种接口(Interface),它们在C++程序设计中扮演着至关重要的角色。以下是头文件如何体现接口特性的几个方面:1.声明与定义分离接口(头文件):头文件中包含了类的声明、函数原型、模板声明等,它们定义了程序中可用的接口,但不包含具体的实现细节。实现(源......
  • web前端期末大作业:婚纱网页主题网站设计——唯一旅拍婚纱公司网站HTML+CSS+JavaScript
    ......
  • #Java篇:java项目init和写接口流程步骤详细
    idea里面file—new-projectServerurlhttps://start.aliyun.com目录结构数据库链接项目配置application.ymlserver:port:8888spring:datasource:url:jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8username:......
  • Linux服务器网卡接口聚合Bond技术及原理
    什么是BondBond技术,也被称为网卡绑定或网卡捆绑,是将两个或更多的物理网卡绑定成一个虚拟的网卡。Bond的工作模式bond的模式有7种,mode=0,mode=1,mode=2,mode=3,mode=4,mode=5,mode=6bond常用的模式有两种1)mode=0,表示平衡负载round-robin,轮询的方式,第一个包走eth0,第二个包走eth......
  • UEFI基本逻辑与接口介绍
    背景所以需要对这块比较新的技术进行学习。在学习之前,有必要了解一下高通UEFI启动流程。原文(有删改):https://blog.csdn.net/Ciellee/article/details/113519478参考文档:80_P2484_117_B_UEFI_With_XBL_On_MSM8998_SDM660_SDM总览先来看下SDM660芯片冷启动的流程。可以看出,在设......
  • ChatGPT生成接口测试用例(一)
     接口测试在软件开发生命周期中扮演着至关重要的角色,有助于验证不同模块之间的交互是否正确。若协议消息被恶意修改,系统是否能够恰当处理,以确保系统的功能正常运行,不会出现宕机或者安全问题。5.1ChatGPT在接口测试中的角色接口测试是确保系统各个模块协同工作的关键......
  • vue3.5.13 + vite6.0.1搭建前端项目的配置文件
    main.js//vue版本为3.5.13import{createApp}from'vue'import'./style.css'importAppfrom'./App.vue'import'element-plus/dist/index.css'importrouterfrom'./router/index'constapp=createApp(App)......
  • 4、交换IP接口功能
    这一篇是讲端口的功能的,应该放在路由前面的,不过关联不大,就这个顺序也行1、DHCP功能作用:交换机端口的DHCP功能可以使网络中的设备(计算机、打印机等等)能够自动的获取IP地址或其它网络参数(我们用到的一般就是自动获取ip地址)步骤:(不同公司可能不一样,但基本一致)那么在交换机上是......