script标签

200 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情

作用

该元素最大的作用其实就是在HTML中书写js代码。

任何定义在script元素中的函数拥有一个跨当前文档的全局范围。

js代码可以直接作为该标签的内容,而这部分代码也被称为嵌入式脚本。

也可以使用该标签的src属性指定一个外部的js文件【属性值为URL】。此时不应该有嵌入式脚本存在,否则会被忽略。

默认阻塞执行

不带任何属性或是仅仅指定type、src等不影响运行和下载时机的属性的该类标签,在加载、编译、执行该段代码时,会阻塞其他任务。

这和浏览器解析其他普通标签是一样的,阻塞执行。

也就是说HTML照常解析渲染,在碰到<script>标签时,停止解析,浏览器开始下载脚本,并执行,执行完毕后才会继续解析渲染。

image.png

默认跨域

该标签对于src要下载的内容是默认允许进行跨域下载的,也就是说没有跨域问题。

所以jsonp就是利用了该特性,可以通过script标签,在前端解决跨域问题。

但是内嵌的代码,或是src指定的那个文件内部的代码是不具备跨域功能的。

属性

src

用于指定外部脚本的url。当指定该属性时,该元素不应该内嵌脚本.

async

使得外部脚本在下载过程中是和其他任务并行进行的。在下载完成之后,会正常阻塞执行下载的代码。

对于模块脚本,如果存在 async 属性,那么脚本及其所有依赖都会在延缓队列中执行,因此它们会被并行请求,并尽快解析和执行。

image.png

  • 使用该属性的脚本中不能调用 document.write 方法
  • 有脚本的 onload 的事件回调
  • 会在window的load事件执行之前执行完毕
  • 由于各个脚本的加载时间不同,所以最终执行脚本的顺序并不是根据脚本在文档中的顺序执行的。

该属性能够消除Javascript的解析阻塞 。解析阻塞的 Javascript 会导致浏览器必须加载并且执行脚本,之后才能继续解析。

这里说的消除解析阻塞并不是说中断正在执行的js脚本去执行该脚本。而是浏览器在解析该脚本节点时,会和正在下载的资源并行下载,等下载完该脚本节点对应的资源并执行。这样就避免其他文件资源下载太慢,而导致js文件全部无法执行。

defer

只对外部脚本有用,且模块脚本【type="module"的脚本】默认带该属性

当文档完成解析后,触发 DOMContentLoaded 事件前执行被该属性标记的脚本。

有 defer 属性的脚本会阻止 DOMContentLoaded 事件,直到脚本被加载并且解析完成。

  • 使用该属性的脚本中不能调用 document.write 方法
  • 有脚本的 onload 的事件回调

image.png

crossorigin

作用

script在请求src指定的资源时,请求头是不带origin头的,所以说可以跨域请求资源。但是内部代码的请求是不允许跨域的。

脚本内部请求跨域报错后,该错误可以被windowerror事件所捕获到,但是仅仅只能捕获到最简单的、没有用处的报错,只知道是跨域问题,这是浏览器的安全策略。

但是脚本标签使用了该属性之后,请求头就会带origin,src的请求也会从原来的无跨域限制,变成了有跨域限制。且内部代码的跨域错误可以使用window.onerror获取到详细信息了。

总体来说该属性就是用于进行跨域设置的,但凡是支持了该属性的元素的功能都差不多。

属性值

具体请看 上面的 《HTML元素的crossorigin属性》

type

该属性定义 script 元素包含或src引用的脚本语言。属性的值为 MIME 类型。如果没有定义这个属性,脚本会被视作 JavaScript。如果不是 JavaScript 类型,则该元素所包含的内容会被当作数据块而不会被浏览器执行。

常用的JSMIME类型基本就是:text/javascripttext/ecmascriptapplication/javascriptapplication/ecmascript以及module

其中module最为特殊,是用于模块导出和引用的,代码会被当作js模块。属于ES6标准的语法。

integrity

允许对比接收到的资源的哈希值和指定的哈希值以验证子资源完整性。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错,脚本不会执行。

这个属性可以用于确保内容分发网络【CDN】不会提供或是被篡改为恶意的内容。

nomodule

该属性为布尔属性,被标记的脚本会在不支持ES6模块语法的浏览器中执行,不会在支持该语法的浏览器中执行,主要是用来做兼容处理。

COOKBOOK

推荐做法:尽可能优先用async,之后defer,最后无属性。

  • 若脚本是模块化脚本,且不依赖其他脚本资源【不导入其他文件的资源,仅仅导出自己的资源】时,用async
  • 若脚本依赖于其他脚本【导入使用】或是被其他脚本依赖【导出】,那就使用defer
  • 如果脚本很小,并且async脚本依赖于它,那么使用一个async内联脚本

句末语

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情