当初始的 HTML 文档被彻底加载和解析完成以后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。另外一个不一样的事件 load 应该仅用于检测一个彻底加载的页面。 在使用 DOMContentLoaded 更加合适的状况下使用 load 是一个使人难以置信的流行的错误,因此要谨慎。注意:DOMContentLoaded 事件必须等待script以前的样式表加载解析完成才会触发。javascript
// 客户端 html 代码
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
document.addEventListener('DOMContentLoaded',function(){
console.log('3 seconds passed');
});
</script>
<link rel="stylesheet" href="http://localhost:3000/sleep">
</head>
<body>
<div><a >1234567</a></div>
</body>
// express 代码
var express = require('express');
var app = express();
app.get('/sleep', function(req, res, next) {
res.type('text/css')
setTimeout(()=>{
res.send("a {\n" +
" color: red;\n" +
"}")
},3000)
});
复制代码
控制台当即显示 3 seconds passed。 页面是空白的。 过三秒后,页面直接显示红字。css
页面是空白的。过三秒后,html
控制台显示 3 seconds passed。java
页面直接显示红字。express
由于 DOM 的渲染须要 DOM 树和 CSSOM 树共同来生成渲染树,因此在 CSS 加载完成以前, DOM 是不会进行渲染的。仍是用上面那个例子,咱们会发现 CSS 没有加载完成时,页面上是不显示的,而当 CSS 加载完成的瞬间,标签就被渲染到页面上了。浏览器
结果:页面先显示出div内容,过三秒后,字体颜色改变+控制台打印。缓存
// html内的代码
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="http://localhost:3000/sleep">
<script defer src="js/test.js"></script>
</head>
<body>
<div><a >1234567</a></div>
</body>
// js/test.js
const div = document.querySelector('div');
console.log(div);
复制代码
此时咱们的 css 还没加载完成,可是咱们能够看到 console 选项卡里面 div 标签已经被打印出来了,说明,此时 DOM 已经解析完成触发 DOMContentLoaded 事件。bash
<script>
var starttime = new Date().getTime();
console.log("page start" + starttime);
</script>
<link rel="stylesheet" href="http://localhost:3000/sleep">
<script src="js/test.js"></script>
// js/test.js
var endtime = new Date().getTime();
console.log("delay:" + (endtime - starttime));
复制代码
sleep 3秒后才返回 网络
当解析起遇到 script 标签时,文档会当即中止解析直到脚本执行完毕,若是脚本是外部的,那么解析过程会中止,直到从网络同步抓取资源完成后再继续。由于咱们的脚本会操做 DOM,因此在脚本跑完以前浏览器不知道脚本会把 DOM 改为什么样,因此就等脚本执行完再进行解析。app
<script >
let ui = document.getElementById('ui')
ui.innerText = 'blue'
</script>
<div id="ui">test3</div>
复制代码
从代码和图中看到,当把dom放在js下面,取不到元素,报错。
GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起(至关于被冻结了),GUI 更新会被保存在一个队列中等到 JS 引擎空闲时当即被执行。
<div id="ui">test3</div>
<script >
var now = new Date().getTime();
let ui = document.getElementById('ui')
while (new Date().getTime() - now < 3000) {
continue;
}
ui.innerText = 'blue'
</script>
复制代码
<div id="ui">test3</div>
<script src="js/test.js"></script>
// js/test.js 内容与上面script内的内容同样
复制代码
另外一套测试代码
// 引入远程文件
<script src="http://localhost:3000/test.js"></script>
// express 服务端代码,2秒后再返回
app.get('/test.js', function(req, res, next) {
res.type('application/javascript')
setTimeout(()=>{
res.send(`alert()`)
},2000)
});
复制代码
浏览器执行 css dom js 在同步模式下,都是从上往下执行。 渲染等到 JS 引擎空闲时当即被执行。
主要缘由仍是script内代码有没有加载完,若是没有加载完就会先渲染一次,加载完就会先执行js,看js有没有改变dom,避免浪费渲染一次,很智能。
比起js的懒加载, css懒加载和初始加载最小体积会更加提升性能。
async 模式下,JS 不会阻塞浏览器作任何其它的事情。它的加载是异步的,当它加载结束,JS 脚本会当即执行。
defer 模式下,JS 的加载是异步的,执行是被推迟的。等整个文档解析完成、DOMContentLoaded 事件即将被触发时,被标记了 defer 的 JS 文件才会开始依次执行。
从应用的角度来讲,通常当咱们的脚本与 DOM 元素和其它脚本之间的依赖关系不强时,咱们会选用 async;当脚本依赖于 DOM 元素和其它脚本的执行结果时,咱们会选用 defer。
一句话,defer是“解析完dom再执行”,async是“下载完就执行”。另外,若是有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。
<link>
元素的 rel 属性的属性值preload可以让你在你的HTML页面中 <head>
元素内部书写一些声明式的资源获取请求,能够指明哪些资源是在页面加载完成后即刻须要的。对于这种即刻须要的资源,你可能但愿在页面加载的生命周期的早期阶段就开始获取,在浏览器的主渲染机制介入前就进行预加载。这一机制使得资源能够更早的获得加载并可用,且更不易阻塞页面的初步渲染,进而提高性能。
// 预加载Css
<link rel="preload" as="style" href="async_style.css" onload="this.rel='stylesheet'">
// 预加载js
<link rel="preload" as="script" href="async_script.js"
onload="var script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);">
复制代码
浏览器会查找关系类型(rel)为 next 或 prefetch 的 HTML 或 HTTP Link: header。例子:
<link rel="prefetch" href="/images/big.jpeg">
<link rel="prefetch alternate stylesheet" title="Designed for Mozilla" href="mozspecific.css">
<link rel="next" href="2.html">
复制代码
已经被许多浏览器支持了至关长的时间,但它是意图预获取一些资源, 以备下一个导航/页面使用(好比,当你去到下一个页面时)。 这很好,但对当前的页面并无什么助益。 此外,浏览器会给使用prefetch的资源一个相对较低的优先级——与使用preload的资源相比。毕竟,当前的页面比下一个页面相对更加剧要。
<link rel="stylesheet" href="http://localhost:3000/sleep2.load" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload1" >
<link rel="prefetch" as="style" href="http://localhost:3000/sleep2.prefetch" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload2" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload3" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload4" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload5" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload6" >
<link rel="preload" as="style" href="http://localhost:3000/sleep2.preload7" >
<link rel="stylesheet" href="http://localhost:3000/sleep.load" >
复制代码
从下图看出,preload比正常load优先级大,会优先占用tcp连接,即便sleep2.load
写在最上面,也要等6条tcp占用释放一条。prefetch 会在空闲时再使用。