文章目录
- JSON传输格式
- 浏览器渲染页面过程
- JS异步加载
- 时间线
JSON传输格式
json是一种传输数据的格式(以对象为样板,本质上即使对象,但是用途有区别,对象就是本地用的,json是用来传输的)
对象的属性加不加双引号都可以,但是json规定 json格式的属性名必须加上双引号
。
JSON是一个静态类(类似于Math),不用去构造它,它自己就有这些方法。
- JSON.stringifg(); json—>string
- JSON.parse(); string—>json
var obj = {
"name" : "abc",
"age" : 123
}
var str = JSON.stringify(obj);
console.log(str);//"{"name":"abc","age":123}"
console.log(JSON.parse(str));//Object{name: "abc", age: 123}
前后端传输的时候传的都是json格式的字符串
浏览器渲染页面过程
第一步,首先是建立一个dom树,它是解析所有的dom节点(深度遍历的方法)
解析dom节点就是解析到哪个dom节点就把它挂到dom树上
像碰到< img src = “XXX”/>这样的还是只把img节点挂到dom树上,意思就是说dom树的构建完成就是所有dom节点的解析完毕,并不代表所有dom节点加载完毕(dom的解析完毕一定在dom加载完毕之前)。
第二步生成cssTree
浏览器解析CSS代码,计算出最终的样式数据。解析CSS的时候会按照如下顺序来定义优先级:浏览器默认设置,用户设置,外联样式,内联样式,html中的style(嵌在标签中的行间样式)。
第三步生成randertree,也可以说是渲染树
渲染树中每一个节点都存储着对应的CSS属性。
因为js可以操作dom,所以当渲染树建立后,如果动态的操作了dom,机会导致页面重新渲染,叫做reflow重排(重构)
reflow重排:dom节点的删除,添加。dom节点的宽高变化,位置变化,displaynone–>block,offsetwidth,offsetLeft等一系列都会导致重排。
repaint重绘:js改变了节点 的css,会导致那一部分的重绘,这样影响比较小。
第四步:当渲染树创建完成之后,浏览器就可以根据渲染树直接把页面绘制到屏幕上。DOM树完全和html标签一一对应,而渲染树会忽略不需要渲染的元素(head、display:none的元素)。
JS异步加载
js加载的缺点
js加载本身是属于同步加载的,加载js文件会阻塞文档,比如有多个js文件,在加载到一个文件处有错后面的就加载不了
或者一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作
但是有些工具方法需要按需加载,有一些工具js文件它是不会改变页面的,用到再加载,不用不加载。
javascript异步加载的三种的方案
- . defer异步加载
defer异步加载,但要等到dom文档全部解析完才会被执行,也可以将代码写到内部。
defer 属性是一个布尔属性。
<script src="demo_defer.js" defer></script>
- async 异步加载
async 异步加载,加载完就执行,async 只能加载外部脚本(只有在使用 src 属性时),不能把js写在script标签里。
Internet Explorer 9 及之前的版本不支持 script 标签的 async 属性。
异步脚本一定会在页面 load 事件前执行。
<script src="demo_async.js" async></script>
defer和async异步加载时不阻塞页面,但是兼容性不好,不能控制加载的顺序
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>
接下来我么创建一个名为demo.js文件写入test函数
function test(){
condole.log("a“);
}
执行他
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";//到了这一步之后就会异步的去下载demo.js文件
document.head.appendChild(script);//当把标签插入到页面的时候才会去执行这个js脚本
test();
</script>
执行到test()时候会报错Uncaught ReferenceError: test is not defined
这是因为文件还没有下载完,读程序是以微秒计的。
有可能异步的下载demo.js文件还没有下载完,程序就读到test执行这了
还没有下载完demo.js,当然没有test函数了
接着我们 添加一个setTimeout() 方法,让test()在1000毫秒后再执行时不时就不报错了
<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)
</script>
但是这样并不能从根本上解决问题,我们需要知道程序什么时候异步把外部js文件下载完成
下面介绍几种方法让我们知道什么时候加载完成
- load事件监听:
Safari,chrome,firefox,opera都可以用这个来监听什么时候这个文件异步下载完毕
<script>
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";
script.onload = function(){
test();
}
document.head.appendChild(script);
</script>
当文件下载完之后就会执行onload方法,这样就知道什么时候demo.js加载完了。
- 如果是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>
综合以上两钟方法实现所有浏览器兼容
<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文件并且可以按需执行该文件里面的函数
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文件,那么这个时候下载,肯定有一个状态码的转换,这样就解决了来不及检测状态码的尴尬。
时间线
- 创建Documen对象开始解析web页面。解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。这个阶段document.readyState= ‘loading’ 。
- 遇到link外部css,创建线程加载,井继续解析文档。
- 遇到script外部js,并且没有设置async,defer, 浏览器加载,并阻塞,等待js加载完成并执行该脚本,然后继续解析文档。
- 遇到script外部js.并且设置有async, defer,浏览器创建线程加载,并继续解析文档。对于async属性的脚本,脚本加载完成后立即执行。(异步禁止使用document.write(),因为它会清除文档流)
- 遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档。
- 当文档解析完成,document.readyState = ‘Interactive’。
- 文档解析完成后,所有设置有defer的脚本会按照顺序执行。(注意 async的不同,但同样禁止使用document.wite()) ;
- document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段。
- 当所有async的脚本加载完成井执行后。img等加载完成后,document.readyState = ‘complete’, window对象触发load事件。
- 从此,以异步响应方式处理用户输入,网络事件等。
document.write()会清除文档流
<body>
<div style="width:100px;height:100px;background-color:red;"></div>
<script>
window.onload = function () {
document.write('a');
}
</script>
</body>
这个时候页面就只会输出a,div就被清除了
创建Documen对象开始解析web页面,script标签也是dom元素,所以它也要识别,这个阶段document.readyState= ‘loading’ 。
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<script>
console.log(document.readyState);//loading
<script>
</body>
</html>
用状态码改变才能检测文档解析到哪个状态了
<body>
<script>
console.log(document.readyState);//loading
document.onreadystatechange = function (){
console.log(document.readyState);// interactive complete
}
<script>
</body>
DOMContentLoaded事件只有用addEventListener绑定才有效
document.addEventListener('DOMContentLoaded',function(){ console.log('a')},false);
正常的情况下,当文档加载(解析文档,就是构成dom树,然后再加载dom元素)完毕后,才执行js代码,
但是document对象触发DOMContentLoaded事件可以当dom解析完了(dom树构建完成,还没加载完毕的时候)就去执行js代码
/就像jquery里面的
当dom解析完就执行的部分
$(document).ready(function (){
…
})
现在用document对象触发DOMContentLoaded事件放在head里面,照样可以拿到div
因为它是在dom树构建完成之后就调用事件的那个处理函数,不用等到文档全部加载完毕
而window.onload()就是要等到文档全部加载完毕了(像img的src的图片加载完成了),它才会执行window.onload()里面的js。
<!DOCTYPE html>
<html lang="en">
<head>
<script>
document.addEventListener('DOMContentLoaded',function(){
var div = document.getElementsByTagName('div')[0];
console.log(div);
},false);
</script>
</head>
<body>
<div style="width:100px;height:100px;background-color:red"></div>
</body>
</html>