Web前端的知识之旅哟—— 异步加载JS

先介绍一下JSON:

我们传输数据就两种格式:xml和json

xml是以html为模板,自定义标签名作为数据名来传递数据,书写起来很麻烦,现在多用json,很少使用xml。

json是传输数据的另一种格式,它是以对象为模板(本质上就是对象,但是用途有所区别,对象是本地使用,json是用来传输数据)。

不过我们传输数据的时候并不能将一个json对象直接传过去,我们只能传过去json形式的字符串,这个时候我们就需要用到JSON上的两个方法了。

JSON.parse() —> string->json

JSON.stringify() —> json->string

通过JSON上的这两个方法,我们就可以进行数据传输了。


1. var obj = {
2. ‘name’: ‘scarlett’,
3. ‘age’: 18
4. };
5. var string = JSON.stringify(obj);
6. console.log(string); // ‘{“name”:”scarlett”,”age”:18}’
7. var data = JSON.parse(string);
8. console.log(data); // Object {name: “scarlett”, age: 18}
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

• 我们的页面有三个树:DOMTree、CSSTree、renderTree。(实际上多于三个),renderTree上有两个规则:repaint和reflow,重绘和重排。

repaint是元素自身的位置和宽高不变,只改变颜色的之类的属性而不会导致后面的元素位置的变化的时候,renderTree发生的动作。

reflow是元素自身的位置或者宽高改变了从而导致的整个页面的大范围移动的时候,renderTree发生的动作。

所以我们在DOM操作的时候,要尽量避免重排

JS异步加载部分

我们前面知道script标签的特点是会阻塞后面的DOM解析,只有当script标签下载完成并且全部执行完之后,浏览器才会继续解析DOM。

这样就导致了js加载有这样的缺点:加载工具方法本身没有必要阻塞文档,js加载会影响整个页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染。

DOMTree就是解析DOM结构的,当我们在解析DOM的时候突然碰到一个script标签,那么这个script标签就会阻断DOMTree和CSSTree,然而我们有一些js的工具库并不操作DOM,这个时候我们就需要让这些工具库进行异步加载或者按需加载了。

以前有一种写法是将script标签写在body的最后面,等在DOM全部解析完成之后才加载js。

现在有了html5标准之后,就有了另一套异步加载js的方法了。

js异步加载的三种方案:

1.defer异步加载

我们在script标签的行间写一个defer=“defer”或者直接写defer,就可以让这个script变成异步加载了。但是这种异步只是针对下载方面,只有等DOMTree全部解析完成(不包括下载完里面的资源)后才会异步执行。而且这个方法只有IE和一些高版本的firefox和chrome可以用


1.  
2. <script type=“text/javascript” defer=“defer”>
3. console.log(‘hello’);
4. </script>
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

不过这一种方式可以在script标签里面写代码

注意:IE6和IE7的异步加载最多只能有2个,当多余两个的时候必须等前两个加载完才会加载第三个。

所有defer的js代码都保证按照顺序执行

2.async异步加载

async是asynchronous的缩写,它是html5的标准,下载完成之后就会立即异步执行,这种方法除了IE6-IE8其他的浏览器都好用


1.  
2. <script type=“text/javascript” async=“async”></script>
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

不过这种方式不能把代码写在script标签里面,只能引用。(虽然标准是这么写的,但是现在随着内核升级,async里面也可以写代码了,在没有src的情况下)。

而且async的js代码不能保证是顺序执行的。

• 这两个属性不能一起使用

%e5%bc%82%e6%ad%a5js

图中蓝色部分属于js加载过程,红色部分表示js执行过程,绿色代表DOM解析过程。

兼容性写法:

1.直接写两个script标签,一个是defer,一个是async。

但是这种方法有缺陷:IE高版本会加载两遍引起冲突,有些浏览器两个都没有会一个都加载不出来。

所以我们就需要用第二种方法了。

2.通过动态添加script标签。

“Script-inserted script elements now have async default to true, which can be set to false to make the scripts execute in insertion order.”

w3c的标准规定:动态添加的script标签是异步的

通过这个特性,我们这里就可以封装一个兼容性的异步加载js的函数了。


1.  
2. function asyncLoaded(url, callback){
3. var script = document.createElement(‘script’);
4. script.type = ‘text/javascript’;
5. if(script.readyState){//IE和高版本的chrome、firefox
6. script.onreadystatechange = function(){
7. if(script.readyState = ‘loaded’ || script.readyState == ‘complete’){
8. script.onreadystatechange = null;
9. callback && callback();
10. }
11. }
12. }else{
13. script.onload = function(e){//Safari Chrome Opera Firefox
14. callback && callback();
15. }
16. }
17. script.src = url;
18. document.body.appendChild(script);
19. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

需要注意的是:readtState的if-else一定要写在script.src=url和appendChild之前,因为电脑速度可能会很快,刚走到src=url部分就已经加载完毕了,readyState已经变成了loaded,后面就不会触发onreadystatechange事件了。

这里src部分的下载是异步的,不会阻塞后面的代码的执行,也就是说可以一边把script插入到DOM中一边下载资源。

还有一点,如果我们的回调函数是写在需要加载进来的文件里面方法的话,我们就需要把这个方法放到匿名函数里面,这样在语法解析的时候才不会因为函数未声明而报错。

• 异步加载js不允许使用document.write这个方法。它会清除文档流,一旦执行就会把全部的标签都清除掉,包括自身的script标签。

JS加载时间线(performace timing line)

1、创建Document对象,开始解析web页面。解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。这个阶段document.readyState = ‘loading’。

2、遇到link外部css,创建线程加载,并继续解析文档。

3、遇到script外部js,并且没有设置async、defer,浏览器加载,并阻塞,等待js加载完成并执行该脚本,然后继续解析文档。

4、遇到script外部js,并且设置有async、defer,浏览器创建线程加载,并继续解析文档。 对于async属性的脚本,脚本加载完成后立即执行。(异步禁止使用document.write())

5、遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档。

6、当文档解析完成,document.readyState = ‘interactive’。

7、文档解析完成后,所有设置有defer的脚本会按照顺序执行。(注意与async的不同,

但同样禁止使用document.write());

8、document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,

转化为事件驱动阶段。

9、当所有async的脚本加载完成并执行后、img等加载完成后,document.readyState = ‘complete’,window

对象触发load事件。

10、从此,以异步响应方式处理用户输入、网络事件等。

JS的时间线是非常重要的知识点,希望大家能背下来这一块哟~