在线运行器无法使用 webpack 代码分割,因其无构建阶段,依赖浏览器原生 import(),需通过 import.meta.resolve 预检、importmap 统一注册、esm 包装层及沙箱隔离实现按需加载与资源复用。

HTML 在线运行器(如 JSFiddle、CodePen、Playground 类工具)加载大规模依赖库(比如 three.js、monaco-editor、pdfjs-dist)时,不能靠传统打包工具做构建期切片——因为代码是用户实时输入、动态执行的。真正的优化点在于「运行时按需解析 + 精准资源调度」,而非 webpack 的 splitChunks。
为什么不能直接用 webpack 的代码分割?
在线运行器没有构建阶段:用户粘贴的 HTML/JS 是直接送进 iframe 或 eval() 执行的,所有 import() 都走浏览器原生动态导入,而浏览器对 import() 的 resolve 路径、缓存、错误处理非常敏感。
- 用户写
import('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/controls/OrbitControls.js'),但没配crossorigin,会静默失败 - 多个示例同时 import 同一个库的不同子路径(如
core.js和math/Color.js),浏览器会发起多次独立请求,无法复用模块实例 - CDN 返回的 ESM 模块若未声明
type="module"或缺少exports字段,import()会抛TypeError: Failed to resolve module specifier
用 import.meta.resolve() 预检模块可用性
在执行用户代码前,先扫描其字符串内容,提取所有 import() 或 import … from 的字面量路径,用 import.meta.resolve() 验证是否可解析(注意:仅 Chromium 支持,需降级 fallback)。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 对已知大库(如
react、vue)预置映射表:'react'→'https://esm.sh/react@18',避免用户手写错误路径 - 拦截
import('./utils.js')这类相对路径:必须转为绝对 URL(基于当前 playground 页面 origin),否则跨域或 404 - 发现
import('lodash/debounce')这类深层路径时,主动改写为支持的入口(如import('https://esm.sh/lodash@4.17.21?target=es2020&deps=')),防止子路径 404
用 ModuleScript + importMap 实现一次加载、多处复用
用户代码里可能分散写 5 个 import('monaco-editor'),但实际只需加载一次。方案是:不放任用户直连 CDN,而是通过 <script type="importmap"></script> 统一注册模块别名,并用 document.createElement('script').type = 'module' 提前加载核心包。
- 预先注入:
{ "imports": { "monaco-editor": "https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/+esm", "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/+esm" } } - 运行用户代码前,检查其依赖项是否已在
importMap中;未命中则动态追加并触发一次空import()预热 - 关键点:所有后续
import('monaco-editor')都复用同一模块实例,避免重复初始化(如 Monaco 的 worker 注册、Three 的 WebGL 上下文创建) - 注意
importMap不支持通配符,"monaco-editor/*"无效,子路径仍需显式映射或由 CDN 自动解析(如 esm.sh)
对非 ESM 库做轻量包装层
很多老库(如 moment、chart.js)默认输出 IIFE 或 UMD,无法被 import() 直接加载。硬塞 type="module" 会报错,也不能用 require()。
- 检测到非 ESM 入口时,自动包裹一层 ESM shim:
export const moment = await import('https://cdn.jsdelivr.net/npm/moment@2.30.1/min/moment.min.js').then(m => m.default || m); - 对含副作用的库(如
bootstrapCSS),在包装层中同步插入<link rel="stylesheet">,确保样式就位后再 resolve 模块 - 禁止对
document.write或全局污染型脚本做包装——这类库本就不适合在线运行器,应明确报错提示「不支持非模块化库」
最易被忽略的是模块作用域隔离:用户反复运行代码时,import() 返回的模块对象是缓存的,但其内部状态(如全局变量、已绑定事件)不会重置。如果用户代码修改了 three 的默认材质参数,下次运行可能继承上次副作用。这不是代码切片能解决的,得靠 iframe 沙箱或 VM2 级别隔离——但那就超出 HTML 运行器本身范畴了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










