前言
你是否也经常被下面这种报错整得抓狂?
- “TypeError: Cannot read property *** of undefined”
- “TypeError: Cannot read property *** of null”
通常这种报错,在控制台打印的都是堆栈信息,往往很难定位到具体位置,而且这种报错信息是很容易复发的,所以我们要经常脑神费心的去一次又一次的解决这个问题,这是一个前端通病大家都会遇到,今天这篇文章的主题就是找一个方法来完美解决这个问题!
分析原因
先看一段代码:
1 | const firstName = message.body.user.firstName; |
- 假设
message
下面并没有body
,那么就会报错:Cannot read property user of undefined
- 假设
message
下面有body
但是值为null
,那么就会报错:Cannot read property user of null
- 同理假设
meseage.body
没问题,但是user
没有定义或者值为null
会报错Cannot read property firstName of undefined/null
日常开发中,前端通过接口获取后端数据后,往往都会出现这样的赋值,而后端如果不严格按照报文来返回数据结构,就会出现这种问题!
比如: 后端觉得 user
下面只有 firstName
一个属性,如果 firstName
有值,那就完整返回,如果没有值,直接连 user
这一层都去掉不返回。
- 当
firstName
有值时返回数据
1 | { |
- 当
firstName
没有值时返回数据
1 | { |
传统解决方案
所以我们如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。安全的写法是写成下面这样。
1 | // 正确的写法 |
上面例子中,firstName
属性在对象的第四层,所以需要判断四次,每一层是否有值,非常的繁琐且有失优雅,而且经常是在前后端联调的时候是有值的,我们就直接赋值漏了判断也没问题(很容易忘记写判断,这也是为什么这个问题很容易复发的原因)
,但是等到上线之后,数据变成没有值了,后端返回的数据结构变动了,所以导致报错,而且还不容易定位具体位置,很难排查,所以我们希望后端就算没有值,也保留数据结构,返回完整的报文结构, 没有值就约定一个 空字符串
或者 null
,这样就不需要一层一层去判断,比如这样:
1 | { |
链判断运算符
上面两种解决方案,第一种层层判断非常麻烦、不够优雅、容易遗忘,第二种后端报文基于框架原因或者个人习惯原因,数据结构也不可控,即使可控也麻烦且不安全,因此 ES2020
引入了 链判断运算符
(optional chaining operator)?.
,简化上面的写法。
1 | const firstName = message?.body?.user?.firstName |
上面代码使用了 ?.
运算符,直接在链式调用的时候判断,左侧的对象是否为 null
或 undefined
。如果是的,就不再往下运算 (短路机制)
,而是返回 undefined
。下面是判断对象方法是否存在,如果存在就立即执行的例子。
1 | iterator.return?.() |
上面代码中,iterator.return
如果有定义,就会调用该方法,否则 iterator.return
直接返回 undefined
,不再执行 ?.
后面的部分。
如何在 vue 项目中应用这种新语法
在
vue 3.0
后可以直接使用链判断运算符(?.)
语法, 否则就要借助babel
插件解析帮助我们进行代码转换。
由于 vue 3.0
还没有普及,我们还是有必要研究一下如何使用插件提供语法支持:
- 插件名称 @babel/plugin-proposal-optional-chaining
- 文档地址 https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining
升级 babel
vue-cli
搭建的项目中,babel
版本默认是小于 7
的,为了使用 @babel/plugin-proposal-optional-chaining
, 我们需要先将 babel
升级到 7
,官方提供了一个工具 babel-upgrade
, 对于已有项目,只需要运行一行命令就可以升级到 7
版本以上了,如果找不到 npx
命令就检查一下 npm
版本是否 < 5.2.0
.
1 | npx babel-upgrade --write --install |
兼容版本
升级完之后运行项目可能会出现一些报错信息:
- Cannot find module ‘babel-plugin-syntax-jsx’
- Cannot find module ‘@babel/core’
根据报错信息,我们稍作调整,将 @babel/plugin-syntax-jsx
卸载,更换成 babel-plugin-syntax-jsx
,将 @babel/polyfill
卸载,更换成 babel-polyfill
,再安装一个 @babel/runtime-corejs2
1 | npm uninstall @babel/plugin-syntax-jsx |
修改配置文件
最后在 .babelrc
文件中注入插件, 在 plugins
数组中 插入 "@babel/plugin-proposal-optional-chaining"
:
1 | { |
总结
完成上述步骤之后,不要忘记重启项目,通常配置文件有改动都需要重启项目才生效,然后就可以愉快的使用 链判断运算符(?.)
语法了,正式告别那些一层一层的判断,还有反复出现的 "TypeError: Cannot read property *** of undefined/null"
。