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

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

内容简介:小编推荐:

详解 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 属性)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Don't Make Me Think

Don't Make Me Think

Steve Krug / New Riders Press / 18 August, 2005 / $35.00

Five years and more than 100,000 copies after it was first published, it's hard to imagine anyone working in Web design who hasn't read Steve Krug's "instant classic" on Web usability, but people are ......一起来看看 《Don't Make Me Think》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具