使用JavaScript添加<script>和<link>元素有哪些优缺点?

Ric*_*uen 10 javascript css dom progressive-enhancement

最近我看到一些HTML只有一个<script>元素<head>...

<head>
    <title>Example</title>
    <script src="script.js" type="text/javascript"></script>
    <link href="plain.css" type="text/css" rel="stylesheet" />
</head>
Run Code Online (Sandbox Code Playgroud)

script.js然后添加任何其它必要的<script>元件和<link>使用元件到文档document.write(...):(或者它可以使用document.createElement(...)等)

document.write("<link href=\"javascript-enabled.css\" type=\"text/css\" rel=\"styleshet\" />");
document.write("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js\" type=\"text/javascript\"></script>");
document.write("<script src=\"https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js\" type=\"text/javascript\"></script>");
document.write("<link href=\"http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.0/themes/trontastic/jquery-ui.css\" type=\"text/css\" rel=\"stylesheet\" />")
document.write("<script src=\"validation.js\" type=\"text/css\"></script>")
Run Code Online (Sandbox Code Playgroud)

请注意,有一个plain.css在文档中的CSS文件<head>script.js只是增加任何和所有的CSS和JavaScript这将由支持JS的用户代理使用.

这项技术有哪些优点和缺点?

Ste*_*ung 12

document.write的阻塞性质

document.write将暂停浏览器在页面上工作的所有内容(包括解析).强烈建议避免因为这种阻塞行为.浏览器无法知道你在那个时候要对HTML文本流进行什么操作,或者写入是否会完全删除DOM树上的所有内容,所以它必须停止直到你完成.

从本质上讲,以这种方式加载scrips将迫使浏览器停止解析HTML.如果您的脚本是内联的,那么浏览器也会在它们继续之前执行这些脚本.因此,作为一个侧面说明,它总是建议你推迟加载脚本,直到您的网页进行解析,你已经证明合理的UI给用户.

如果您的脚本是从"src"属性中的单独文件加载的,那么脚本可能无法在所有浏览器中一致地执行.

失去浏览器速度优化和可预测性

这样,您就失去了现代浏览器所做的许多性能优化.此外,当您的脚本执行时可能无法预测.

例如,某些浏览器会在您"编写"它们之后立即执行脚本.在这种情况下,您将失去脚本的并行下载(因为浏览器在下载并执行第一个脚本标记之前不会看到第二个脚本标记).您将丢失脚本和样式表以及其他资源的并行下载(许多浏览器可以同时下载资源,样式表和脚本).

有些浏览器将脚本推迟到结束后执行它们.

当document.write正在进行时,浏览器无法继续解析HTML,并且在某些情况下,由于阻塞行为而正在执行脚本时document.write,因此您的页面显示得慢得多.

换句话说,您的网站刚刚变得像在几十年前没有优化的浏览器上加载一样慢.

为什么有人会这样做?

你可能想要使用这样的东西的原因通常是为了可维护性.例如,您可能拥有一个包含数千个页面的大型站点,每个页面都加载了相同的脚本和样式表集.但是,添加脚本文件时,您不希望编辑数千个HTML文件来添加脚本标记.这在加载JavaScript库(例如Dojo或jQuery)时尤其麻烦 - 在升级到下一个版本时必须更改每个HTML页面.

问题是JavaScript没有@include或@import语句供您包含其他文件.

一些解决方案

解决这个问题的方法可能不是通过注入脚本document.write,而是通过:

  1. 在样式表中使用@import指令
  2. 使用服务器脚本语言(例如PHP)来管理"母版页"并生成所有其他页面(但是,如果您不能使用它并且必须单独维护许多HTML页面,这不是解决方案)
  3. 避免document.write,但通过XHR加载JavaScript文件,然后eval()它们 - 这可能有安全问题
  4. 使用具有模块加载功能的JavaScript库(例如Dojo),以便您可以保留加载其他文件的主JS文件.您将无法避免必须更新库文件的版本号...


Nik*_*ani 8

一个主要缺点是浏览器不兼容.并非所有浏览器都正确地获取并将资源合并到DOM中,因此使用此方法存在风险.样式表比脚本更真实.

另一个问题是可维护性.连接和编写字符串以在客户端添加DOM元素可能会成为维护的噩梦.最好使用DOM方法(如createElement)来语义创建元素.

一个明显的优点是它使资源的有条件使用变得更加容易.您可以使用逻辑来确定要加载的资源,从而减少页面的带宽消耗和总体处理时间.我会使用一个库调用,如jQuery $ .getScript()来加载脚本与document.write.优点是这种方法更清晰,并且允许您在请求完成或失败时执行代码.


jor*_*aul 8

好吧,我也可以把这顶帽子放在戒指上......

如果你检查谷歌的闭包库base.js,你会看到document.write在他们的writeScriptTag_()函数中使用.这是"闭包"提供的依赖管理系统的一个重要部分,并且在创建复杂的,多文件的,基于库的javascript应用程序时是一个巨大的优势 - 它允许文件/代码先决条件确定加载顺序.我们目前使用这种技术,并没有遇到任何问题.TBH,我们没有一个浏览器兼容性问题,我们定期测试IE 6/7/8,FF3/2,Safari 4/5和Chrome最新版本.

到目前为止,我们唯一的缺点是,追踪因加载资源两次或根本无法加载资源而导致的问题可能具有挑战性.由于加载资源的行为是编程,它受规划失误,而不像直接添加标签的HTML,它可能很难看到确切的加载顺序是什么.但是,通过使用具有某种形式的依赖关系管理系统(如closure或dojo)的库,可以在很大程度上克服此问题.

编辑:我已经对这种性质做了一些评论,但我认为最好在我的回答中总结一下:dojo.require()和jQuery.getScript()都存在一些问题(两者都最终执行ajax请求和eval) .

  1. 通过ajax加载意味着没有交叉脚本 - 即没有加载不是来自您网站的JavaScript.如果您想要包含说明中列出的https://ajax.googleapis.com,这将是一个问题.
  2. Eval'd脚本不会出现在javascript调试器的页面脚本列表中,这使得调试非常具有挑战性.最近发布的firebug将显示eval'd代码,但文件名丢失使得设置断点的行为变得乏味.AFAIK,Webkit javascript控制台和IE8开发人员工具显示eval'd脚本.