带defer属性的脚本导致Angular应用加载受阻问题咨询
问题描述
我有一个依赖某不稳定服务器脚本的Angular应用,该脚本加载时常耗时较久,因此我为其添加了defer属性,但发现应用仍会等待该延迟脚本下载完成后才加载。
根据调研,Angular应用应在DOMContentLoaded前渲染,我甚至在全新Angular应用中测试,将defer.js的加载延迟10秒,结果依旧如此。
初始HTML代码
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Untitled</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <script src="http://localhost:8080/defer.js" defer></script> </head> <body> <app-root></app-root> </body> </html>
浏览器实际加载的HTML代码
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Untitled</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <script src="http://localhost:8080/defer.js" defer=""></script> <link rel="stylesheet" href="styles.css"> </head> <body> <app-root></app-root> <script src="runtime.js" type="module"></script> <script src="polyfills.js" type="module"></script> <script src="styles.js" defer></script> <script src="vendor.js" type="module"></script> <script src="main.js" type="module"></script> </body> </html>
测试环境:使用Chrome浏览器插件URL Throttler进行模拟测试
解决方案
问题根源在于浏览器对传统defer脚本和ES模块脚本的执行顺序规则:
- 你添加的
defer.js是传统延迟脚本,而Angular注入的核心脚本(runtime.js、polyfills.js等)都是type="module"类型的ES模块脚本。 - 浏览器会优先执行所有传统
defer脚本,再执行ES模块脚本。这就导致defer.js的延迟加载直接阻塞了Angular核心脚本的执行,进而让应用必须等它加载完成才启动渲染。
推荐两种解决方式:
将外部脚本改为ES模块类型
ES模块默认具备类似defer的异步加载特性,修改后它会和Angular的模块脚本并行加载,不会互相阻塞执行:<script src="http://localhost:8080/defer.js" type="module"></script>动态加载外部脚本
在Angular应用启动后手动加载该脚本,完全不影响应用初始化过程,比如在AppComponent的ngOnInit中添加逻辑:ngOnInit() { const script = document.createElement('script'); script.src = 'http://localhost:8080/defer.js'; script.onload = () => { // 可添加脚本加载完成后的业务逻辑 }; document.body.appendChild(script); }
内容的提问来源于stack exchange,提问作者faragos
相关产品推荐
相关产品推荐

