import()是es模块的动态导入语法,返回promise,由浏览器运行时发起http请求加载并执行模块,同时依赖构建工具(如webpack)对可预测路径进行预打包和映射,支持按需加载、作用域隔离与构建优化,但路径须满足静态可分析性。

动态导入(import())的底层原理,其实可以类比“点外卖”:你不用一早就把所有菜都买回家堆满厨房(静态导入),而是等客人来了、想吃哪道菜,再临时下单,餐厅做好后送上门——整个过程不耽误你招待客人,厨房也一直清爽。
它不是语法糖,而是浏览器+构建工具协同完成的“按需派单”
动态导入不是单纯靠 JS 引擎自己搞定的,背后有两层配合:
-
运行时层(浏览器):当执行到
import('./xxx.js'),浏览器会发起一个 HTTP 请求去加载这个文件,解析并执行它,最后把导出的对象包装进一个 Promise 返回。这一步和fetch()+eval()逻辑相似,但更安全、受模块系统约束(比如作用域隔离、this绑定、循环依赖处理)。 -
构建时层(Webpack/Vite/Rollup):工具会扫描代码里的
import()调用,识别出路径是否“可预测”。比如import(`./locales/${lang}.js`),虽然用了变量,但结构固定,工具能推断出可能的文件是./locales/zh.js、./locales/en.js等,于是提前把这些文件打包成独立 chunk,并生成映射表。真到了运行时,就直接从已准备好的资源里取,而不是现场拼个乱七八糟的 URL 去 404。
为什么必须返回 Promise?
因为加载 JS 文件本质上是异步行为:要发网络请求、下载、解析、执行——每一步都可能耗时或失败。Promise 是 JavaScript 处理这种“将来才有的结果”的标准方式。它天然支持 await 和 .then(),让你能自然地写“等它加载完再干啥”,而不是卡住主线程或手动维护回调嵌套。
它和手写 document.createElement('script') 有啥区别?
表面都是“动态加 JS”,但根本不同:
- 手写 script 标签加载的是普通脚本,没有模块作用域,不能用
export/import,变量容易污染全局,也无法被 tree-shaking 或做依赖分析; -
import()加载的是真正的 ES 模块,享有完整模块特性:作用域封闭、静态分析支持、默认严格模式、可被构建工具优化(如自动分割、预加载提示、错误边界捕获)。
路径不能完全随意,是有“可分析性”要求的
你不能写 import(someUnpredictableUrl),比如从用户输入、随机数或远程 API 拿来的路径——构建工具无法提前知道要打包哪些文件,运行时大概率报错 Failed to resolve import。它接受的是“结构可控”的动态路径,例如:
-
import(`./pages/${pageName}.js`)→ 工具能猜出./pages/*.js -
import('./utils/' + tool + '.js')→ 若tool是确定的字符串变量(如 const tool = 'date'),也能被识别
说白了:它要的是“有限枚举”,不是“无限可能”。这是为了兼顾灵活性和可构建性。











