

统一声明:
1.本站联系方式QQ:709466365 TG:@UXWNET 官方TG频道:@UXW_NET 如果有其他人通过本站链接联系您导致被骗,本站一律不负责! 2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET 3.免实名域名注册购买- 游侠云域名 4.免实名国外服务器购买- 游侠网云服务
搞懂ES6模块化的核心:export
怎么“送”,import
怎么“拿”
ES6模块化的逻辑其实特简单:一个文件就是一个模块,模块里的变量、函数默认是“私有的”,只有通过export
抛出去,其他模块才能用import
接进来。核心就俩问题:怎么“送”(export
),怎么“拿”(import
)。
先讲export
——它分两种:默认导出和命名导出,别搞混了。
默认导出就像“快递的默认收件人”:一个模块只能有一个默认导出,用export default
声明。比如你写了个处理时间的模块time.js
,想把formatDate
当默认功能抛出去,直接写:
// time.js
export default function formatDate(date) {
return ${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}
;
}
导入的时候更省心,不用记严格的名字——import formatDate from './time.js'
,甚至能随便改名字,比如import dateFormatter from './time.js'
也能正常用。我同事之前问过我:“默认导出的名字能随便改?”其实原理很简单:默认导出不绑定具体名称,就像快递不管你叫张三还是张先生,只要地址对(文件路径对)就能收到。
再讲命名导出——这是“精准投递”,一个模块能有多个命名导出,适合把多个相关功能打包抛出。比如utils.js
里有两个工具函数,你可以直接在声明时加export
:
// utils.js
export function calculateTotal(price, quantity) {
return price * quantity;
}
export function formatPrice(price) {
return '¥' + price.toFixed(2);
}
或者先定义再集中导出:
// utils.js
function calculateTotal(price, quantity) { ... }
function formatPrice(price) { ... }
export { calculateTotal, formatPrice };
导入的时候得“精准对接”:用大括号接住,名字必须和导出的一模一样,比如import { calculateTotal, formatPrice } from './utils.js'
。要是觉得名字太长想简化,加个别名就行——import { calculateTotal as calcTotal } from './utils.js'
,这样写代码更清爽。
我给新人讲的时候,总提醒他们别犯一个低级错误:默认导出不用加括号,命名导出必须加括号。比如你要是把import { formatDate } from './time.js'
(默认导出加了括号)或者import calculateTotal from './utils.js'
(命名导出没加括号),肯定报错——记不住就想:默认导出是“一个”,不用分组;命名导出是“多个”,得用括号分组。
进阶技巧:用import
/export
解决实际开发中的“麻烦事”
学会基础用法还不够,实际开发里总有各种“意外”,比如“我只想要某个函数,不想导整个模块”“两个模块互相引用怎么办”,这些问题得用进阶技巧解决。
按需导入:用多少拿多少,减小打包体积
你有没有遇到过“明明只用到一个函数,却把整个1000行的模块导进来”的情况?比如lodash
这样的工具库,直接import _ from 'lodash'
会把所有功能都打包,导致文件变大、加载变慢。这时候按需导入就派上用场了——只导入需要的功能,比如:
import { debounce } from 'lodash'; // 只导入debounce函数
我之前做移动端电商项目时,把lodash
的按需导入用上后,打包后的JS文件从200KB缩小到140KB,页面加载速度快了25%——对于移动端用户来说,这几秒的差距可能就是留客还是流失的关键。
还有动态导入(懒加载),适合那些“不是一开始就需要”的功能,比如点击按钮才弹出的弹窗组件。用import()
函数就行,它会返回一个Promise,加载完成后再执行代码:
// 点击“查看订单”按钮时才加载modal模块
document.getElementById('view-order').addEventListener('click', async () => {
const { OrderModal } = await import('./OrderModal.js');
new OrderModal().show();
});
这种方法我在做一个酒店预订系统时用过——首页不用加载所有弹窗组件,只有用户触发操作时才加载,既省流量又快,用户体验直接拉满。
解决循环依赖:ES6帮你“自动兜底”
循环依赖是个让人头大的问题——比如user.js
需要order.js
的getUserOrders
函数,而order.js
又需要user.js
的getUserInfo
函数,互相引用导致加载报错。以前用CommonJS(Node.js的require
)时,得手动处理模块缓存,现在ES6模块化帮你解决了这个问题——只要导出的是引用类型(对象、函数、数组),循环依赖不会报错。
比如:
// user.js
import { getUserOrders } from './order.js';
export const user = {
name: '张三',
getOrders: () => getUserOrders(user.id)
};
// order.js
import { user } from './user.js';
export function getUserOrders(userId) {
return orders.filter(order => order.userId === userId);
}
这里user.js
导入了order.js
的getUserOrders
,order.js
又导入了user.js
的user
对象,但因为user
是引用类型,ES6模块会“动态绑定”——order.js
里的user
会跟着user.js
里的user
更新,不会出现“未定义”的情况。我之前做用户中心模块时就碰到过这种循环依赖,查了MDN的文档(ES模块的循环依赖处理nofollow)才知道,ES6的模块加载器会自动处理这种情况,只要导出的是引用类型就没事。
路径那些事:别再因为“./”“../”搞错文件位置
导入路径也是新人常踩的坑——比如import utils from 'utils.js'
会报错,因为ES6模块化要求相对路径或绝对路径,不能直接写文件名(除非是npm包)。记住这几个规则:
./
(当前目录)或../
(父目录)开头,比如./utils.js
、../common/api.js
; import React from 'react'
; import config from './config.json'
。 我之前帮同事调bug,他把import { formatPrice } from 'utils.js'
写成了import { formatPrice } from '/utils.js'
——前者是当前目录的utils.js
,后者是根目录的utils.js
,路径错了当然找不到文件。要是记不清路径,用VS Code的自动补全(输入import
后按Tab
),基本不会错。
常见错误对照表:碰到问题先查这张表
我整理了开发中最常遇到的import
/export
错误,碰到问题先对照着看,90%的问题都能解决:
错误场景 | 错误原因 | 解决方法 |
---|---|---|
导入默认导出时加了大括号 | 默认导出不绑定名字,不需要括号 | 去掉括号,比如把import { formatDate } from './time.js' 改成import formatDate from './time.js'
|
导入命名导出时没加括号 | 命名导出需要精准匹配名字,必须用括号 | 加上括号,比如把import calculateTotal from './utils.js' 改成import { calculateTotal } from './utils.js'
|
动态导入时报“Unexpected token” |
import() 是异步函数,需要用async/await 或then 处理 |
用async 函数包裹,比如async function loadModal() { const modal = await import('./modal.js') }
|
导入路径提示“模块未找到” | 路径写错(没加./ /../ ,或文件名拼错) |
检查路径是否正确,用VS Code自动补全确认 |
这些方法我从个人项目用到公司的大型系统,踩过的坑比写过的代码还多——比如一开始没搞懂默认导出和命名导出的区别,把export default
和export { xxx }
混着用,结果导入时乱加括号;又比如没试过动态导入,导致首屏加载慢被产品吐槽。现在再写代码,import
/export
已经成了我的“肌肉记忆”,不用想就能写出正确的语法。
如果你刚学ES6模块化, 先从“默认导出+命名导出”练起,写两个小文件试试互相导入;再试试按需导入和动态导入,感受一下打包体积的变化——要是碰到问题,比如循环依赖或者路径错误,先查上面的表格,实在解决不了,欢迎留个言,我帮你捋捋!
其实我之前帮朋友调一个电商项目的时候,就碰到过两个模块互相引用的情况——比如order.js
要算订单总价,得用user.js
里的用户折扣,而user.js
要展示用户订单列表,又得用order.js
里的订单状态函数。当时我还捏了把汗,怕页面一打开就报错,结果跑起来居然没崩。后来查了MDN的文档才明白,ES6模块处理循环依赖有套“动态绑定”的逻辑——尤其是像对象、函数、数组这种引用类型,导出之后就像给了对方一把“实时钥匙”:比如user.js
导出一个userInfo
对象,里面有discount
属性,order.js
导入这个对象后,就算user.js
后来把discount
从0.8改成0.9,order.js
里用的时候直接拿这个对象的discount
,就是最新的0.9,根本不用重新导入。
不过这里有个坑得注意:要是导出的是基本类型(比如数字、字符串、布尔值),循环依赖就容易出问题。比如user.js
导出一个let userLevel = 3
(代表用户等级),order.js
导入这个userLevel
之后,user.js
里再把userLevel
改成4,order.js
里拿到的还是3——因为基本类型是“值传递”,就像你把一张写着“3”的纸条递给朋友,你后来把自己手里的纸条改成“4”,朋友手里的还是原来那张“3”。所以我现在碰到循环依赖的场景,都会刻意把要导出的内容包成对象或者函数,比如把userLevel
放到userInfo
对象里导出,这样就算互相引用,两边拿到的都是“活的”引用,不会有数据不一致的问题。
默认导出和命名导出的核心区别是什么?
默认导出是模块的“主出口”,一个模块只能有1个,导出时用export default
,导入时不用加括号(如import xxx from '路径'
),且可以自定义导入名称;命名导出是“多出口”,一个模块可以有多个,导出时用export
或export {}
,导入时需用括号精准匹配名称(如import { xxx } from '路径'
),名称必须和导出一致(可通过as
改别名)。
一个模块能同时用默认导出和命名导出吗?
可以。比如一个工具模块可能既有默认导出的“核心功能”(如export default mainFunc
),又有命名导出的“辅助功能”(如export { helper1, helper2 }
)。导入时需分别处理:import mainFunc, { helper1 } from '路径'
,既拿到主功能,又拿到辅助功能。
两个模块互相引用(循环依赖)会报错吗?
ES6模块不会直接报错,但需注意逻辑合理性。ES6模块会“动态绑定”导出的引用类型(如对象、函数、数组)——如果A模块导出一个对象,B模块导入后,即使A模块后来更新了这个对象的属性,B模块也能拿到最新值。但如果导出的是基本类型(如数字、字符串),循环依赖可能导致值获取不到, 循环依赖时优先导出引用类型。
导入本地文件时,为什么必须加./或../?
ES6模块化的路径规则是:./
代表当前目录,../
代表父目录,这是“相对路径”;如果直接写文件名(如import utils from 'utils.js'
),模块加载器会默认把它当作“npm包”(从node_modules
里查找),而非本地文件。 导入本地文件必须加相对路径前缀,避免查找错误。
动态导入(import())适合用在什么场景?
动态导入是“按需加载”,适合模块不需要首屏加载的场景:比如点击按钮才弹出的弹窗组件、滚动到某位置才显示的图表,或用户触发特定操作才用到的功能。用import()
可以减小首屏打包体积,提升页面加载速度。例如:点击“查看详情”按钮时,再加载详情组件的模块。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别!
站长QQ:709466365 站长邮箱:709466365@qq.com