24、Json、异步加载、JS加载时间线

Json、异步加载、JS加载时间线

json

JSON是一种传输数据的格式

(以对象为样板,本质上就是对象,但用途有区别,对象就是本地用的,json是用来传输的)

早期前后端以xml的格式发送数据
之前讲过xml可以自定义标签(标签名就是属性名 内容就是属性值) 但是html不行

<student>
    <name>deng</name>
    <age>40</age>
</student>

现在传的数据格式就是对象 运用到数据传输里面换了个名字叫json

为了区分对象和json写法 json里面强制规定属性名必须加双引号

传送对象给后端会识别不了 因为只能识别二进制文本 所以传送的对象是文本形式 也就是字符串 后端会有特殊的语法把字符串的json转换成正常的json

'{
    "name" : "Jason";
    "age" : 123;
}'

前端把json变成字符串的json的方法

json是一种静态类

JSON.stringify(); json — > string

数据传输给后台json — > string

// 注意 属性名要加引号  虽然不加stringify转换时也会帮我们加上
var obj = {
    "name" : "abc",
    "age" : 123
}
var str = JSON.stringify(obj);

image-20221016064048642

JSON.parse(); string—>json

数据后台传输给前台string—>json

 JSON.parse(str)  // 把字符串形式的对象转变成正常的对象

image-20221016064319982

renderTree生成的过程

这个过程是同步加载 也就是domTree生成之后才会生成cssTree 然后两者再组成renderTree

domTree

浏览器中的渲染引擎会根据html结构和css样式一行一行的绘制页面

div {
    width:100px;
    height:100px;
    background-color:red;
}
<div></div>
<span></span>
<strong>
	<em></em>
</strong>

系统内核的内部是不指代div和div的样式之间的关系,不会让他俩产生连接,所以怎么产生联系呢?

浏览器的内核会对页面进行一步步检索,先识别html代码,会形成domTree

domTree符合深度优先原则 就是有下级就会就会把下级都先遍历完再遍历别的标签 (广度优先就是按照层级结构一层一层的遍历)

image-20221016072221339

注意:img等这种带文件的标签,只有系统看到了这个标签,就会先挂到domTree上 同时开启一个异步线程来下载对应的文件。

<div></div>
<img src="xxx">
<iframe src="xxx"></iframe>
<img src="yyy">
<span></span>
<strong></ strong>

domTreed的完成代表的是dom节点的解析完毕,不代表dom节点的加载完毕,下载完对应的文件才算加载完毕。

解析完一定比加载完更快

cssTree

domTree解析完毕后等cssTree解析

cssTree和domTree结构类似,也是深度优先。

renderTree

dmTree和cssTree都加载完毕之后会拼在一起会生成新的树renderTree

domTree + cssTree = renderTree

image-20221016074115495

浏览器绘制页面

只有当renderTree真正形成完了之后,渲染引擎才会真正的开始绘制页面

reflow 重排(重构)

重构产生原因

js可以动态的改变domTree和cssTree的一些东西,标签和样式都能间接修改。但是如果改了这两棵树的节点,renderTree就需要重新构建,renderTree重新构建就意味着页面会从第一行开始重新绘制,很浪费效率。

dom优化就是避免做一些无效的事儿,所以尽量减少dom节点的改动,非要做也尽量一次改完,重新再加载一次就好。

触发重构的情况(尽量避免)
  1. dom节点的删除,添加
  2. dom节点的宽高变化,位置变化,display none ——> block
  3. offsetWidth,offsetLeft (这是因为查看的时候 系统会重构一遍 确保查出来的宽高是实时的)

repaint 重绘

重绘的只是一部分,不是一整个页面。

给dom节点改变背景颜色,如果是基于css的颜色重新构建的话,只会把cssTree上的那一小部分改掉,然后改rennderTree上的对应那一小部分,影响比较小。

小知识

JS的下载过程和执行过程为什么不能和html和css并行去做(异步)

