三种异步加载js的方式

2,805 阅读3分钟

默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。

script标签中属性defer是“渲染完再执行”,async是“下载完就执行”

如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

type="module"

script标签type属性取值为“module”,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本。type="module"等同于defer属性,使用相同的执行队列,谁在前面谁先执行

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

//写法1
<script src="./test.js" type="module"></script>
//写法2
<script type="module">
    //code
</script>

路径

// 支持
import {foo} from 'https://jakearchibald.com/utils/bar.js';
import {foo} from '/utils/bar.js';
import {foo} from './bar.js';
import {foo} from '../bar.js';

// 不支持
import {foo} from 'bar.js';
import {foo} from 'utils/bar.js';

必须要/、./、../打头

普通情况

如下执行顺序,内联defer、1.js、module.js、defer.js、内联模块。

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

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

<script type="module">
    console.log('init module');
</script>
<script defer>
    //会忽略defer,当正常脚本
    console.log('defer');
</script>
<script src="./1.js"></script>

组合情况

async + module,脚本及其引入的模块加载完成后立即执行。

//firefox不支持此模式
<script async type="module">
  import {addTextToBody} from './utils.js';

  addTextToBody('Inline module executed.');
</script>

<script async type="module" src="1.js"></script>

与正常脚本相同,带有 async 属性的脚本在下载时不会阻塞 HTML parser,一旦加载完毕,立即执行。

不携带凭证信息

<!-- 请求脚本时会携带相关凭证 (如 cookie) -->
<script src="1.js"></script>

<!-- 不会携带相关凭证 -->
<script type="module" src="1.js"></script>

<!-- 会携带相关凭证 -->
<script type="module" crossorigin src="1.js?"></script>

<!-- 不会携带相关凭证 -->
<script type="module" crossorigin src="https://other-origin/1.js"></script>

<!-- 会携带相关凭证-->
<script type="module" crossorigin="use-credentials" src="https://other-origin/1.js?"></script

对于一个同源的模块脚本,可以为其添加 crossorigin 属性,这样在请求时就可以携带相关凭证了。如果你还想将凭证发给其他域,请使用 crossorigin="use-credentials"。需要注意的是,接收凭证的域必须返回 Access-Control-Allow-Credentials: true 的响应头。

各大浏览器的情况:

  • 请求同源模块时,Chrome 会携带凭证信息
  • 即使添加了 crossorigin 属性,Safari 在请求同源脚本时也不会携带凭证信息
  • Edge 则完全弄反了。请求同源模块时,Edge 默认会发送凭证信息,但如果手动添加了 crossorigin 属性,则又不会携带
  • Firefox 是唯一正确实现这一点的浏览器 —— 好样的!

参考地址如下:

浏览器中的 ES6 module 实现

ECMAScript modules in browsers

script 标签的加载是异步的吗?