vlambda博客
学习文章列表

HTML中的JavaScript(附思维导图)

前言

JavaScript的来源,最初就是在HTML页面中处理输入验证。那么作为一门独立的语言,它怎么在HTML中使用呢?本篇就在介绍它在HTML中的使用方式:<script>标签


思维导图

本章重点

本章的主要考察点是<script>标签的属性及其执行顺序。

1.<script>属性

本章介绍的<script>属性有8个,其中两个已经近乎或已经废弃(charset 和 language),所以真正需要了解的就只有6个属性:async, defer, crossorigin, integrity, src, type。前两个会在执行顺序中详细讨论,这里就只看后面四个。

crossorigin

crossorigin:可选。配置相关请求的 CORS(跨源资源共享)设置。默认不使用CORS。crossorigin="anonymous"配置文件请求不必设置凭据标志。crossorigin="use-credentials"设置凭据标志,意味着出站请求会包含凭据。

想要理解 crossorigin,首先需要理解 CORS。关于CORS的详细解释,可以看这一篇文章:15 张精美动图全面讲解 CORS ,用动图的方式解释的非常清楚。

看完了文章,你肯定会知道放宽(或者说配置)跨域请求的关键是在请求中带上 origin 字段,而 crossorigin 就是做这件事情的。只要有 crossorigin 属性,就会在请求资源时自动带上origin头。然后请求会要求服务器进行 CORS 校验,跨域的时候如果 response header 没有 'Access-Control-Allow-Origin' 是不会拿到资源的。

而 crossorigin 中两个属性值的区别就在于会不会设置凭据标志,一般就是指 cookie 。如果crossorigin="use-credentials",那么在跨域时就会带上 cookie。

integrity

integrity:可选。允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,Subresource Integrity)。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错,脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容。

书本中的解释非常清楚,这里主要解释一下加密签名。这里加密签名的格式是 加密算法 - hash 值,如引入 vue:

<script integrity="sha384-0k9//QJdpmfSdp5IK3oJjOYPfz42f2FE0goMLtK9Vq7aKllvc4Lnz7lHPHiFhvDP" > </script>

这里的 sha384 是加密算法名,后面则是其 hash 值。如果 hash 值与 CDN 返回的文档的 hash 值不同,就说明 CDN 可能被入侵了,文件被篡改了,于是就不会再运行它。

src

这个属性大家日常都会用到,也没有什么特殊的东西。简单提醒一下:如果指定了 src 属性的值,那么这个 script 标签就是外部文件,行内所写的js代码会被忽略。

type

type也很简单,type值一般只有两种情况:一是默认的普通的 text/javascript ,二是表示 ES6 的模块的 module,只有这时候代码中才能出现 import 和 export 关键字。


2. 执行顺序

如果页面中有多个 script 标签,会按照顺序从上到下执行。但是 script 的执行顺序还可以受到其他情况的影响,书中具体介绍了三种情况:

  1. defer

  2. async

  3. 动态写入(如document.createElement('script')

defer

defer 表示立即下载脚本,但是会被延迟到整个页面都解析完毕之后再运行。

有一种常用的性能优化减少等待HTML渲染的时间(白屏时间)的做法是:将js脚本放在 body 标签的最后面,等到HTML顺序解析到最后的时候才会下载js脚本。这种方法和 defer 的区别是什么呢?我个人认为有两个原因:

一是语义上的问题,script 标签常常引用外部文件,应该和 link 等其他外部文件,或者说表示元数据的内容一起放在 header 里面,这样也能避免开发者没有看到文档最后的js脚本。

二是兼容性的问题,参见MDN文档, IE10 以上才支持 defer ,所以考虑到 IE8 的情况,可能还是要使用放在末尾的方式。

async

async 表示立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或等待其他脚本加载。

从上图可以看出:如果脚本下载完成之后HTML页面并没有解析完成,浏览器会暂停解析,先让js脚本执行。而如果HTML解析完成了,那就相当于无事发生,和 defer 的行为类似。

它和 defer 一样都会告诉浏览器立即开始下载但是推迟执行,不过与 defer 不同的是:标记为async的脚本并不能保证按他们出现的顺序执行。所以设置为 async 的脚本不能有依赖关系,否则可能会出错。

给脚本添加 async 属性的目的是告诉浏览器,不必等脚本下载和执行完后再加载页面,同样也不必等到该异步脚本下载和执行后再加载其他脚本。正因为如此,异步脚本不应该在加载期间修改 DOM,如不能使用 document.write

动态写入

由于js可以使用 DOM API 动态添加 script 标签,所以实际上我们可以动态的写入一个脚本。

当然,在把这个动态写入的 script 标签元素添加到 DOM 且执行到这段代码之前不会发送请求。所以默认情况下,以这种方式创建的 script 元素是以异步方式加载的,相当于添加了 async 属性。


3.性能优化相关

常见的性能优化的方式除了上文提到的:将js脚本放在 body 标签的最后面。本章还介绍了使用外部文件,尤其是小的组件化的js脚本文件这种优化方法。

使用外部文件的原因大家都能想到:浏览器会根据特定的设置缓存所有外部链接的 JavaScript 文件,这意味着如果两个页面都用到同一个文件,则该文件只需下载一次。这最终意味着页面加载更快。

而使用小的组件化文件的原因则和 SPDY/HTTP2 有关。在初次请求时,如果浏览器支持 SPDY/HTTP2,就可以从同一个地方取得一批文件,并将它们逐个放到浏览器缓存中。从浏览器角度看,通过 SPDY/HTTP2 获取所有这些独立的资源与获取一个大JavaScript 文件的延迟差不多。但在第二个页面请求时,由于你已经把应用程序切割成了轻量可缓存的文件,第二个页面也依赖的某些组件此时已经存在于浏览器缓存中了。这样就实现了页面加载速度的加快。

总结

本章主要介绍了 HTML 中使用 JavaScript 的方法:script 标签。

本章的内容在面试中一般会结合网络相关内容来考察:比如 crossorigin 和跨域有关;async/defer 与异步网络请求有关;性能优化和浏览器缓存甚至 HTTP2 的特性有关。前端是一个天天和网络打交道的行业,有时间还是要系统的学习计算机网络相关内容~