使用document.writeln加载S3预签名URL内容时远程jQuery无法加载
问题描述
尝试从S3预签名URL动态加载内容,使用document.writeln()实现,但远程jQuery及其他库无法正常加载,报错:
VM413:1 Uncaught ReferenceError: $ is not defined at :1:1
使用的代码如下:
var url = "S3 Pre-signed url"; fetch(url).then(response => response.text()).then(data => { var lines = data.split("\n"); for (var i = 0; i < lines.length; i++) { var line = lines[i]; if (line.toUpperCase().indexOf("<SCRIPT ") >= 0) { if (line.indexOf("$") < 0) { var index1 = line.toUpperCase().indexOf("<SCRIPT "); var before_line = line.substring(0, index1 - 9); var index2 = line.indexOf(">"); var sub = line.substring(index1 - 8, index2); var after_line = line.substring(index2 + 1); //var sub2 = sub.substring(0,index2-1); sub += " async >"; //sub = sub.replace(/"/g, '\""); var line2 = before_line + sub + after_line; line2 = line2.replace(/<\/script>/g, "<\/script>") document.writeln(line2) } else { //console.log("Index of $") document.writeln(line) } } else { document.writeln(line) } } })
问题原因
document.writeln()的局限性:若在页面加载完成后调用该方法,会清空当前文档并创建新文档,导致已加载的脚本丢失;即便在页面加载中使用,也难以精准控制脚本的加载顺序。- 盲目添加
async属性:给jQuery脚本强制添加async会使其异步加载,而依赖jQuery的脚本(含$的代码)可能提前执行,此时jQuery尚未加载完成,就会触发$ is not defined错误。 - 按行拆分HTML的风险:HTML标签(包括
<script>)可能跨多行,用split("\n")拆分后会破坏标签结构,导致脚本无法被正确解析执行。
解决方案
1. 改用DOM操作加载内容,弃用document.writeln()
使用DOMParser解析完整的HTML字符串,再将解析后的节点插入页面,能完整保留原HTML的结构和逻辑。
2. 严格控制脚本加载顺序
对于有依赖关系的脚本(比如必须先加载jQuery),通过监听脚本的load事件实现顺序加载,确保依赖库加载完成后再执行后续脚本。
修改后的代码示例
var url = "S3 Pre-signed url"; fetch(url) .then(response => response.text()) .then(html => { // 解析HTML内容为DOM节点 const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); // 提取页面内容和所有脚本标签 const bodyContent = doc.body.innerHTML; const scripts = Array.from(doc.querySelectorAll('script')); // 先插入非脚本内容到页面 document.body.insertAdjacentHTML('beforeend', bodyContent); // 递归按顺序加载脚本 const loadNextScript = (index) => { if (index >= scripts.length) return; const originalScript = scripts[index]; const newScript = document.createElement('script'); // 复制原脚本的所有属性(如src、type等) for (const attr of originalScript.attributes) { newScript.setAttribute(attr.name, attr.value); } // 处理内联脚本内容 if (originalScript.textContent) { newScript.textContent = originalScript.textContent; } // 当前脚本加载完成后,加载下一个脚本 newScript.onload = () => loadNextScript(index + 1); newScript.onerror = () => loadNextScript(index + 1); // 忽略错误继续加载后续脚本 document.body.appendChild(newScript); }; // 启动脚本加载流程 loadNextScript(0); });
额外提示
- 如果原HTML中的脚本有明确的依赖优先级(比如jQuery必须最先加载),可以手动调整
scripts数组的顺序,确保依赖库排在最前面。 - 不要随意给依赖类脚本添加
async或defer属性,除非能确保依赖顺序不受影响。
内容的提问来源于stack exchange,提问作者chiku
相关产品推荐
相关产品推荐

