通过js高级程序设计学js 二、在HTML中使用JavaScript

137 阅读3分钟

<script> 元素

提到JavaScript,就不得不涉及到web的核心语言--HTML。在当初开发JavaScript的时候,网景要解决的一个重要问题就是如何让JavaScript既能与HTML页面共存,又不影响那些页面在其他浏览器的展示效果。经过尝试和讨论,最终决定为web增加统一脚本支持。web诞生早期的很多做法都保留了下来,并被正式纳入HTML规范当中。

向HTML页面插入JavaScript的重要方法,就是使用 <script>元素。这个元素由网景创造,并在Netscape Navigator2中首先实现。后来这个元素被加入到HTML规范中,HTML4.01为script元素定义了8个属性:

  1. async:可选。表示应该立即下载脚本,但不应妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本,仅对外部脚本文件有效;
  2. charset:可选。表示通过src属性指定的代码的字符集。由于大多数浏览器会忽略它的值,这个属性很少有人用;
  3. defor:可选。表示脚本可以延迟到文档被完全解析和显示之后执行。对外部脚本文件有效。IE7及更早版本对嵌入脚本同样有效;
  4. language:已废弃;
  5. src:可选。表示要执行代码的外部文件;
  6. type:可选。可以看成是language的代替属性;属性值默认为 text/javascript;
  7. crossorigin:可选。配置相关请求的CORS(跨源资源共享)设置。默认不使用CORS。crossorigin="anonymous"。配置文件请求不必设置凭据标志。crossorigin="use-credentials"。设置凭据标志,意味着出站请求会包含凭据。
  8. integrity:可选。允许比对接收到的资源和指定的加密签名以验证子资源完整性。如果接收到的资源的签名和指定的签名不匹配,则页面会报错,脚本不会执行。这个属性可以确保内容分发网络(CDN)不会提供恶意内容。

使用script元素的方法有两种:一是直接在页面中嵌入,二是引入外部js文件。 一般script元素嵌入时只需指定type属性,值为text/javascript,也可以不指。 在使用<script>嵌入JavaScript代码时,记住不要在任何位置出现 "</script>"字符串。例如:

<script type="text/javascript">
    function hi() {
      alert("</script>");
    }
</script>

按照解析嵌入式代码规则,当浏览器遇到字符串"</script>"时,就会认为那是<script>的结束标签。可以通过转义符 "\" 解决这个问题。例如:

<script type="text/javascript">
    function hi() {
      alert("<\/script>");
    }
</script>

如果通过<script>元素来包含外部js文件,那么src属性就成为必需了。这个属性期望传入一个指向要引入文件的地址,例如:<script type="text/javascript" src="./hello.js"></script>;需要注意的是带有src属性的<script>元素不应在<script>和</script>之间编写额外的代码,会被浏览器忽略。

嵌入式的<script>和外部引入的脚本都会阻塞页面,在js代码被计算完之前,页面其余内容不会被加载,也不会被显示;

<script>元素一个最为强大、同时也备受争议的特性是,它可以包含来自外部域的JavaScript文件。<script>的src属性值可以是一个完整的URL。而这个URL指向的资源可以跟包含它的HTML页面不是同一个域,比如:

<script src="https://cdn.jsdelivr.net/npm/bootstrap/dist/js/bootstrap.bundle.min.js"></script>

浏览器解析这个资源时,会向src属性指定的路径发送一个GET请求,取得相应的资源,如果获取到的是一个JavaScript,那么这个请求不受浏览器同源策略的限制,但是返回并被执行的JavaScript则受限,当然这个请求依然受父页面的HTTP或HTTPS的协议的限制。

也正因为这样,我们在获取外部JavaScript的时候一定要格外的小心,要确保获取到的JavaScript是安全的,或者来自于一个可信的域。当然<script>元素的integrity属性是防范这种问题的一个武器,但是这个属性不是所有浏览器都支持。

浏览器会按照<script>元素的顺序自上而下,依次执行,前提是<script>元素没有defer和async属性。

标签的位置

以前所有<script>元素都会放在<head>标签内,如下:

<!DOCTYPE html>
<html lang="en">
    <head>
      <title>Document</title>
      <style></style>
      <script src="./h1.js"></script>
      <script src="./h2.js"></script>
    </head>
    <body></body>
</html>

这种做法是为了把所有外部css和JavaScript文件集中在一起,但这样做的也就意味着必须把所有的JavaScript代码都下载解析完成之后,才能开始渲染页面,这样会造成一个长时间的页面白屏的效果,降低了用户体验。为解决这个问题现代浏览器都会把<script>元素的引用放在<body>元素后面,如下:

