ESM需显式声明type="module",不支持裸导入,file://协议下受限于CORS和MIME类型需本地服务器,顶层await和动态import在现代浏览器中可用。

ESM(ECMAScript Module)在现代浏览器中已获得原生支持,但并非“开箱即用”,实际使用时需注意加载方式、跨域策略、文件协议限制等关键约束。
必须通过 type="module" 声明才能启用 ESM
浏览器不会自动将 <script></script> 当作模块处理。即使文件内容是标准 import/export 语法,也必须显式添加 type="module" 属性:
<script type="module" src="main.js"></script> <!-- ✅ 正确:触发模块解析 --> <p><script src="main.js"></script> <!-- ❌ 错误:按传统脚本执行,import 会报 SyntaxError -->
同时,带 type="module" 的脚本默认为 defer 行为(即不阻塞 HTML 解析,且按顺序执行),无需额外写 defer。
模块路径必须是完整 URL 或相对路径,不支持 bare specifiers
浏览器原生 ESM 不支持 Node.js 风格的裸导入(如 import { foo } from 'lodash')。所有模块说明符必须是有效的 URL:
立即学习“Java免费学习笔记(深入)”;
-
import { init } from './utils.js';✅ 相对路径 -
import React from 'https://cdn.skypack.dev/react@18';✅ 完整 HTTPS URL -
import { debounce } from 'lodash';❌ 报错:无法解析 bare specifier
若需使用第三方包,必须通过 CDN 提供的可直接加载的 ESM 入口(如 Skypack、esm.sh、jspm),或自行构建打包产物。
受限于同源策略与 CORS,本地文件(file://)几乎不可用
直接双击打开 HTML 文件(URL 以 file:// 开头)时,大多数浏览器会拒绝加载模块脚本,报错类似:
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/plain".
这是因为:
– 模块脚本需服务端返回 Content-Type: application/javascript;
– file:// 协议无 HTTP 头,浏览器无法确认类型,且多数现代浏览器主动阻止跨 origin 模块加载(即使同目录也被视为不同 origin)。
解决方法只有:启动一个本地服务器(如 npx serve、python3 -m http.server 或 VS Code Live Server 插件)。
顶层 await 和动态 import 是可用的,但有兼容性边界
现代主流浏览器(Chrome 89+、Firefox 89+、Safari 15.4+)已支持顶层 await,可在模块顶层直接写:
const data = await fetch('/api/config').then(r => r.json());
console.log(data); // ✅ 合法动态 import() 也完全可用,且返回 Promise,适合代码分割或条件加载:
-
const mod = await import('./feature.js');✅ 支持 -
import('./legacy-polyfill.js').then(...)✅ 支持
但注意:IE 完全不支持 ESM,旧版 Safari(


















