温故而知新:js的异步加载和资源预加载

995 阅读2分钟

前言

🙋‍♂️ 知其然,更知其所以然,举一反三,融会贯通

浏览器默认情况下加载js脚本是同步的,必须等脚本完全加载完成,才会继续后续的网页渲染,如果js文件较大,并且网络慢,那么就会对整个网页后续的加载造成阻塞,对于这种情况我们可以把脚本放到网页底部加载执行。

但是有些情况我们想提前或同时加载脚本,又不会对网页的渲染造成阻塞,那么就可以用到js异步加载和预加载的方案。

异步加载

动态加载脚本

通过创建script标签,添加属性动态引入js脚本

 <script>
    const asyncScript = document.createElement('script')
    asyncScript.src = './a.js'
    asyncScript.type = 'text/javascript'
    document.getElementsByTagName('head')[0].append(asyncScript)
  </script>

async-异步加载

添加async属性,js会异步加载script标签,等加载完成后立即执行,那么js的执行会中断界面的渲染,直到脚本执行完成后继续渲染,由于每个脚本加载的速度是不一样的,那么存在多个async脚本之间无法确保执行顺序,如果脚本间有依赖关系的话就可能存在问题。

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

defer-延迟加载

添加defer属性,js会异步加载script标签,脚本会等待整个页面加载完成后才去执行(这里的加载完包括整个页面的渲染和其他脚本的执行), 如果存在多个defer,那么会按照脚本引入的顺序依次执行。

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

ES6模块化

目前主流的浏览器都已经支持ES模块化了,可以直接通过type="module"设置,浏览器默认情况下会对其进行与defer属性一样的方式处理。

外联脚本

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

内联脚本

<script type="module">
    import moduleA from './a.js'
    console.log(moduleA)
</script>

预加载

preload和prefetch都是预加载网页资源,加载完成并不立即执行,而是在真正用到的时候才执行,但是它们使用场景不一样。

preload

preload是提前预加载当前页面所需要的资源(提高加载的优先级),而不用等到html文档解析标签的时候再去加载,这一机制使得资源可以更早的得到加载并可用,不会阻塞界面的初步渲染,进行提升界面性能。

在界面中使用link标签预加载资源,onload事件后解析执行

<link ref="preload" href="my.css" as="style" onload="this.rel='stylesheet'">
<link ref="preload" href="my.js" as="script" onload="const script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);">>

prefetch

perfetch是提前预加载其他界面所用到的资源,并且是在浏览器空闲的时候进行加载,等后续界面使用资源的时候直接从缓存读取(disk cache), 提高界面的加载速度

<link prefetch href="my.png">
<link prefetch href="mycss.css">
<link prefetch href="myjs.js">

总结:preload预加载的资源一定会被用到,prefetch预加载的资源可能会被用到(其他界面使用)