<!DOCTYPE html>
<html lang="en">
    <head>
      <title>Document</title>
      <style></style>
    </head>
    <body>
        <---内容区域--->
        <script src="./h1.js"></script>
        <script src="./h2.js"></script>
    </body>
</html>

这样一来,页面会在渲染完成之后开始下载解析JavaScript,用户会感觉页面加载速度变快了。

推迟/异步执行脚本

defer和async这两个属性出现率很低,web开发实践中根本就不推荐使用。如果想了解可以看下书,没书的话有人看的话,我后期补上。

动态加载脚本

除了<script>标签外,还有其他方式可以加载脚本,通过DOM API向HTML中动态的插入<script>元素。如下:

let script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/bootstrap.js;
document.head.appendChild(script);

用这种方式创建的<script>元素默认是异步加载的,相当于添加了async属性,这样会造成一个问题,不是所有浏览器都支持这个属性,如果要统一动态脚本的加载行为,需要手动设置async属性,如下:

let script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/bootstrap.js;
script.async = false;
document.head.appendChild(script);

这种方式获取的资源可能有严重的性能问题,需要显式的告知预加载器这些资源的存在,可以在文档头部声明它们。如下:

<link rel='perload' href='https://cdn.jsdelivr.net/npm/bootstrap.js'>

XHTML中的变化

XHTML已经退出历史舞台,需要了解的如上推迟/异步执行脚本

行内代码与外部文件

虽然可以在HTML中直接嵌入JavaScript代码,但通过实践,最佳的方法是将JavaScript代码放在独立的外部文件中。这个不是强制的,推荐理由如下:

  1. 可维护性:如果JavaScript代码分散在很多HTML页面中,会导致难以维护的情况,而放在一个单独的目录中,开发者就可以独立于使用它们的HTML页面进行编辑。
  2. 缓存:浏览器会根据特定的设置缓存所有外部的JavaScript文件,这意味着如果有两个页面使用了同一份JavaScript文件,这个文件只会下载一次,让页面加载的更快。

在配置浏览器请求外部文件时,要着重考虑它所占用的带宽,在SPDY/HTTP2中,预请求的消耗显著降低,以轻量、独立的JavaScript组件形式向客户端送达脚本更具有优势。

<noscript>元素

针对早期浏览器不支持JavaScript的情况,需要一个优雅降级的处理方案,<noscript>被用于那些不支持JavaScript的浏览器提供代替内容,虽然现如今的浏览器已经100%支持了JavaScript,但对于一些禁用JavaScript的浏览器来说仍然有它的用处。

<noscript>元素可以包含任何可以出现在<body>中的HTML元素,<script>除外,在两种情况下浏览器会显示<noscript>中的内容:

  1. 不支持JavaScript的浏览器;
  2. 禁用了JavaScript的浏览器; 任意一个条件被满足都会渲染<noscript>元素中的内容,否则,不显示。
<!DOCTYPE html>
<html lang="en">
    <head>
      <title>Document</title>
      <style></style>
      <script src="./h1.js"></script>
      <script src="./h2.js"></script>
    </head>
    <body>
        <noscript>
            您的浏览器不支持JavaScript,请前往 xxx 下载现代浏览器
        </noscript>
    </body>
</html>

这样可以告知用户,并提出解决方案。反之,如果浏览器支持JavaScript脚本,那么用户永远不会看到这句话。

小结

JavaScript是通过<script>元素插入到HTML页面中的,这个元素可用于把JavaScript代码嵌入到页面中,跟其他标记混合在一起,也可用于引入外部文件中的JavaScript代码。重点总结如下:

  1. 要引入外部JavaScript文件,<script>元素要声明src属性并且填入准确的URL,文件可以和页面存在同一台服务器上,也可以是一个不同的域。
  2. 所有<script>元素会严格遵循自上而下依次执行,除给<script>元素设置了async或defer外。
  3. 对不推迟执行的<script>元素,浏览器必需解释完<script>中的代码后才能继续渲染页面的剩余部分,所以我们应该将<script>元素放到页面末尾,介于主内容之后及</body>标签之前。
  4. 可以使用defer属性把脚本推迟到页面渲染完成之后执行,推迟的脚本原则上按照它们自上而下的次序执行。
  5. 可以使用async属性表示脚本不需要等待其他脚本,也不阻塞文档渲染,即异步加载,异步加载的脚本不能保证按照它们在页面上的次序执行。
  6. 通过<noscript>元素,可以指定当浏览器在不支持JavaScript或禁用了JavaScript时显示的内容。