因为js会动态修改html和css,还没等你页面绘制完呢,js动态把html和css给改了,那不就又得重新绘制,所以js的下载过程和执行过程不能和html和css并行去做,所以要同步加载,要么执行完了再绘制,要么绘制完再执行。

JS为什么是单线程(同步加载)

因为JS可以修改页面,如果多线程(异步)的话,一个线程去删除节点,一个线程去添加一个节点,那无法决定到底听哪个线程的。

JS异步加载

js加载的缺点︰

加载工具方法没必要阻塞文档,因为js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作。

有些工具方法需要按需加载,用到再加载,不用不加载。

javascript异步加载的三种方案

defer 异步加载,但要等到dom文档全部解析完才会被执行。

js会和html和css一起并行的下载,各下载各的。

// 写上defer这个js就变成了异步加载
<script type="text/javascript" src="tools.js" defer="defer">
    //  defer内部可以写代码  async不可以
    <div></div>
</script>
async异步加载,加载完就执行async只能加载外部脚本不能把js写在script标签里。
// 写上async这个js就变成了异步加载
<script type="text/javascript" src="tools.js" async="async">
    // 不能在这内部写东西 只能加载外部脚本
</script>

asychronous javascript and xml ---> ajax(阿贾克斯的缩写)

上面两种方法执行时也不阻塞页面

创建script,插入到DOM中,加载完毕后callBack

由于想异步加载,又想把js写在script标签中,所以产生了这种方法。

// 不插入到DOM中也能看到network下载的html文件和demo.js(下载了 但是不展示 插入到dom中 也就是出现效果的时候会展示)
<script type="text/javascript">
    var script = document.createElement('script');
    script.type = "text/javascript";
    script.src="demo.js";
    // document.head.appendChild(script);
</script>
// demo.js
alert('张杰很帅')

中文名会乱码

image-20221016095420927

<script type="text/javascript">
    var script = document.createElement('script');
    script.type = "text/javascript";
    script.src="demo.js";
	// 插入到dom中就能看到效果
    document.head.appendChild(script); 
</script>

image-20221016095834757

image-20221016095911920

onload

提示我们外部js下载完了,可以调用了

没下载完就执行会报错

// demo.js
function test() {
    console.log('张杰');
}
<script type="text/javascript">
    var script = document.createElement('script');
    script.type = "text/javascript";
    script.src="demo.js";
    document.head.appendChild(script); 
	test();
</script>

报错是因为程序执行是非常快的,src还在请求demo.js资源的过程就已经执行到test()了,所以报错,因为demo.js还没下载完呢。

image-20221016100903693

onload兼容性非常好

<script type="text/javascript">
    var script = document.createElement('script');
    script.type = "text/javascript";
    script.src="demo.js";
	// 监听下载完成之后才会执行test();
	script.onload = function () {
        test();
    }
    document.head.appendChild(script); 
</script>

兼容IE的方法readyState的写法

<script type="text/javascript">
        var script = document.createElement('script');
        script.type = "text/javascript";
        script.src = "demo. js";
        if (script.readyState) { // //Ie独有的方法readyState
            script.onreadystatechange = function () {
                if (script.readyState == "complete" || script.readyState == "loaded" ) {
                    test();
                }
            }
        } else { // onload是非IE的方法
            script.onload = function () {//Safari chrome firefoxopera
                test();
            }
        }
</script>
封装兼容IE的函数
// demo.js
function test () {
    console.log('张杰')}
<script type="text/javascript">
    // src等着用户去传  callback回调函数 用户传了就会执行
    function loadScript(url,callback) {  
        var script = document.createElement('script');
        script.type = "text/javascript";
        if (script.readyState) { // Ie独有的方法readyState
            script.onreadystatechange = function () {
                if (script.readyState == "complete" || script.readyState == "loaded") {
                    callback();
                }
            }
        } else { // onload是非IE的方法
            script.onload = function () {
            callback();
           }
        }
        // src写在这儿是防止文件下载过快 所以先绑定事件 再加载文件  
        // 防止IE的onreadystatechange无法监听到change 而不执行循环
        script.src = url;  
        document.head.appendChild(script);
    }
    // 下载demo.js这个文件  执行test函数 但是会报错  
    // 因为代码解析执行到loadScript的时候不会看函数具体代码 
    // 会继续往下执行loadScript ('demo.js',test);  
	// 但是文件还没加载过来 所以test未定义 然后报错
    // loadScript ('demo.js',test); 

	// 通过函数引用解决 解析时也不会读函数体内的代码 只有执行时才会读
	loadScript ('demo.js',function () {
            test();  // 解析时不读 执行时才读
     }); 
