动态加载JavaScript脚本主要有三种方式:通过document.createElement插入标签兼容性最好;import()支持按需加载ES模块并返回Promise;eval或Function构造器适用于执行代码字符串但风险较高。使用时需注意避免重复加载、处理超时及服务端渲染环境。
JavaScript动态加载与执行脚本教程
作者:技术拾光
时间:2026-07-18
浏览:518
文章目录
聊到Ja vaScript的脚本动态加载,本质无非是那几件事:在合适的时间,按需拉取一段代码并执行,还得处理好依赖、容错和性能损耗。但实现起来,大家用的路子却不太一样。那么,到底有哪几种途径,各自又适合什么场景?今天我们把最主流的三种方式掰开揉碎聊清楚。
先说最通用的手段:通过 用
如果说上面是“老江湖”,那
先说最通用的手段:通过 document.createElement 动态插入 标签。这一招兼容性极好,几乎任何JS文件——不管是本地的还是CDN上的——都能驾驭,而且可以方便地处理加载成功与否的回调。
基本流程是这样的:创建一个 script 元素,把 src 指向目标文件路径;如果想异步加载、不阻塞HTML解析,默认把 async 设为 true 就行;假如需要保证执行顺序(仅限于同域脚本,且脚本在 中插入),就把 defer 设为 true。之后,监听 load 事件确认脚本已加载并执行,监听 error 事件捕捉404、跨域等异常。最后把它插入到 document.head 或 document.body,加载就自动触发了。
来个典型的例子:
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Failed to load ${src}`));
document.head.appendChild(script);
});
}
// 使用
loadScript('/js/utils.js')
.then(() => console.log('utils.js loaded'))
.catch(err => console.error(err));
用 import() 动态导入模块(ES Module)
如果说上面是“老江湖”,那 import() 就是现代浏览器和打包工具中的“新王”。它支持真正的按需加载、tree-shaking 和静态分析,返回一个 Promise,天然支持 async/await 展开异步逻辑。
不过要注意,它只能加载符合 ES 模块规范的文件——也就是 type="module" 的脚本或后缀为 .mjs 的文件。而且路径必须是字符串字面量或模板字符串,不能是拼接变量,否则打包工具压根没法做静态分析。导入之后,直接使用导出出来的函数、类或值就行,不需要自己搞额外的执行逻辑。顺带一提,多次调用同一路径,模块会被复用,不会重复加载。
看段代码就清楚了:
// utils.js 导出:export function formatDate(date) { ... }
// 动态加载并使用
async function init() {
const { formatDate } = await import('./utils.js');
console.log(formatDate(new Date()));
}
手动执行内联或远程脚本内容
第三种路子,适用于加载纯代码字符串——比如从API拉回来一段JS片段,或者想绕过CORS限制(但风险很高,不推荐用于不可信来源)。这时候可以用eval 或 Function 构造器。
具体做法:先用 fetch 拿到脚本文本内容,再用 new Function(code)() 执行。相比 eval,Function 构造器作用域隔离得更好,相对安全些。但要注意,这样执行的代码无法访问当前作用域的变量(除非显式传参),而且调试体验也差很多。如果脚本里包含 import/export,就不能直接执行了,得先转成模块,或者改用 import()。
来个仅限可信上下文的例子:
async function executeScript(url) {
const res = await fetch(url);
const code = await res.text();
new Function(code)();
}
注意事项与最佳实践
动态加载不是万能药,用起来必须分清场景。 首先,避免在循环里无节制地加载同一个脚本——缓存加载的 Promise,或者用document.querySelector(`script[src="${src}"]`) 检查一下是否已经存在,能省不少冤枉请求。其次,第三方脚本(比如统计分析、埋点脚本)最好加个超时控制,以免网络波动把后续逻辑卡死。另外,在服务端渲染环境下,document 压根不存在,必须先用 if (typeof document !== 'undefined') 兜底。最后,调试时别只盯着代码看,去 DevTools 的“Network”面板确认脚本是否正确发起了请求,再到“Sources”面板看它有没有成功加载并执行,往往能更快定位问题。实用工具推荐
电子包浆
使用 ›绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
图片压缩
使用 ›选择画质和输出格式,预览压缩前后的大小。
相关文章