计算机代码中什么只能放在body里面不能放在script里面

JavaScript020

计算机代码中什么只能放在body里面不能放在script里面,第1张

计算机代码中什么只能放在body里面不能放在script里面,通常我们在开发html时,通常将script标签放到body标签下面,那原因到底是为什么呢?

js代码在载入完后,是立即执行的。

Document

var item = document.getElementById("item")

console.log(item)//null

我是html结构,我要展示到页面上!

通过这个简单的例子我们可以看到,js代码在加载完后,是立即执行的,执行完后,body才开始解析渲染,所以是找不到item,所以为null。

js代码执行时会阻塞页面渲染(由于GUI渲染线程和js引擎线程互斥。具体原理可以看这)

Document

while (true) {

console.log(1)

}

我是html结构,我要展示到页面上!

测试结果,浏览器一直处于加载过程,一会就卡死了。html整个结构(包括html标签)都无法渲染出来。js的下载和执行会阻塞Dom树的构建。

86250c123e53

1523429832(1).jpg

所以,我们一般采用以下方法

将所有的script标签放到页面底部,也就是body闭合标签之前,这能确保在脚本执行前页面已经完成了DOM树渲染。

如果放在上面,将js包裹在$(document).ready(function(){})或者$(function(){})里面

,或者window.οnlοad=function(){}里面。

具体这几句代码什么意思,可以看一下文章

ready和onload的区别

1.浏览器线程

浏览器有这么几大线程:UI渲染线程(用于页面的渲染),javascript引擎线程(用于处理js),GUI事件触发线程(用于交互)。

有时会开启的线程:http传输线程,定时触发线程(定时器)

它们之间的关系是什么呢?

(1)UI渲染线程 与 javascript引擎线程 互斥

由于javascript可以操纵页面的DOM,所以如果UI渲染线程与javascript引擎线程 不互斥的话,在UI渲染线程进行页面渲染的同时,javascript引擎线程进行DOM修改,最终会造成DOM状态不一致的现象。所以,当javascript引擎线程运行的时候,UI渲染线程处于冻结状态。

(2)javascript引擎线程 与 GUI事件触发线程(用于交互) 异步

浏览器开启事件触发线程,等待用户动作,事件触发线程解析为响应事件,转移到javascript引擎线程,排队等候,等待javascript引擎的处理。

(3)javascript引擎线程 与 http传输线程 异步

网页get,post等请求,xhr异步请求都通过http传输线程,传送到javascript引擎排队,等候处理。

(4)javascript引擎线程 与 定时触发线程(定时器) 异步

setTimeout(),setInterval()由单独的线程 定时触发线程 触发,传送到javascript引擎排队等候,等待处理。

上述的所有的异步操作有不同的浏览器分配线程执行,那个先执行完就先将那个加入到异步队列中,利用事件的轮询执行异步队列中的回调函数

react在16+的版本中使用fiber架构解决了很多的问题。

react在调用this.setState()的过程中使用的脏处理(从root节点开始调用),在以前的版本中就会导致卡顿,尽管有虚拟dom,diff算法,但是如果递归的足够深,如div-div一直有100个div,在重新渲染阶段会造成卡顿现象,如在渲染过程中不能触发点击事件等,也正是因为这个原因,fiber架构的出现就是为了解决递归过深,会造成无可避免的卡顿这个问题。

fiber架构采用了chrome浏览器的一个api requestIdleCallback(callback[, options]),在浏览器的空闲阶段调用,这里可能会扯很多浏览器的概念,由于这个api目前只支持谷歌,所以react也对此方法进行了重写。

浏览器的空闲阶段调用究竟是怎么回事呢?就必须要说一说浏览器了。

浏览器是一个多进程。Browser 进程,第三方插件进程,gpu进程,和重中之重浏览器内核。

下面我们说一下浏览器内核,一个进程可以有多个线程。那浏览器内核也不为过

如gui渲染线程,js引擎线程,定时器线程,事件触发线程,http请求线程。

我们经常会说gui渲染线程和js引擎线程互斥,因为js在浏览器中运行也可以操作dom,为了防止渲染出现问题,所以互斥,就说到这里。

浏览器在每一帧(时间片)都是在工作的,和我们人一样,上班都需要工作,只是在工作的时候没有活,我们可以偷懒一会。

一个看上去不卡的浏览器,必须需要1s中完成60帧,也就是16ms为一帧,当你的显卡更好的时候,肯定是不止60帧这个数值。但是这里就使用60帧,这个概念,所以说打游戏的时候fps低帧数低,每秒加载的帧数低就算网络不卡,你也会玩起来很卡。

每一帧

1.首先会先进行事件处理,如果有事件触发了且有回调函数,如触发点击事件,且有回调函数,会把回调函数放到eventLoop的宏任务队列中。

2.然后执行js,在js调用栈为空,先去执行宏任务队列最先执行的,再执行所有微任务队列里面的方法,执行完再去执行js调用栈,来回循环调用。

3.执行requestanimationframe,再重绘之前执行

4.解析html --先把html解析成dom树,构建渲染树,布局渲染树,绘制渲染树,这也是一个经常会出的面试题,重绘 -重排(重绘-回流)。

5.剩余时间片,好比人工作总会有没事的时候。requsetIdleCallback就是再剩余时间片执行,如果这个帧没有剩余时间片,那就下一个帧寻找是否有剩余时间片。

我们渲染阶段放到requestIdleCallback中,之前的递归必须递归到底,但是现在我可以放到每一个时间片之中,requestIdleCallback(workLoop,{timeout:1000}),再workloop方法中我再调用requestIdleCallback(workLoop,{timeout:1000}),这样每一帧的空闲时间我都会去调用workloop方法,这样我就不需要一次性递归到底,也就避免了造成卡顿。

下面我来说一说fiber是怎么实现的。

这里说的肯定不可能太具体,只能讲一点思路,下次写博客深入理解fiber机构。

如果了解过一点fiber的数据结构知道,fiber的数据结构中有

React.createElement是为了帮我们创建虚拟dom的。我们需要通过虚拟dom去创建对应的fiber。

componentDidmounted是从外到内执行的,好比先序遍历,而componentWillUnmounted是从里往外执行,后序遍历。

那么fiber调用的时候肯定就会出现这两个阶段。

在先序遍历的时候我们调用beginWork 根据先给fiber.stateNode=dom/new实例,在给其子节点创建fiber

在后续遍历中,我们调用completeUnitOfWork,完成effectlist,

effectlist 先调用根节点的firstEffect即d,然后一次nexteffect,即d-b-c-a

最后在commit阶段根据effectList,依次appendChild,removeChild,或者更新属性,完成渲染。