</script>

image-20221016104438996

image-20221016104944800

上面报错解决办法二

// demo.js
var tools = {
    test : function () {
        console.log('a');
    },
    demo : function () {
        console.log('a');
    }
}
<script type="text/javascript">
        function loadScript(url,callback) { 
            var script = document.createElement('script');
            script.type = "text/javascript";
            if (script.readyState) {
                script.onreadystatechange = function () {
                    if (script.readyState == "complete" || script.readyState == "loaded") {
                        // 调用tools的test方法(callback是传入的参数)
                        tools[callback]();
                    }
                }
            } else { 
                script.onload = function () 
                	// 调用tools的test方法
                    tools[callback]();
                }
            }
            script.src = url; 
            document.head.appendChild(script);
        }
        // 以字符串形式传入
        loadScript ('demo.js',"test"); 
    </script>

JS加载时间线

浏览器运行页面的时候会初始化js的功能,当初始化初始完js功能开始,js发生它的作用的那一刻开始,开始记载着一系列浏览器接下来要发生的过程,每一步的顺序的事儿,这局势js加载时间线。

记录的就是执行顺序,顺序的起始点就是创建Document对象,有了Document才意味着js初始化完了

js时间线

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

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

  3. 遇到script外部js,并且没有设置async,defer,浏览器加载,并阻塞,等待js加载完成并执行该脚本,然后继续解析文档。
    没有设置异步加载,所以是同步解析,需要外部js加载完再继续解析。

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

    document.write()

    <div style="width:100px;height:100px;background-color:red;">
    </div>
    <script type="text/javascript">
        document.write('a'); // 这么写会把a文档流输入到页面中
    </script>
    

    image-20221016112111950

    <div style="width:100px;height:100px;background-color:red;">
    </div>
    <script type="text/javascript">
        window.onload = function () { // 页面加载完执行
        // 加载完之后再执行会把之前的文档流全清空  换上自己的内容 狠到把script都删了
        	document.write('a');
    	}
    </script>
    

    image-20221016112424265

    image-20221016112919650

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

  6. 当文档解析完成(domTree形成),document.readyState = “interactive”(interactive 活跃)

    <script type="text/javascript">
        // 打印的时候domTree还没构建完毕 所以打印最原始的loading
        console.log(document.readyState);
    	// 监听readyState改变
    	document.onreadystatechange = function () {
            // 打印 interactive 和 complete
        	console.log(document.readyState);
        }
    </script>
    

    image-20221016115625616

  7. 文档解析完成后(domTree形成),所有设置有defer的脚本会按照顺序执行。(注意与async的不同,但同样禁止使用document.write();

  8. document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段。

    <script type="text/javascript">
        console.log(document.readyState);
        document.onreadystatechange = function () {
            console.log(document.readyState);
        }
        document.onDOMContentLoaded = function () {
            console.log('a');
        }
        document.addEventListener('DOMContentLoaded', function () {
         // DOMContentLoaded只在addEventListener上绑定有效果
            console.log('a');
        }, false);
    </script>
    

    image-20221016120333514

  9. 当所有async的脚本加载完成并执行后、img等加载完成后,也就是所有都加载完了之后,document.readyState = ‘complete’,window对象触发load事件

    <script type="text/javascript">
        window.onload = function () {
        	console.log(document.readyState);
    	}
    </script>
    

    image-20221016115145395

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好好学习_fighting

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值