详解 script 标签(async,defer,integrity,crossorigin 和 onerror 属性)

栏目: JavaScript · 发布时间: 6年前

内容简介:小编推荐:

详解 script 标签(async,defer,integrity,crossorigin 和 onerror 属性)

详解 script 标签(async,defer,integrity,crossorigin 和 onerror 属性)

小编推荐: 掘金是一个面向 程序员 的高质量技术社区,从 一线大厂经验分享到前端开发最佳实践,无论是入门还是进阶,来掘金你不会错过前端开发的任何一个技术干货。

script 标签用来在 HTML 文档中嵌入或引用可执行的脚本,当浏览器解析 HTML ,碰到 script 标签的时候,根据其不同的属性,有着不同的行为:

不带 async 或 defer 属性

<script src="script.js"></script>

没有 deferasync 属性时,浏览器会立即加载并执行指定的脚本。浏览器解析 HTML 文档,解析到没有 deferasync 属性的 script 标签时,会阻塞 HTML 文档解析,并按照它们出现的顺序执行。例如:

<script src="a.js"></script>
<script>
  console.log('b')
</script>
<script src="b.js"></script>

浏览器在解析到上面的 script 标签时会阻止页面解析,并平行下载 a.js , b.js ,依次执行 a.js , console.log('b') , b.js 后,再继续解析渲染后续的页面。这也是为什么你会经常看到一些建议,将需要 DOM 操作的 js 要放在 body 标签的最后,原因就是不阻塞 HTML 文档的解析,使页面尽可能快的在浏览器上渲染呈现。

带 async 属性

<script async src="script.js"></script>

asyncscript 标签,加载和渲染后续文档元素的过程将和脚本的加载并行进行(异步),脚本的加载完成后就马上执行,脚本执行时会阻塞 HTML 解析。

async

兼容性: async attribute for external scripts

带 defer 属性

<script defer src="script.js"></script>

deferscript 标签,加载后续文档元素的过程将和脚本的加载并行进行(异步),和 async 属性不同的是,脚本的执行要在所有元素解析完成之后, DOMContentLoaded 事件触发之前完成。

defer
DOMContentLoaded

兼容性: defer attribute for external scripts

async 和 defer 属性的异同

  • deferasync 都是并行加载的,主要区别在于下载后何时执行。
  • 每一个 async 属性的脚本都在它下载结束之后立刻执行,所以就有可能出现脚本执行顺序被打乱的情况
  • 每一个 defer 属性的脚本会在 HTML 解析完成后, DOMContentLoaded 之前,按照 DOM 中的顺序执行(ie>=10)
  • deferasync 都只适用于外部脚本文件,对与内联的 script 标签是不起作用

type 为 module 的 script 标签

相比传统 script<script type="module"></script> 将被当作一个 JavaScript 模块对待,被称为 module script ,且不受 charsetdefer 属性影响。

// app.js
import { assign } from "./utils.js"

var obj = Object.create(
  { foo: 1 }, 
  {
    bar: { value: 2 },
    baz: { value: 3, enumerable: true }
  },
)
var copy = assign({}, obj)
console.log(copy)
<script type="module" src="app.js"></script>
<script nomodule src="classic-app-bundle.js"></script>

上面的代码,可以这么理解:

  • 支持 module script 的浏览器,不会执行拥有 nomodule 属性的 script
  • 不支持 module script 的浏览器,会忽略未知的 type="module"script ,同时也会忽略传统 script 中不认识的 nomodule 属性,进而执行传统的 bundle.js 代码
  • module script 以及其依赖所有文件(源文件中通过 import 声明导入的文件)都会被下载,一旦整个依赖的模块树都被导入,页面文档也完成解析, app.js 将会被执行
  • 但是如果 module script 里有 async 属性,比如 <script type="module" src="util.js" async></script>module script 及其所有依赖都会异步下载,待整个依赖的模块树都被导入时会立即执行,而此时页面有可能还没有完成解析渲染。

兼容性: JavaScript modules via script tag

图示说明

一张图片胜过千言万语,可以更好的说明传统 scriptmodule script 下载,执行的情况:

详解 script 标签(async,defer,integrity,crossorigin 和 onerror 属性)

查看大图

理解说明:

  • 使用 <script> ,脚本下载和执行会阻塞 HTML 文档的解析。
  • 使用 <script defer> ,脚本下载与 HTML 解析并行,等 HTML 解析完成后没脚本都会有序执行。
  • 使用 <script async> ,脚本下载与 HTML 解析并行,但一旦脚本加载完成,就会中断 HTML 解析,同时执行脚本。
  • <script type ="module"> 的行为类似于 <script defer> ,但是模块依赖关系也会被下载,
  • <script type ="module" async> 的行为类似于 <script async> ,额外的模块依赖关系也会被下载。

