13. 模块化:import / export
前端项目不可能把所有代码都写在一个 JS 文件里,所以需要模块化。
你可以先理解成:
export= 把东西暴露出去
import= 从别的文件拿进来
13.1 命名导出 export
比如文件:
js
// user.js
export const userName = '张三'
export function getUser() {
return {
id: 1,
name: '张三'
}
}
别的文件使用:
js
import { userName, getUser } from './user'
console.log(userName)
const user = getUser()
注意:
js
import { userName, getUser } from './user'
这里有 {}。
因为这是:
命名导出。
名字一般要对应。
13.2 也可以最后统一导出
js
const userName = '张三'
function getUser() {
return {
id: 1,
name: '张三'
}
}
export {
userName,
getUser
}
和刚才效果一样。
13.3 export default
还有一种:
js
export default
例如:
js
// user.js
const user = {
id: 1,
name: '张三'
}
export default user
引入:
js
import user from './user'
这里就没有 {}。
default 导入可以改名字
例如原文件:
js
export default user
你可以:
js
import aaa from './user'
也可以:
js
import test from './user'
都能用。
因为:
js
export default
一个模块只能有一个默认导出。
所以导入的时候名字可以自己取。
13.4 命名导出和默认导出的区别
这个很容易混。
命名导出
js
export function getUser() {
}
export function updateUser() {
}
引入:
js
import {
getUser,
updateUser
} from './user'
默认导出
js
export default request
引入:
js
import request from '@/utils/request'
若依项目里肯定见过:
js
import request from '@/utils/request'
这说明 request.js 很可能最后:
js
export default service
比如:
js
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 35000
})
export default service
然后接口文件:
js
import request from '@/utils/request'
export function listUser(params) {
return request({
url: '/system/user/list',
method: 'get',
params
})
}
这里其实同时存在:
text
request
↓
默认导入
listUser
↓
命名导出
13.5 重命名
命名导入也可以改名字:
js
import {
getUser as queryUser
} from './user'
那么:
js
queryUser()
就相当于原来的:
js
getUser()
13.6 导入整个模块
还可以:
js
import * as userApi from './user'
假设:
js
export function getUser() {}
export function addUser() {}
export function deleteUser() {}
那么:
js
userApi.getUser()
userApi.addUser()
userApi.deleteUser()
这种写法的好处是来源清晰:
js
userApi.getUser()
一眼知道是用户接口。
14. 可选链 ?.
这个特别实用。
它主要解决:
对象可能是
null或undefined,继续访问属性会报错。
先看传统问题。
js
const user = null
console.log(user.name)
会报错:
text
Cannot read properties of null
14.1 使用 ?.
可以:
js
const user = null
console.log(user?.name)
结果不是报错,而是:
js
undefined
意思就是:
user 有值,就继续访问 name。
user 是 null / undefined,就直接返回 undefined。
14.2 多层对象特别好用
接口返回:
js
const response = {
data: {
user: {
name: '张三'
}
}
}
正常:
js
const name = response.data.user.name
但如果某一次:
js
response.data.user
是:
js
null
就报错。
以前你可能写:
js
let name
if (
response &&
response.data &&
response.data.user
) {
name = response.data.user.name
}
很啰嗦。
现在:
js
const name = response?.data?.user?.name
就够了。
14.3 Vue 实战
例如:
js
const customerName = order?.customer?.name
假如:
js
order = null
没问题。
假如:
js
order.customer = null
也没问题。
最后:
js
customerName === undefined
数组也可以
例如:
js
const firstUserName = response?.rows?.[0]?.name
意思:
text
response 存在吗?
↓
rows 存在吗?
↓
第 0 个元素存在吗?
↓
取 name
14.4 方法调用也可以
例如:
js
user?.sayHello?.()
意思:
user 存在,并且 sayHello 方法存在,才调用。
这个在一些回调场景也有用:
js
options.onSuccess?.()
以前可能要写:
js
if (options.onSuccess) {
options.onSuccess()
}
现在一行:
js
options.onSuccess?.()
14.5 不要滥用 ?.
这点很重要。
比如:
js
const orderId = order?.id
如果业务上 order 理论上必须存在,那么疯狂用 ?. 有时候反而会隐藏问题。
例如本来应该报错:
js
order.id
结果你用了:
js
order?.id
最后得到:
js
undefined
程序继续跑,可能过一会儿才出别的问题。
所以:
数据本来就允许为空 → 用
?.
数据理论上必须存在 → 不一定要用,应该尽早发现异常。
15. 空值合并 ??
这个通常会和 ?. 配合使用。
先看:
js
const name = null
希望:
如果 name 没值,就使用默认值。
可以:
js
const result = name ?? '未知用户'
结果:
js
'未知用户'
15.1 ?? 判断什么
只有两个值会触发默认值:
js
null
undefined
所以:
js
null ?? '默认值'
// 默认值
js
undefined ?? '默认值'
// 默认值
但是:
js
0 ?? 100
结果:
js
0
js
false ?? true
结果:
js
false
js
'' ?? '默认值'
结果:
js
''
这就是它最大的价值。
15.2 ?? 和 || 的区别
这个是重点。
以前大家经常写:
js
const quantity = value || 10
但是这里有个问题。
假设:
js
value = 0
那么:
js
const quantity = 0 || 10
结果:
js
10
可业务上:
text
0
可能是合法值(对的值)。
这时候应该:
js
const quantity = value ?? 10
结果:
js
0
为什么?
因为 || 会把这些都认为是假:
text
false
0
''
null
undefined
NaN
而 ?? 只认:
text
null
undefined
所以可以直接记:
text
||
没有"有效真值"就给默认值
而:
text
??
只有真正没值时才给默认值
15.3 实战:数量
例如接口:
js
const stock = {
availableQty: 0
}
如果写:
js
const qty = stock.availableQty || 100
结果:
js
100
明显错了。
因为库存确实是:
text
0
正确:
js
const qty = stock.availableQty ?? 100
结果:
js
0
这在:
text
数量
金额
状态值
布尔值
这种业务数据里尤其重要。
15.4 ?. 和 ?? 一起使用
这是非常经典的组合。
例如:
js
const userName = response?.data?.user?.name ?? '未知用户'
逻辑是:
text
先安全地取:
response.data.user.name
如果中间不存在
↓
得到 undefined
然后
↓
?? '未知用户'
最终:
text
有姓名
→ 返回姓名
没有姓名
→ 返回 "未知用户"
更贴近后台业务
例如:
js
const availableQty =
response?.data?.stock?.availableQty ?? 0
表示:
如果接口里真的有库存数量,就使用接口的数据。
即使:
js
availableQty = 0
也保留:
js
0
只有数据不存在:
js
null
或者:
js
undefined
才使用默认值:
js
0
把第 13~15 个串起来
比如接口文件:
js
// order.js
import request from '@/utils/request'
export function getOrder(orderId) {
return request({
url: `/xxx/order/${orderId}`,
method: 'get'
})
}
组件里:
js
import { getOrder } from '@/api/repair/order'
async function loadOrder(orderId) {
const response = await getOrder(orderId)
const customerName =
response?.data?.customer?.name ?? '未知客户'
const amount =
response?.data?.amount ?? 0
console.log(customerName)
console.log(amount)
}
这里已经把很多前面知识串起来了:
text
13. import
↓
import { getOrder } ...
11. async / await
↓
await getOrder(...)
14. 可选链
↓
response?.data?.customer?.name
15. 空值合并
↓
?? '未知客户'
到现在 15 个知识点已经形成完整体系
你现在可以这样记:
text
1. let / const
变量
2. 模板字符串
`${}`
3. 解构赋值
const { data } = response
4. 箭头函数
item => item.id
5. 对象增强
{ name, age }
6. 展开运算符
{ ...user }
7. 剩余参数
(...args)
8. 默认参数
pageNum = 1
9. 数组方法
map/filter/find/some/every
10. Promise
异步基础
11. async / await
异步实战
12. class
类
13. import / export
模块化
14. ?.
安全访问可能为空的数据
15. ??
null / undefined 时提供默认值