前言
html css javascript 可以算是前端必须掌握的东西了,但是我们的浏览器是怎样解析这些东西的呢 我们如何处理 html css javascript 这些东西来让我们的网页更加合理,在我这里做了一些实验,总结起来给大家看看。
最简单的页面
1 2 3 4 5 6 7 8 9 |
<!DOCTYPE html> <html> <head> <title>test</title> </head> <body> <img src="download-button.png"> </body> </html> |
我们打开 chrome 的控制台查看 timeline
由上图 可得结论
1 图中蓝色透明条标识浏览器从发起请求到接收到服务器返回第一个字节的时间,时间还是挺长的,而蓝色实体条则为真正的 html 页面下载的时间 还是很短的。
2 图中红框内的这部分时间则表示浏览器从下载完成 html 之后开始构建 dom,当发现一个 image 标签时所花费的时间,由此可见 dom 是顺序执行的,当发现 image 时便立即发起请求,而紫色透明条则是 image 发起请求时在网络传输时所消耗的时间。
3 图中 timeline 蓝色竖线所处的时间为 domComplete 时间,红色竖线为 dom 的 onload 时间,由此可见两种事件的差异。而浏览器构建 dom 树所花费的时间可以算出即 domComplete 时间 减去 html 下载完成后的时间大概 80ms。
含有 css 的页面
1 2 3 4 5 6 7 8 9 10 |
<!DOCTYPE html> <html> <head> <title>test</title> <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen"> </head> <body> <img src="download-button.png"> </body> </html> |
我们打开 chrome 的控制台查看 timeline
1 在添加了外部引入 css 之后,并没有发现什么异常,但是有一点指的注意,也就是红色竖线和蓝色竖线挨得更进了,这表明 domComplete 时间必须等待 css 解析完成,也就是构建 dom 树必须等待 css 解析完成,这也就解释了下图
含有 javascript 和 css 的页面
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE html> <html> <head> <title>test</title> <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen"> </head> <body> <img src="download-button.png"> <script type="text/javascript" src="H5FullscreenPage.js"></script> </body> </html> |
我们打开 chrome 的控制台查看 timeline
1 图上显示在引入外部的 js 文件之后 domComplete 时间又被延后了,结合上面的 renderTree,由于 javascript 代码可能会更改 css 属性或者是 dom 结构,所以在形成 renderTree 之前必须等待 javascript 解析完成才能接着构建 renderTree。
2 将 javascript 放在 head 内和 body 底部的区别也在于此,放在 head 里面,由于浏览器发现 head 里面有 javascript 标签就会暂时停止其他渲染行为,等待 javascript 下载并执行完成才能接着往下渲染,而这个时候由于在 head 里面这个时候页面是白的,如果将 javascript 放在页面底部,renderTree 已经完成大部分,所以此时页面有内容呈现,即使遇到 javascript 阻塞渲染,也不会有白屏出现。
内嵌 javascript 的页面
1 图上可以看到,由于内嵌了 javascript,页面上减少了一个请求,导致 html 文档变大,消耗时间增多,但是 domComplete 时间提升的并不多。
使用 async 的 javascript
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE html> <html> <head> <title>test</title> <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen"> </head> <body> <img src="download-button.png"> <script async src="H5FullscreenPage.js" type ="text/javascript" ></script > </body> </html> |
1 可以看到 domComplete 时间被大大提前 javascript 也没有阻塞 css 和 body 里面 img 元素的并行下载。
2 使用 async 标识的 script,浏览器将异步执行这中 script 不会阻塞正常的 dom 渲染,这时 html5 所支持的属性,另外 defer 也可以达到这种效果。
head 里面 js 和 css 加载的关系
外联 js 在 css 前面
1 2 3 4 5 6 7 8 9 10 11 12 |
<!DOCTYPE html> <html> <head> <title>test</title> <script src="H5FullscreenPage.js" type ="text/javascript" ></script > <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen"> <link rel="stylesheet" type="text/css" href="page-animation.css" media="screen"> </head> <body> <img src="download-button.png"> </body> </html> |
1 没有阻止 css 的并行加载但是影响了 body 里面 img 的并行加载
外联 js 在 css 中间
1 2 3 4 5 6 7 8 9 10 11 12 |
<!DOCTYPE html> <html> <head> <title>test</title> <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen"> <script src="H5FullscreenPage.js" type ="text/javascript" ></script > <link rel="stylesheet" type="text/css" href="page-animation.css" media="screen"> </head> <body> <img src="download-button.png"> </body> </html> |
1 影响了 css 的并行加载和 body 里面 img 的并行加载
外联 js 在 css 最后
1 2 3 4 5 6 7 8 9 10 11 12 |
<!DOCTYPE html> <html> <head> <title>test</title> <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen"> <link rel="stylesheet" type="text/css" href="page-animation.css" media="screen"> <script src="H5FullscreenPage.js" type ="text/javascript" ></script > </head> <body> <img src="download-button.png"> </body> </html> |
1 影响了 css 的并行加载和 body 里面 img 的并行加载
内嵌 js 在 css 前面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!DOCTYPE html> <html> <head> <title>test</title> <script type ="text/javascript" > var f = 1; f++; </script > <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen"> <link rel="stylesheet" type="text/css" href="page-animation.css" media="screen"> </head> <body> <img src="download-button.png"> </body> </html> |
1 没有影响 css 的并行加载也没有影响 body 里面 img 的并行加载
内嵌 js 在 css 中间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!DOCTYPE html> <html> <head> <title>test</title> <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen"> <script type ="text/javascript" > var f = 1; f++; </script > <link rel="stylesheet" type="text/css" href="page-animation.css" media="screen"> </head> <body> <img src="download-button.png"> </body> </html> |
1 影响了 css 的并行加载没有英雄 body 里面 img 的并行加载
内嵌 js 在 css 最后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!DOCTYPE html> <html> <head> <title>test</title> <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen"> <link rel="stylesheet" type="text/css" href="page-animation.css" media="screen"> <script type ="text/javascript" > var f = 1; f++; </script > </head> <body> <img src="download-button.png"> </body> </html> |
1 影响了 css 和 body 里面 img 的并行加载。
综上所述:
当浏览器从服务器接收到了 HTML 文档,并把 HTML 在内存中转换成 DOM 树,在转换的过程中如果发现某个节点 (node) 上引用了 CSS 或者 IMAGE,就会再发 1 个 request 去请求 CSS 或 image, 然后继续执行下面的转换,而不需要等待 request 的返回,当 request 返回 后,只需要把返回的内容放入到 DOM 树中对应的位置就 OK。但当引用了 JS 的时候,浏览器发送 1 个 js request 就会一直等待该 request 的返回。因为浏览器需要 1 个稳定的 DOM 树结构,而 JS 中很有可能有代码直接改变了 DOM 树结构,浏览器为了防止出现 JS 修改 DOM 树,需要重新构建 DOM 树的情况,所以 就会阻塞其他的下载和呈现.
这里的结论:
1 在 head 里面尽量不要引入 javascript.
2 如果要在 head 引入 js 尽量将 js 内嵌.
3 把内嵌 js 放在所有 css 的前面.
后记
1 本次的测试页面 http://1.lvming6816077.sinaapp.com/testaa/demo.html
2 测试所用浏览器 chrome
3 参考资料:http://www.zhihu.com/question/20357435/answer/14878543
http://www.haorooms.com/post/web_xnyh_jscss
4 如果有哪里说的不清楚或者错误的地方,欢迎留言反馈。
小公鸡 2017 年 8 月 15 日
图挂了
xiangnaier 2016 年 11 月 17 日
真棒!早发现这么好的东西就好了!
bire 2016 年 10 月 26 日
图挂了
f91 2016 年 4 月 3 日
非常感谢
f91 2016 年 4 月 3 日
mark
J 2015 年 11 月 6 日
赞 受教了
非礼 2015 年 10 月 15 日
有错别字,“英雄” 改成 “影响”。还有有仁兄说大部分网站还是将 js 放在 head 里,这个我觉得是每个网站各自考虑的东西和平衡不一样。就好比大家都知道两点之间直线最短。但是实际上又有多少人按照直线走了呢。
新浪前端 2015 年 7 月 30 日
http://www.alloyteam.com/2014/03/effect-js-css-and-img-event-of-domcontentloaded/与这一篇文章对比着看,看晕了。动态引入的 js 为什么不会影响 domContentLoaded?css 到底会不会影响 js 的加载?既然浏览器只能等 js 加载完了,才能执行 domContentLoaded,那 js 放到页尾还有毛用?晕了晕了。求解释。(还有:浏览器的渲染引擎,是实时渲染,还是只有样式改变时,才渲染?就告诉。谢谢!)
吕鸣 2015 年 7 月 30 日
domContentLoaded 和 domComplete 是两个不同的事件哦 http://www.nihaoshijie.com.cn/index.php/archives/472
吕鸣 2015 年 7 月 30 日
动态引入的 js 不是一开始在页面就写的是通过 js 去改变 dom 来引入 script 标签 这个时候整个页面也已经完成了自然不会影响首屏的 domContentLoaded
新浪前端 2015 年 7 月 30 日
依然是似懂非懂。通过脚本动态引入的 js 文件,是等这个动态 js 文件加载完了,再执行 domContentLoaded,还是 domContentLoaded 根本就不受动态 js 文件加载的影响?我问的是这个意思。
吕鸣 2015 年 7 月 30 日
这个是不影响的 你可以做个实验试一下嘛 先不动态引入 js 然后看一下 domContentLoaded 时间 然后在动态引入 js 在看一下时间 这样一对比就知道结果了 自己动手更加清晰 可以参考 http://1.lvming6816077.sinaapp.com/testaa/domComplete.html
吕鸣 2015 年 7 月 30 日
既然浏览器只能等 js 加载完了,才能执行 domContentLoaded 这里说的是 js 放在 body 底部 这是为了尽快让页面有可视内容出来 这个时候这些内容不一定都能点击一些事件绑定等逻辑放在 body 底部这些逻辑不影响页面展示的
云库网 2015 年 7 月 27 日
看来大家都学到东西了
游客 2015 年 7 月 25 日
你们都在说尽量吧外联 JS 放在底部,但是基本所有的网站都是放在 head 里面…….
John 2015 年 6 月 9 日
非常好的文章只是第二点我不同意 (2 如果要引入 js 尽量将 js 内嵌.)
1. 第一次测试没问题,但是咱们都知道浏览器是有缓存的,当浏览器缓存 js 文件以后,下次在加载,直接从缓存里面加载了,如果是内嵌的,就没有这个优势了,
2. 对于 js,css 可以开启 gzip 压缩,如果是内嵌的话,就没办法使用 gzip 压缩功能了。
3. 后续维护是难题,因为现在页面里面有大量 js 代码后续维护可以说是噩梦
TAT.tennylv 2015 年 6 月 11 日
1 可能解释的有点不准确,我这里特指的是在 head 引入 js 的话最好内嵌,已更改。
2 尽管 js css 可以 gzip 压缩 pc 上可能不明显 但是对于移动端每多发一次请求都会耗费大量的性能,另外 html 也会 gzip 压缩。
3 这边内嵌 js 一般都不会直接在 html 页面里写 js 的 按正常的 js 文件编写,在构建的时候把 js 压缩并内嵌到 html 里面。
裕波 2015 年 5 月 23 日
HTML,CSS,JavaScript 大小写能规范书写吗?
TAT.tennylv 2015 年 5 月 25 日
OK
C。 2015 年 5 月 13 日
如果引入个不存在的 js 文件,使用 defer 的时候蓝红线始终在一起,使用 async 蓝线会先出现,defer 没有 async 好用啊
C。 2015 年 5 月 13 日
如果 css 的 url 不对,蓝线有可能会在 css 文件下载过程中出现,所以 “domComplete 时间必须等待 css 解析完成” 这个结论貌似不对啊
TAT.tennylv 2015 年 5 月 13 日
如果 css 的 url 不对,css404,那么浏览器是下载不了 css 的 domComplete 时间也就可 css 无关了 这里其实也就不用考虑 css 解析了
C。 2015 年 5 月 13 日
可是浏览器在尝试下载失败后才知道是 404 啊,所以有可能 dom 没有等 css 解析完就 Complete
TAT.tennylv 2015 年 5 月 14 日
这里都没 css 了 就不属于此类情况的了
星情 2015 年 5 月 7 日
很不错的文章
小无路 2015 年 5 月 6 日
结论简单明了。。我稀饭
Jation 2015 年 5 月 6 日
nice