script 标签的 integrity 属性

<script crossorigin="anonymous" integrity="sha256-PJJrxrJLzT6CCz1jDfQXTRWOO9zmemDQbmLtSlFQluc=" src="https://assets-cdn.github.com/assets/frameworks-3c926bc6b24bcd3e820b3d630df4174d158e3bdce67a60d06e62ed4a515096e7.js"></script>

integrity 属性是资源完整性规范的一部分,它允许你为 script 提供一个 hash ,用来进行验签,检验加载的JavaScript 文件是否完整。

上面的代码来自github源码, integrity="sha256-PJJrxrJLzT6CCz1jDfQXTRWOO9zmemDQbmLtSlFQluc=" 告诉浏览器,使用sha256签名算法对下载的js文件进行计算,并与intergrity提供的摘要签名对比,如果二者不一致,就不会执行这个资源。

intergrity 的作用有:

  • 减少由【托管在CDN的资源被篡改】而引入的XSS 风险
  • 减少通信过程资源被篡改而引入的XSS风险(同时使用https会更保险)
  • 可以通过一些技术手段,不执行有脏数据的CDN资源,同时去源站下载对应资源

注意:启用 SRI 策略后,浏览器会对资源进行 CORS 校验,这就要求被请求的资源必须同域,或者配置了 Access-Control-Allow-Origin 响应头

5.script 标签的 crossorigin 属性

crossorigin 的属性值可以是 anonymoususe-credentials ,如果没有属性值或者非法属性值,会被浏览器默认做 anonymous

crossorigin 的作用有三个:

  • crossorigin 会让浏览器启用CORS访问检查,检查http相应头的 Access-Control-Allow-Origin
  • 对于传统 script 需要跨域获取的js资源,控制暴露出其报错的详细信息
  • 对于 module script ,控制用于跨域请求的凭据模式

我们在收集错误日志的时候,通常会在window上注册一个方法来监测所有代码抛出的异常:

window.addEventListener('error', function(msg, url, lineno, colno, error) {
  var string = msg.toLowerCase()
  var substring = "script error"
  if (string.indexOf(substring) > -1){
    alert('Script Error: See Browser Console for Detail')
  } else {
    var message = {
      Message: msg,
      URL:  url,
      Line: lineNo,
      Column: columnNo,
      'Error object': JSON.stringify(error)
    }
    // send error log to server
    record(message)
  }
  return false
})

但是对于跨域js来说,只会给出很少的报错信息: 'error: script error' ,通过使用 crossorigin 属性可以使跨域js暴露出跟同域js同样的报错信息。但是,资源服务器必须返回一个 Access-Control-Allow-Origin 的header,否则资源无法访问。

动态导入script(Dynamically importing scripts)

function loadError (error) {
  throw new URIError(`The script ${error.target.src}  is not accessible.`)
}

function importScript (src, onLoad) {
  var script = document.createElement('script')
  script.onerror = loadError
  script.async = false
  if (onLoad) { script.onload = onLoad }
  document.header.appendChild(script)
  script.src = src
}

可以上面的方法动态加载js资源,但是要注意的是,默认 append 到文档中的 script 会异步执行(可以理解为默认拥有 async 属性,如果需要加载的js按顺序执行,需要设置 asyncfalse

script 标签的 onerror

JavaScript运行时的错误(抛出的语法错误和异常)发生时,实现了ErrorEvent接口的error事件在window上触发,并且调用window.onerror(或者window.addEventListener(‘error, cb))的回调函数

当资源(如 <img><script> )无法加载,或者启用SRI策略资源不完整时,使用Event接口的error事件在会在该资源元素处触发,元素上的onerror回调函数被调用

script 标签与 innerHTML

通过 innerHTML 动态添加到页面上的 script 标签则不会被执行

参考阅读

如果你觉得本文对你有帮助,那就请分享给更多的朋友

关注「前端干货精选」加星星,每天都能获取前端干货

详解 script 标签(async,defer,integrity,crossorigin 和 onerror 属性)

以上所述就是小编给大家介绍的《详解 script 标签(async,defer,integrity,crossorigin 和 onerror 属性)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

超越门户

超越门户

吴晨光 / 中国人民大学出版社 / 2015-4-17 / 39.80

在这个PC端影响力下降、人们对手机的依赖与日俱增的时代,这种探索的意义非同寻常,可以说是试图树立新媒体时代的行业标准。 ——陈彤(小米内容投资与运营副总裁、新浪网前总编辑、资深网络媒体人) 我将对此书的阅读,视作对往日岁月的怀念,它提醒我,自己曾 投身于多么富有蓬勃朝气和探索精神的事业。而对这种事业的原则、逻辑和方法的继承和继续学习,对于互联网时代的企业形象塑造 ,同样有融会变通的参考......一起来看看 《超越门户》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码