哈喽单车一面面经
- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
- 遇到异步任务, 进入Event Table并注册回调函数; 等到指定的事件完成时,Event Table会将这个回调函数移入Event Queue,如果是宏任务就加入宏任务队列,微任务加入微任务队列。
- 当栈中的代码执行完毕,执行栈(call stack)中的任务为空时,主线程会先检查micro-task(微任务)队列中是否有任务,如果有,就将micro-task(微任务)队列中的所有任务依次执行,直到micro-task(微任务)队列为空; 之后再检查macro-task(宏任务)队列中是否有任务,如果有,则取出第一个macro-task(宏任务)加入到执行栈中,之后再清空执行栈,检查micro-task(微任务)队列,以此循环,直到全部的任务都执行完成。
定时器代码:
setInterval(fn(), N); 复制代码
上面这句代码的意思其实是fn()将会在 N 秒之后被推入任务队列。
考虑极端情况,假如定时器里面的代码需要进行大量的计算(耗费时间较长),或者是 DOM 操作。这样一来,花的时间就比较长,有可能前一次代码还没有执行完,后一次代码就被添加到队列了。也会到时定时器变得不准确,甚至出现同一时间执行两次的情况。
最常见的出现的就是,当我们需要使用 ajax 轮询服务器是否有新数据时,必定会有一些人会使用 setInterval,然而无论网络状况如何,它都会去一遍又一遍的发送请求,最后的间隔时间可能和原定的时间有很大的出入。
setInterval 有两个缺点:
- 使用 setInterval 时,某些间隔会被跳过;
- 可能多个定时器会连续执行;
可以这么理解:每个 setTimeout 产生的任务会直接 push 到任务队列中;而 setInterval 在每次把任务 push 到任务队列前,都要进行一下判断(看上次的任务是否仍在队列中,如果有则不添加,没有则添加)。
因而我们一般用 setTimeout 模拟 setInterval,来规避掉上面的缺点。
具体实现如下:
1.写一个 interval 方法
let timer = null
function interval(func, wait){
let interv = function() {
func.call(null);
timer=setTimeout(interv, wait);
};
timer= setTimeout(interv, wait);
}
2.和 setInterval() 一样使用它
interval(function() {}, 20);
3.终止定时器
if (timer) { window.clearSetTimeout(timer); timer = null; }
- 避免使用table布局。
- 尽可能在DOM树的最末端改变class。
- 避免设置多层内联样式。
- 将动画效果应用到position属性为absolute或fixed的元素上。
- 避免使用CSS表达式(例如:calc())。
- 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
- 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
- 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
- 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
- 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
渲染流程主要有4个步骤
-
解析 HTML 生成DOM 树
-
解析 CSS 样式生成 CSSOM 树,CSSOM 树与 DOM 树结合生成 Render tree
-
布局 Render Tree 对每个节点进行布局处理,确定在屏幕上的位置
-
绘制 Render Tree,遍历渲染树将每个节点绘制出来
为了优化用户体验,渲染引擎不会等到 HTML 解析完才创建布局渲染树
从实现动画的复杂度来看,CSS 动画大多数都是补间动画,而 JS 动画是逐帧动画。当然这里我们不谈论实现的效果
第二点
编码的高效,采用 JS 去实现的动画,无论多简单的动画,都需要去控制整个过程,当然你可能会说可以采用一些库来解决这些问题,但是这些库的实际运行可能要比原生实现的效率要低的多
第三点
性能的高效,如果我们要操作一个元素向右移动,我们可能需要控制 dom.style.left 属性,每次来改变元素的位置,几何属性的改变必然会引起回流,回流必然引起重绘,可想而知如果我们采用 JS 来实现动画,这个代价有多大,这会造成浏览器在不断的计算页面,从而导致浏览器内存堆积。同时由于 JavaScript 运行在浏览器的主线程中,主线程中还有其他的重要任务在运行,因而可能会受到干扰导致线程阻塞,从而丢帧
而 CSS 的动画是运行在合成线程中的,不会阻塞主线程,并且在合成线程中完成的动作不会触发回流和重绘,当然还有一个重要的点:JS 动画运行在 CPU,而 CSS 动画运行在 GPU,总的来说, CSS动画的渲染成本小,并且它的执行效率高于 JavaScript 动画