关于document.write()加载JS等静态资源 和 异步async加载JS

时间:2023-03-09 03:03:26
关于document.write()加载JS等静态资源 和 异步async加载JS

现流行浏览器对于静态资源的预加载

传统的浏览器,对于静态资源加载,会阻塞 HTML 解析器的线程进行,无论内联还是外链。

例如:

	<script src="test1.js"></script>
<script src="test2.js"></script>
<script src="test3.js"></script>
<img src="img.png" />

传统浏览器HTML解析器,会从test1.js 逐一解析到img.png,只个解析过程是同步的,只有当test1.js解析加载完成才会到test2.js 顺序加载。假设js文件加载时间需要1秒,img文件也需要1秒的时间,那么除去页面其他阶段的render时间不计,img图片会是4秒之后才显示给用户。

然而,现在主流浏览器对于静态资源的解析,已经做到了预解析和预加载。

相比传统浏览器,当浏览器HTML解析器,遇到test1.js静态资源的是,主线程中的解析器暂停解析,浏览器会新开启一个解析器线程,去预加载后面的资源。假设js文件加载时间需要1秒,img文件也需要1秒的时间。当浏览器遇到第一个js文件test1.js的时候,会新开启一个解析器线程,去预加载解析其他静态资源。除去页面其他阶段的render时间不计,那么img图片只会是2秒之后才显示给用户。

但浏览器能做的仅仅是预解析和预加载,脚本的执行和 DOM 树的构建仍然必须是线性的

关于document.write()加载JS等静态资源 和 异步async加载JS

document.write() 打破浏览器的预解析和预加载,因为document.write()加载的JS文件无法让HTML预解析器发现

	<script src="test1.js"></script>
<script>
document.write('<script src="http://xxx.com/test2.js"><\/script>')
</script>
<script src="test3.js"></script>

这个例子中,由于 test2.js 是通过 JS 代码插入的,HTML 预解析器是看不到的,所以只有当 test1.js 下载并执行完毕,且第二个内联的 script 执行完毕后,test2.js 才会开始下载,也就是说,test2.js 不能和 test1.js 及 test3.js 并行下载了,从而导致页面展现变慢,同样假设每个文件的下载时间都是 1 秒,那么这三个文件下载执行完就需要两秒,就因为 test2.js 不能预加载。在一个外链的 JS 文件比如 a.js 中执行 document.write("<script...) 也是类似的效果。

针对document.write() 去加载静态资源,我们可以做出什么优化?

将资源转成外链的方式加载。

如下

	<script src="test1.js"></script>
<script>
document.write('<script src="http://xxx.com/test2.js"><\/script>')
</script>
<script src="test3.js"></script>

改成

	<script src="test1.js"></script>
<script src="test2.js"></script>
<script src="test3.js"></script>

尽可能,让浏览器预解析器发现静态资源文件。但是这并不意味着页面的加载时间会大大减少。

假设test1.js的加载时间为1秒,test2.js的加载时间为10秒。即便test1.js之后的静态资源参与了浏览器的预解析加载,为了配合这句话“但浏览器能做的仅仅是预解析和预加载,脚本的执行和 DOM 树的构建仍然必须是线性的”。页面始终会全部资源加载完之后才完成渲染,test2.js的10秒加载时间仍旧会让页面处于loading转圈状态。

对于非第三方的静态资源的加载时间太长,应考虑前端资源的优化,这里列出来可能多的优化方案,但是暂不做详解

  • 减少静态文件的文件大小 (代码压缩)
  • 减少静态文件请求数量 (文件合并)
  • gzip
  • CDN和缓存

对于第三方的静态资源文件,可以使用async实现异步加载

async 加载静态资源

一句话概括便是异步的 script 根本不会阻塞 HTML 解析器,也就用不到预解析了

目前大部分第三方库都会支持async 异步 加载JS资源,然后去调用一个全局function 例如

<script src="test.js?callback=dosomething" async ></script>

async 的js资源,如果长时间pedding 会影响onload加载时间

关于document.write()的其他知识点

ducument.write 在onload之前,插入执行document.write()都会给页面插入内容,页面onload完成之后,浏览器输出流自动关闭;在此之后,任何一个对当前页面进行操作的document.write()方法将打开—个新的输出流

如下

关于document.write()加载JS等静态资源 和 异步async加载JS

结果是:

关于document.write()加载JS等静态资源 和 异步async加载JS

页面onload完成之后,调用document.write

关于document.write()加载JS等静态资源 和 异步async加载JS

结果是:

关于document.write()加载JS等静态资源 和 异步async加载JS