首先铺垫一下浏览器的绘制页面过程:、
- 第一步,首先是建立一个dom树,它是解析所有的dom节点(深度遍历的方法),解析dom节点就是解析到哪个dom节点就把它挂到dom树上,像碰到
<img src = "XXX"/>
这样的还是只把img节点挂到dom树上,意思就是说dom树的构建完成就是所有dom节点的解析完毕,并不代表所有dom节点加载完毕(dom的解析完毕一定在dom加载完毕之前)。 - 第二步生成cssTree
- 第三步生成randertree
因为js可以操作dom,所以当渲染树建立后,如果动态的操作了dom,机会导致页面重新渲染,叫做reflow重排(重构)
dom节点的删除,添加。dom节点的宽高变化,位置变化,displaynone–>block,offsetwidth,offsetLeft等一系列都会导致重排。
repaint重绘:js改变了节点 的css,会导致那一部分的重绘,这样影响比较小。
异步加载js
js加载的缺点
js加载本身是属于同步加载的,加载js文件会阻塞文档,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作。
但是有些工具方法需要按需加载,有一些工具js文件它是不会改变页面的,用到再加
载,不用不加载。
javascript异步加载的三种的方案
1. defer异步加载
defer异步加载,但要等到dom文档全部解析完才会被执行,只有IE能用,也可以将代码写到内部。
2. async 异步加载
async 异步加载,加载完就执行,async 只能加载外部脚本,不能把js写在script标签里。
asychronous javscript and xml
---->ajax的缩写
( 1和2执行时不阻塞页面,但是兼容性不好)
3. 创建script,插入到DOM中,加载完毕后callBack
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";//到了这一步之后就会异步的去下载demo.js文件
document.head.appendChild(script);//当把标签插入到页面的时候才会去执行这个js脚本
</script>
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";//到了这一步之后就会异步的去下载demo.js文件
document.head.appendChild(script);//当把标签插入到页面的时候才会去执行这个js脚本
test();//Uncaught ReferenceError: test is not defined
//为什么当前执行不了,因为文件还没有下载完,读程序是以微秒计的。有可能异步的下载demo.js文件还没有下载完,程序就读到test执行这了,还没有下载完demo.js,当然没有test函数了
</script>
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";//到了这一步之后就会异步的去下载demo.js文件
document.head.appendChild(script);//当把标签插入到页面的时候才会去执行这个js脚本
setTimeout(function () {
test();
}, 1000)
//这样就可以执行test函数了。等一段时间,等demo.js文件下载完毕了,再执行。
</script>
但是我们怎么知道它什么时候异步的把外部js文件下载完呢???
这个时候就要有一个信号来提示我们外部js文件异步下载完毕,你可以用了。
- load事件监听:Safari,chrome,firefox,opera都可以用这个来监听什么时候这个文件异步下载完毕
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";
script.onload = function(){
test();//当文件下载完之后就会执行onload方法,这样就知道什么时候demo.js加载完了。
}
document.head.appendChild(script);
</script>
- IE 就用状态码的改变来知道什么时候完成文件的下载
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState == "loaded"){
test();
}
}
document.head.appendChild(script);
</script>
- 综合1和2,实现所有浏览器兼容
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";//到了这一步之后就会异步的去下载demo.js文件
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState == "loaded"){
test();
}
}
}else{
script.onload = function(){
test();
}
}
document.head.appendChild(script);
</script>
- 封装一个函数兼容性的异步加载js文件并且可以按需执行该文件里面的函数
第一版:
<script>
//callback名字叫做回调函数,在事件里面的绑定的事件处理函数就是回调函数,回调函数其实就是当满足一定条件才调用的函数。
function loadScript (url, callback){
// url是异步下载的js文件
//callback是异步下载的js文件中的某一个函数
var script = document.createElement('script');
script.type = "text/javascript";
script.src = url;//异步下载的js文件
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState == "loaded"){
callback();
}
}
}else{
script.onload = function(){
callback();
}
}
document.head.appendChild(script);
}
</script>
第一版有一个缺陷就是在IE上如果下载太快(比读程序还快),IE的readystatechange 事件检测状态码的时候,它早已经从loading变成complete或者loaded(以极快的速度加载完了文件,你还没来得及检测),
那你再检测它就不会变了,它一直都是complete或者loaded,这个时候就是马后炮了,检测也没用了。
第二版(完美版):
<script>
function loadScript (url, callback){
// url是异步下载的js文件
//callback是异步下载的js文件中的某一个函数
var script = document.createElement('script');
script.type = "text/javascript";
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState == "loaded"){
callback();
}
}
}else{
script.onload = function(){
callback();
}
}
script.src = url;//开始异步下载的js文件
document.head.appendChild(script);
}
</script>
第二版就是先绑定事件,然后再script.src = url开始异步下载js文件,那么这个时候下载,肯定有一个状态码的转换,这样就解决了第一版还来不及检测状态码的尴尬。
封装函数的用法完善(截图中省略了loadScript,它就是上面的完整版):
第一种解决办法:
第二种解决办法(当要异步加载的js文件里面的函数构造是以这种命名空间的形式):
<script>
function loadScript (url, callback){
// url是异步下载的js文件
//callback是异步下载的js文件中的某一个函数
var script = document.createElement('script');
script.type = "text/javascript";
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState == "loaded"){
tools[callback]();
}
}
}else{
script.onload = function(){
tools[callback]();
}
}
script.src = url;
document.head.appendChild(script);
}
loadScript('demo.js', "test");
//传进去一个字符串,然后 tools[callback]();里面的tools[callback]拿到的就是test函数体,后面带上执行符号()执行就可以了。
</script>