html,css,js代码的加载顺序问题
在写计算器的小程序中,把script标签放在head标签中css样式下,发现script中写的代码无法获取到计算器的按键,于是去搜集了一下html,css,js代码的执行顺序
html文档的加载历程
- 用户输入一个网址(假定是一个html文档,且是第一次访问),浏览器向服务器发出请求,服务器返回html文件
- 浏览器开始载入html代码,发现
<head>
标签内有一个<link>
标签引用外部的css文件 - 浏览器又发出css文件请求,服务器返回这个css文件
- 浏览器继续载入html中
<body>
标签内的代码,并且css文件已经拿到手,可以开始渲染页面了 - 浏览器在代码中发现了一个
<img>
标签引用了一张图片,向服务器发出请求,此时浏览器不会等到图片下载完而是继续渲染后面的代码 - 服务器返回图片文件,由于图片占用了一定的面积。影响了后面段落的排布,因此浏览器需要重头来过来重新渲染这部分代码
- 浏览器发现一个包含Javascript代码的script标签,如果
<script>
标签引用了外部脚本,就下载改脚本,否则就直接执行 - 执行完成之后,控制权交给渲染引擎,恢复向下解析HTML网页(如果Javascript脚本执行了命令浏览器隐藏掉代码中的某个例如执行语句
<style>
(style.display = “none”),突然少了此元素,浏览器不得不重新渲染这部分代码) - 假如用户点击了一下“换肤”按钮,Javascript让浏览器换了一下
<link>
标签的路径,浏览器向服务器请求了新的css文件,重新渲染界面
以上是html 文档的执行顺序,下面来详细解读一下JS的加载
JS加载
- js代码不可以和其他代码并行下载和解析(又称阻塞下载)
当引用了JS之后,浏览器回发送一个js request,然后一直等待该request返回,因为浏览器需要一个稳定的DOM树结构,而JS中很有可能会有代码直接改变了DOM树的结构,浏览器为了防止出现JS修改DOM树,需要重新构建DOM树的情况,就会阻塞其他的下载和呈现,DOM的操作顺序是不可控的
3.js如果放在head中会立即执行,阻塞后续资源的下载与执行,如果是嵌入了外部脚本,外部脚本加载时间很长(例如:一直无法完成下载),就会造成网页长时间失去响应,浏览器就会呈现“假死”的情况,这也被称为“阻塞效应”。html需要等head中所有的JS和CSS加载完成后才开始绘制,但是如果把HTML不需要等待放在body最后的js下载执行完毕,可以避免资源阻塞,同时使静态的html页面迅速显示。而且把脚本文件放在网页尾部加载,还有一个好处。在DOM结构生成之前就调用,JavaScript就会报错,如果脚本都在网页尾部加载就不存在这个问题,因为此时DOM已经生成
4..js的执行依赖前面的样式。即只有前面的样式全部下载完成后才会执行js,但是此时外链css和外链js是并行下载的。
所以css需要分块,首页的css独立,其余的css需要动态加载,因为html的绘制会被css阻塞,这样可以减少首次进入时的白屏时间。
script标签的两个属性
defer属性
外链的js如果含有defer=”true”属性,将会并行加载js,到页面全部加载完成后才会执行,会按顺序执行。(defer是script标签的一个属性,当所有内容加载解析完成之后才开始执行defer修饰的脚本语言)
defer属性的作用是,告诉浏览器,等到DOM加载完成后,再执行指定脚本。
含有defer的script标签的执行顺序
1.浏览器开始解析HTML网页
2.解析过程中,发现带有defer属性的script标签
3.浏览器继续往下解析HTML网页,同时并行下载script标签中的外部脚本
4.浏览器完成解析HTML网页,此时再执行下载的脚本
5.对于内置而不是连接外部脚本的script标签,以及动态生成的script标签,defer属性不起作用。
async属性
外链的js如果含有async=”true”属性,将不会依赖于任何js和css的执行,此js下载完成后立刻执行,不保证按照书写的顺序执行。因为async=”true”属性会告诉浏览器,js不会修改dom和样式,故不必依赖其它的js和css。 (ansync是script标签的一个属性,其英文含义是异步,添加在script标签中也可以写成async=”async”)
async属性的作用是,使用另一个进程下载脚本,下载时不会阻塞渲染。
含有ansync的script标签的执行顺序
1.浏览器开始解析HTML网页
2.解析过程中,发现带有async属性的script标签
3.浏览器继续往下解析HTML网页,同时并行下载script标签中的外部脚本
4.脚本下载完成,浏览器暂停解析HTML网页,开始执行下载的脚本
5.脚本执行完毕,浏览器恢复解析HTML网页
async属性可以保证脚本下载的同时,浏览器继续渲染。需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序。哪个脚本先下载结束,就先执行那个脚本。另外,使用async属性的脚本文件中,不应该使用document.write方法。
一般来说,如果脚本之间没有依赖关系,就使用async属性,如果脚本之间有依赖关系,就使用defer属性。如果同时使用async和defer属性,后者不起作用,浏览器行为由async属性决定。
搜集了一点关于提高HTML页面加载速率的建议
1. 页面减肥:页面的肥瘦是影响加载速度最重要的因素。
a. 删除不必要的空格、注释。
b. 将inline的script和css移到外部文件。
c. 可以使用HTML Tidy来给HTML减肥,还可以使用一些压缩工具来给JavaScript减肥。
2. 减少文件数量:减少页面上引用的文件数量可以减少HTTP连接数。
a. 许多JavaScript、CSS文件可以合并最好合并
3. 减少域名查询: DNS查询和解析域名也是消耗时间的,所以要减少对外部JavaScript、CSS、图片等资源的引用,不同域名的使用越少越好。
4. 缓存重用数据:
a. 对重复使用的数据进行缓存。
5. 优化页面元素加载顺序:
a. 首先加载页面最初显示的内容和与之相关的JavaScript和CSS,然后加载HTML相关的东西,像什么不是最初显示相关的图片、flash、视频等很肥的资源就最后加载。
6. 减少inline JavaScript的数量:
a. 浏览器parser会假设inline JavaScript会改变页面结构,所以使用inline JavaScript开销较大。
b. 不要使用document.write()这种输出内容的方法,使用现代W3C DOM方法来为现代浏览器处理页面内容。
7. 使用现代CSS和合法的标签:
a. 使用现代CSS来减少标签和图像,例如使用现代CSS+文字完全可以替代一些只有文字的图片。
b. 使用合法的标签避免浏览器解析HTML时做“error correction”等操作,还可以被HTML Tidy来给HTML减肥。
8. 减少嵌套:
a. 不要使用嵌套table,而使用非嵌套table或者div。将基于大块嵌套的table的layout分解成多个小table,这样就不需要等到整个页面(或
大table)内容全部加载完才显示。
9. 指定图像和table的大小:
a. 如果浏览器可以立即决定图像或table的大小,那么它就可以马上显示页面而不要重新做一些布局安排的工作。
b. 这不仅加快了页面的显示,也预防了页面完成加载后布局的一些不当的改变。
c. image使用height和width。