首先让我们看看html的渲染过程:
我们发送请求后,服务器返回我们html文档,浏览器的parser从上而下进行token分析,一点点生成dom树,当碰到link之类的并发去请求css文件,然后同时加载CSSOM,挂载到dom上,js也是类似,从而当CSSOM和DOM渲染完后,就开始渲染树,这时候就开始渲染页面了。
HTML的渲染过程
- 顺序执行 通过nextToken一个个进行词法分析
- 并发加载,并发加载依赖的资源比如css,js。但是一个域名下的并发加载数是有限制的,所以会造成一定的阻塞。
- 阻塞,css阻塞js的执行,但不阻塞加载,html的加载阻塞js等等,因为js要操作dom对象,然后修改样式。总而言之,加载不会阻塞,执行会阻塞
- 依赖关系,比如有时候页面的css还没加载出来,就会出现一下闪屏,这是css和dom的依赖,然后另一个就是js之间的相互依赖
这边顺便补充下 async 和 defer,这两种js的下载都会和html解析同时进行,只是执行的时机不一样,async的话会在下载完后执行,然后阻断html的解析,而defer会在html解析完后执行。
懒加载和预加载
懒加载(Lazyload)
-
实现原理: 判断当前页面中某个元素距离Page顶部的高度是不是小于屏幕高度,小于的话,就把当前img的data-original属性替换到现在img的src上。
原生实现方法:
var viewHeight = document.documentElement.clientHeight
function lazyload() {
// 获取元素数组
var eles = document.querySelectorAll('img[data-original][lazyload]')
Array.prototype.forEach.call(eles, function(item, index) {
var rect
if (item.dataset.original === "") {
return
}
rect = item.getBoundingClientRect() // 获取当前元素的位置信息
if (rect.bottom >= 0 && rect.top < viewHeight) {
!function() {
var img = new Image()
img.src = item.dataset.original
img.onload = function() {
item.src = img.src
}
item.removeAttribute('data-ogiginal')
item.removeAttribute('lazyload')
}()
}
})
}
lazyload() // 解决首屏加载的问题
document.addEventListener('scroll', lazyload)
然后各个框架一般也都有自己的实现方法。
预加载(Preload)
- 第一种方式,就是直接通过html标签,然后设置display为none
- 第二种方法,就是new 一个Image对象,然后设置src
- 第三种方法,使用XHR,优点是过程更加可控,但是会有跨域问 题
- 第三方库,比如Preload.js