HTML5原生按需加载依赖import()动态导入、loading="lazy"图片懒加载、type="module"/nomodule分流及Service Worker缓存。import()返回Promise需异步处理;lazy仅适用于非首屏图片;module脚本自动defer;SW需匹配构建生成的chunk路径并清理旧缓存。

HTML5里没有import,但可以用import()动态加载模块
浏览器原生支持的模块按需加载,靠的是import()这个函数式动态导入语法,不是ES6静态import语句。静态import在解析时就确定依赖,无法条件触发;而import()返回Promise,适合点击、滚动、路由切换等时机调用。
常见错误是写成import('./module.js')却没处理Promise,导致报错或阻塞渲染。它必须配合.then()或async/await使用。
-
import()路径必须是字符串字面量或模板字符串(不能是拼接变量),否则打包工具会报错 - Webpack/Vite会自动将
import()转为独立chunk,但原生环境只支持相对/绝对URL,不支持包名 - 加载失败时Promise reject,建议用
try/catch包裹,避免未捕获异常
button.addEventListener('click', async () => {
try {
const { renderChart } = await import('./charts.js');
renderChart();
} catch (err) {
console.error('图表模块加载失败:', err);
}
});
用loading="lazy"和decoding="async"按需加载图片资源
这不是JS模块加载,但属于HTML5原生按需加载的关键实践。对长页面中大量图片,盲目预加载会拖慢首屏,而loading="lazy"让浏览器只在图片即将进入视口时才发起请求。
注意:该属性仅对和生效,且Safari 15.4+、Chrome 76+才支持。旧版需回退到IntersectionObserver方案。
立即学习“前端免费学习笔记(深入)”;
-
decoding="async"让图片解码不阻塞主线程,尤其适合大图或多图并行加载 - 不要对首屏关键图加
loading="lazy",否则可能被延迟甚至跳过加载 - 服务端渲染(SSR)中若初始HTML已含
loading="lazy",客户端 hydration 不会覆盖它
@@##@@
通过type="module" + nomodule做现代/旧版模块分流
不是所有用户都用新浏览器,但你又想用ES模块语法开发。HTML5提供type="module"标记,让支持模块的浏览器加载现代代码,同时用nomodule属性让旧浏览器降级加载打包后的bundle.js。
关键点在于:浏览器对type="module"的支持是硬性判断,不依赖UA,所以比JS特征检测更可靠。
-
type="module"脚本默认defer,无需额外声明 -
nomodule脚本在支持模块的浏览器中完全不执行,包括内联脚本 - Vite/Webpack生成的
legacy构建通常就靠这对属性做兼容分发
Service Worker缓存策略决定“按需”的实际效果
即使用了import(),如果每次都要网络请求,那只是“延迟加载”,不算真正“按需”。Service Worker能拦截import()发出的fetch请求,从Cache Storage返回已缓存模块,实现离线可用和秒级加载。
容易忽略的是:动态import()的chunk名由构建工具生成(如chunk-abc123.js),缓存策略必须匹配这些运行时路径,不能只写死/modules/前缀。
- 推荐用Workbox的
registerRoute({ urlPattern: /.*\.js$/, handler: 'StaleWhileRevalidate' })捕获所有JS请求 - 首次安装SW时不会缓存已加载的模块,需在激活后主动
cache.addAll()关键chunk - 模块更新后,旧缓存仍存在,需在activate事件中清理过期key
模块化加载的复杂点不在语法,而在加载时机、缓存生命周期、错误兜底这三者的耦合。一个import()调用背后,可能牵扯构建配置、HTTP缓存头、SW版本管理、以及用户网络状态判断——漏掉任何一环,“按需”就变成“按运气”。











