序言
开启本章前,先回顾一下第一章节:介绍 JavaScript 作为一门用来与网页交互的脚本语言,自身通过 ECMAScript、BOM 和 DOM 三者来实现。我觉得第二章就开始一点点撩开 JavaScript 的“面纱”了,讨论的是将 JavaScript 引入 HTML 所形成的 HTML 规范,主要包括 <script>
和 <noscript>
两种元素。本章有一些值得注意的地方以及面试可能遇到的点。话不多说,开始。
MIND
<script>
元素
将 JavaScript 插入 HTML 的主要方法是使用
<script>
元素
嵌入方式
首先看看这个元素是如何将 JavaScript 插入 HTML 的,和 CSS 类似,JavaScript 可通过行内代码和外部文件嵌入到 HTML中。
- 行内代码
这里的<script type="text/javascript"> <!-- some code --> </script>
type
是可选的,代替退出舞台的language
,指代脚本语言的内容类型,一般值都是text/javascript
。 - 外部文件
这里的<script src="index.js"></script>
src
同样也是可选的(外部文件必须),用来表示要执行的代码的外部文件。
通常推荐以外部文件嵌入的方式,该方式有以下推荐理由:
- 可维护性:代码汇集相比于分散,更容易维护,并独立于 HTML 文件。
- 缓存:浏览器会缓存外部嵌入的 JavaScript 文件,无需重复下载,加载更迅速
- 适应未来:避免 XHTML 对行内代码的影响(XHTML 退出历史舞台)
标签位置
了解了嵌入方式,再看看这个元素在文件的哪里起作用。<script>
标签的放置也是有很大学问的,现在和过去的做法有很大区别:过去是将元素放在页面的 <head>
标签内,现在通常将其放到 <body>
元素中的页面内容后面。
<head>
标签内<head> <title>async</title> <script src="index.js"></script> </head>
<body>
元素中的页面内容后面<body> <script src="index.js"></script> </body>
为什么需要更改位置呢?因为以往的方式会导致白屏现象(面试考点)。这一点与浏览器解析规则相关:
- 页面在浏览器解析到
<body>
的起始标签才开始渲染 - 浏览器解析到 JavaScript 时会阻碍文档的解析
以往的放置方式中,JavaScript 位于 <body>
的起始标签前,阻碍了文档渲染,期间浏览器窗口会完全空白。正因如此,现代的标签位置很好的解决了这个问题。
执行方式
标签都放好了,怎么执行 JavaScript 呢?一般情况下,JavaScript 加载后会马上执行,可能会阻塞文档的解析。不过可以通过设置,改善或者说解决这种问题,那就是 <script>
的两个属性 defer
和 async
(面试考点)。
defer
defer 表示脚本在执行的时候不会改变页面的结构。
<head>
<title>defer</title>
<script defer src="index.js"></script>
</head>
听起来可能有点晦涩,不过我们要理解的就是,外部脚本文件添加这个属性后, JavaScript 会与文档同步解析,但不会马上执行,而是等到整个页面解析完之后再执行。并且一般情况下有两个特点:
- JavaScript 还是依次执行
- JavaScript 会在
DOMContentLoaded
事件之前执行
注意,以上特点是一般情况下,会有部分非主流浏览器忽略这个特定,按照通常做法处理 JavaScript,因此,还是将 <script>
放在在页面底部为好。
async
async 告知浏览器不必等脚本下载和执行完毕后再加载页面,同样不必等到该异步脚本下载和执行后再加载其他脚本。
<head>
<title>async</title>
<script async src="index.js"></script>
</head>
简单来说, async
也是保证脚本与文档同步加载,但会立即执行,可能阻塞文档的解析。同样两个与 defer
不同的特性:
- JavaScript 执行不保证次序
- JavaScript 会在页面 load 事件前执行,但不保证在
DOMContentLoaded
前后
动态加载
<script>
元素不仅能静态写入,同样可以动态生成:使用 DOM API 生成 script
元素同样可以加载脚本。
let script = document.createElement('script');
script.src = 'index.js';
document.head.appendChild(script);
这种方式生成的 <script>
元素是默认添加 async 的,当然可以修改这个设置:
...
script.async = false;
document.head.appendChild(script);
但这种动态添加且默认 async
的方式,会严重影响 JavaScript 的执行顺序,建议使用 <link>
提前显示声明
<link rel="preload" href="index.js">
JSONP 跨域
这一点是基于动态加载的,是一个面试考点,便私自加上了。
<script>
元素在解析资源时,会向 src 属性执行的路径发送一个 GET 请求。这个初始的请求不受浏览器同源策略限制。
根据上面条件,我们可以通过动态生成 script
元素,给 src 源发跨域的 GET 请求,这样的方式称为 JSONP 跨域。其实现方式暂时就不细说了,细节网上比比皆是,如传送门。
noscript
其实这个元素很少用到,是用来给不支持 JavaScript 浏览器提供替代内容,现在仅用于禁用 JavaScript 的浏览器。这个元素在浏览器不支持脚本或脚本支持关闭情况下,会显示出 <script>
以外的 HTML 元素,使用如下:
<body>
<noscript>
<span>浏览器不支持脚本或脚本支持关闭</span>
</noscript>
</body>
总结
第二章主要讲的是 <script>
元素,包括:
- 嵌入方式:行内代码和外部文件,推荐使用外部文件
- 标签位置:
<head>
中和</body>
前,推荐第二种 - 执行方式:
defer
(推迟) 和async
(同步) - 动态加载:动态生成
<script>
元素,可用于 JSONP 跨域 还有一个浏览器不支持或禁止脚本情况下的显示 HTML 内容。
整章的 <script>
元素相关需要掌握,下一章是 JavaScript 语言基础。