(十)Javascript中的JSON,异步加载,时间线(进程优化基础)
JSON
- JSON是一种传输数据的格式(以对象为样板,本质上就是对象,但用途有区别,对象就是本地用的,json是用来传输的)
- 为了区分对象和JSON,JSON中的属性名要加双引号
- 所以前端后台传输数据的格式其实是字符串,只不过这个是JSON格式的字符串,使用下面的方法,来使对象变为JSON传输给后台
<student>
<name>deng</name>
<age>40</age>
</student>
好比创建了一个对象,标签名就是标签名
传输数据要满足JSON的格式(即对象的格式)
- JSON.stringify(); json——>string
//JSON是一个静态类,是个构造函数,但不需要构造它
//因为JSON自身带着很多函数
<script type="text/javascript">
var obj = {
"name":"deng";
"age":40
}
console.log(JSON.stringify(obj));
//会返回字符串"{"name":"deng","age":123}"
var str = JSON.stringify(obj);
</script>
- JSON.parse(); string——> json(从后端接受字符串,转为对象)
var obj1 = JSON.parse(str);
//从后台传过来
//通过js操作把属性放在网页上
关于渲染
- 浏览器在渲染前会创建一个domTree,把HTML代码一行一行识别,然后挂到树上,树的顶端,分叉为和,里面可能又有很多
- 符合的原则:深度优先原则。一条枝干走到头
dom结点的解析
<img src="xxx">
<ifram src="xxx"></iframe>
<img src="xxx">
dom树的完成代表所有结点的解析完毕,并不是所有结点的解析完毕
-
当dom树结构完成之后先等着,开始生成cssTree,与dom结点对应,同样是深度优先。
-
domTree + cssTree = randerTree
-
randerTree形成之后,浏览器才开始渲染页面
-
randerTree的重构 reflow(效率最低)重排
-
会如何触发:dom结点的增删
dom结点的宽高变化,位置变化,display,none->block
offsetWidth offsetLeft(查看dom结点尺寸,因为求出结果是实时的,所以要重构randerTree)
-
-
所以要避免重排
-
repaint 重绘
- 比如背景颜色,字体颜色,背景图片位置(因为不影响位置)
-
-
异步加载JS
- JS加载的缺点: 加载工具方法没必要阻塞文档,过度JS加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作
- 有些工具方法需要按需加载,用到再加载,不用不加载
当代码执行到script的时候,做的是同步加载,会阻断CSS和HTML的下载线,因为执行的JS会修改CSS和HTML,所以执行的时候是单线程的,要不然一个线程去增加一个结点,一个线程去删除一个结点,就没有定论了。所以JS是单线程的
- 但有些JS文件不是用于操作页面的
- 可能是初始化页面或是工具包
- 所以我们希望它能实现异步加载(按需加载)
1、defer(IE)
- 用法:放入script标签中
- 概念:异步加载,但是要等到dom文档全部解析完(非加载)即构造好了randerTree才会被执行。注意这个只有IE能用,同时==可以将代码写至标签内部==
2、async
- 概念:异步加载,加载完就执行,async==只能加载外部脚本(即内部不能写入代码)
- 特点:执行时不阻塞页面
- AJAX: asychronous javascript and xml
3、创建script
,插入到DOM中,加载完毕后callback
- 可按需加载
<script type = "text/javascript">
//1.创建
var script = document.createElement("script");
//2.设置
script.type = "text/javascript";
//3.下载(发送请求的过程),这也是实现异步加载的过程
script.src = "tools.js";
//4.执行
document.body.appendChild(script);
</script>
问题:没加载完src就执行了,调用src里面的函数会被报错未定义
-
因为第三步的加载是异步加载,当代码来到第9行时,这个script放到body已经放入body中了,加入后面调用这个tool.js中的方法,但src认为加载完,会报错说调用的这个方法没有定义
-
解决办法:提醒src下载完了,然后即可调用里面的方法
script.onload=function(){test();} //这个load前面讲过,表示当script加载完就执行这个函数
兼容性高,但是IE无法使用,所以对于IE(使用监听)
//IE不用方法,IE用状态码(一个静态类的属性) script.readyState = "loading" 或者"complete" 或者"loaded" //表示script的加载状态 script.onreadystatechange= function(){ //进行校验 if(script.readyState=="complete" || script.readyState=="loaded") test(); }
- 衍生出来的问题:加入src一下下载完了,在执行下面的这个函数时,readyState实际上根本没有变化,状态从"complete"还是到"complete",所以不生效
- 解决办法,先绑定函数的状态,再进行加载,即将`script.src=url;`放在这两个函数的后面,这样就一定会经过状态的改变
快乐的封装函数时间
- 这个函数的作用是通过异步加载来调用某个js文件中的函数
```javascript
function loadScript(url,callback){
//1.创建
var script = document.createElement("script");
//2.设置
script.type = "text/javascript";
//兼容IE的检查src是否加载完
if(script.readyState){
script.onreadystatechange= function(){
if(script.readyState == "complete" || script.readyState == "loaded"){callback();}
}
}
else {
script.onload = function(){ callback(); }
}
//3.异步加载文件
script.src = url;
//
document.head.appendChild(script);
}
-
这里同样衍生出一个问题,文件和调用文件内的方法一起传入,但是没见过这个方法,也就是说想要调用的函数存在于文件中,但文件并未加载,现在作为实参传进去,系统不识别,会给他认为是未定义报错。
- 解决方法一:(比如像调用tool.js文件中的test方法)
loadScript("tool.js",function(){test();});
- 解决方法二:传入字符串
loadScript("tool.js", "test();"); //需要把callback改为 eval(callback) //eval的作用就是把字符串当作带代码来使 //但注意es3.0不能用,开启严格模式可以
- 解决方法三:同样传入字符串,但需要配合
loadScript("tool.js", "test"); //在tool.js中,test()被归属于它里面的一个对象的对象的方法 var tool = { test : function(){} } //需要把callback改为tool[callback]
JS加载时间线(为后面优化做铺垫)
-
依据JS出生那一刻开始,记录的一系列浏览器按照顺序做的事
- 创建Document对象,开始解析web页面。document.readyState = “loading”
- 遇到link外部css,创建线程加载,并继续解析文档
- 遇到script外部js,并且没有设置async、defer,浏览器加载,并阻塞,等待js加载完并执行完该脚本,然后继续解析文档
- 遇到script外部js,并且有设置async、defer,浏览器创建线程加载,并继续解析文档。对于async属性的脚本,脚本加载完成后立即执行(异步禁止使用document.write())
//假设在这个script标签前面有一系列的html和css,他们刚形成randerTree <script> window.onload = function(){ document.write("a"); } </script> //这时候页面剩下a,这个document.write("a")会把前面的文档流都清空
- 遇到img等,先正常解析dom结构,然后浏览器异步加载src(图片的源头),并继续解析文档
- 当文档解析完成(domTree完成),document.readyState = “interactive”.
- 文档解析完成后,所有设置有defer的脚本会按照顺序执行(根据第六步的监听)。(注重与async不同,async的script一解析完就执行)
- document对象触发DOMContentLoader事件,这也标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段(以及可以接触事件了)
- 当所有async脚本加载完成并执行后、img等加载完成后,document.readyState = “complete”
- 从此异步响应方式处理用户输入、网络事件
主要记1文档解析 6 文档解析完成 7defer按顺序执行 8触发事件,转化阶段 9所有东西执行并加载好
<script> console.log(doncument.readtState); //这里显示loading,因为打印出来这时,整个文档还没解析完,就是这里, //domTree还没形成完,script自个也是个结点,这里形成了一半 //给document绑定监听事件 document.onreadtstatechange = function(){ console.log(document.readyState) } //这里显示 loading(前面的) //interactive(监听到解析好) complete(监听到加载好) //关于第8步 //在document上,给这个DOMContnetLoader绑定事件处理的程序 document.addEventListener("DOMContentLoaded",function(){ console.log("a") },false) //这时后页面的控制台上为 //loading interactive a complete //所以含有这个的script可以不用写在尾部,因为它可以在下面的domTree解析构建完后执行