前端知识点之BOM类
BOM Browser Object Model 浏览器对象模型
知识点:
--渲染机制类
——什么是DOCTYPE及其作用
——浏览器渲染过程
——重排(Reflow)
——重绘(Repaint)
--JS 运行机制
——如何理解 JS 的单线程
——什么是任务队列
——什么是Event Loop
--页面性能
——原则
——加载资源优化
——渲染优化
--错误监控
一、渲染机制类
(1)什么是DOCTYPE及其作用
<!DOCTYPE> 声明必须是 HTML 文档的第一行,位于 <html> 标签之前。
<!DOCTYPE> 声明不是 HTML 标签;它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令。
在 HTML 4.01 中,<!DOCTYPE> 声明引用 DTD,因为 HTML 4.01 基于 SGML。DTD 规定了标记语言的规则,这样浏览器才能正确地呈现内容。
HTML5 不基于 SGML,所以不需要引用 DTD。
提示:请始终向 HTML 文档添加 <!DOCTYPE> 声明,这样浏览器才能获知文档类型。
摘自 w3school
DTD:* document type definition,文档类型定义,通俗来说,DTD就是告诉浏览器我是什么文档类型
常见有三种写法:
- HTML 4.01 Strict 严格模式, 该 DTD 包含所有 HTML 元素和属性,但不包括展示性和弃用的元素,比如 font
- HTML 4.01 Transitional 传统模式, 该 DTD 包含所有 HTML 的元素和属性,包括展示性和弃用的元素,比如 font
- HTML 5
目前主要使用 HTML 5
// HTML5:
<!Doctype html>
// HTML 4.01 Strict
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
// HTML 4.01 Transitional
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
(2)浏览器渲染过程
HTML => HTML 解析 => DOM Tree (DOM 树)
CSS => CSS 解析 => CSS Rules (CSSOM)
DOM Tree + CSS Rules => Render tree (渲染树) => Painting(绘画) => Display (显示在页面上)
(3)重排(Reflow)
定义:DOM 结构中的各个元素都有自己的盒子(模型),这些多需要浏览器根据各种样式来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为 reflow
触发 Reflow
- 当你增加、删除、修改 DOM 节点时,会导致 Reflow 或 Repaint
- 当你移动 DOM 的位置,或是搞个动画的时候
- 当你修改 CSS 样式的时候
- 当你 Resize 窗口的时候,或者滚动的时候有可能触发 Reflow
- 当你修改网页的默认字体时
(4)重绘(Repaint)
定义:当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制一遍,于是页面的内容出现了,这个过程称之为repaint
触发 Repaint
- DOM 改动
- CSS 改动
如何减少 repaint 的次数
repaint 是无法避免的,但是要减少 repaint 的次数,比如,页面需要插入一个列表节点,要尽可能的减少操作dom的次数
// 不妥当的写法
<ul id="list"></ul>
<script>
var books = ['js','php','html5'],
list = document.querySelector('#list')
for (var i = 0; i < books.length; i++) {
const oLi = document.createElement('li')
oLi.innerHTML = books[i]
list.appendChild(oLi)
}
</script>
// 正确的写法
<ul id="list"></ul>
<script>
var books = ['js','php','html5'],
list = document.querySelector('#list'),
frag = document.createDocumentFragment()
for (var i = 0; i < books.length; i++) {
const oLi = document.createElement('li')
oLi.innerHTML = books[i]
frag.appendChild(oLi)
}
list.appendChild(frag)
</script>
例子:面试题:从输入 URL 到得到 html 页面的整个过程
- 浏览器根据 DNS 服务器得到域名的 IP 地址
- 想找个 IP 地址的机器发送 http 请求
- 服务器收到、处理并返回 http 请求
- 浏览器得到返回内容
二、JS 运行机制
console.log(1)
setTimeout(function (){
console.log(2)
},0)
console.log(3)
// 结果:1 3 2
结果:1 3 2,原因:console.log 属于同步任务,setTimeout 属于异步任务,同步任务先于异步任务执行
如何理解 JS 的单线程
一个时间内,JS 只能干一件事,这就是单线程
JS 为什么是单线程呢?
JS最初被设计用在浏览器中,那么想象一下,如果浏览器中的JS是多线程的。
场景描述:
那么现在有2个线程,process1 process2,由于是多线程的JS,所以他们对同一个dom,同时进行操作
process1 删除了该dom,而process2 编辑了该dom,同时下达2个矛盾的命令,浏览器究竟该如何执行呢?
这样想,JS为什么被设计成单线程应该就容易理解了吧。
什么是任务队列
分为同步任务和异步任务,也就是说, setTimeout里的函数并没有立即执行,而是延迟了一段时间,满足一定条件后,才去执行的,这类代码,我们叫异步代码,而 console.log 属于同步代码
常见异步任务:
- setTimeout
- setInterval
- ajax 请求、动态 <img> 加载
- DOM 事件
- ES6 中的 Promise
什么是Event Loop
首先判断JS是同步还是异步,同步就进入主线程,异步就进入event table
异步任务在event table中注册函数,当满足触发条件后,被推入event queue
同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主线程中
以上三步循环执行,这就是event loop
所以上面的例子,你是否可以描述它的执行顺序了呢?
console.log(1) 是同步任务,放入主线程里
setTimeout() 是异步任务,被放入event table, 0秒之后被推入event queue(事件队列)里
console.log(3 是同步任务,放到主线程里
当 1、 3在控制条被打印后,主线程去event queue里查看是否有可执行的函数,执行setTimeout里的函数
三、页面性能
原则:
- 多使用内存、缓存或者其他方法
- 减少 CPU 计算,减少 网络
1. 加载资源优化
-
静态资源的压缩合并
一般通过构建工具合并 -
静态资源的缓存
可以通过链接的名字来缓存(比较麻烦)或者 路径后面加 js 的版本号
<script src="index.js?v=20190101"></script>
// 更新发布文件后
<script src="index.js?v=20190102"></script>
-
使用 CDN 让资源加载更快
-
使用 SSR 后端渲染,数据直接输出到 HTML 中
2. 渲染优化
- CSS 放前面,JS 放后面
为什么一般要把 CSS 放 head 中?
假设 把 css 放在了 DOM 结构的后面,那么页面渲染的时候会按照浏览器默认的样式对 DOM 进行了渲染,加载完成了 CSS 之后又再次重排/重绘,所以把 CSS 放在 head 中可以减少页面的 reflow/repaint
为什么一般要把 JS 放 body 内的最后面?
1.JS的加载会阻塞其它内容的加载,就是页面要等待JS下载完成,解析运行完成才能继续加载其它内容,JS放在页面开始,如果网速慢或JS复杂,会造成长时间页面空白,用户体验不好。
2.JS一般会对DOM树进行操作,放在页首,DOM元素还没有加载完,一般还是要在 onload 等事件中运行JS,放在页尾,DOM树已经完成,可以直接运行
-
懒加载,(图片懒加载,下拉加载更多)
-
减少 DOM 查询,对 DOM 查询做缓存
// 没做 DOM 查询缓存
for (var i = 0; i < document.getElementsByTagName('p').length; i++) {
// ...
}
// DOM 查询缓存
var pList = document.getElementsByTagName('p'),
lens = pList.length
for (var i = 0; i < lens; i++) {
// ...
}
-
减少 DOM 操作,对多个操作尽量合并在一起执行
合并 dom 插入,具体实现,查看上面的 重绘(Repaint) 部分 -
事件节流
通过定时器 setTimeout 来实现,在输入框change、移动端手指移动 mousemove等事件,可通过 setTimeout 来减少触发
<input type="text" id="input-text">
<script>
var text = document.getElementById('input-text'),
timer = null
text.addEventListener('keyup',function (){
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function (){
console.log(text.value)
},500)
})
</script>
- 尽早执行操作,比如 DOMContentLoaded
window.onload: *页面的全部资源加载完才会执行,包括图片、视频等
DOMContentLoaded: DOM 结构加载完就可以执行,此时图片、视频还没加载完
window.addEventListener('load', function (){
console.log('load')
})
window.addEventListener('DOMContentLoaded', function (){
console.log('DOMContentLoaded')
})
// 结果:
// DOMContentLoaded
// load
四、错误监控
以上为学习笔记,
参考资料:https://segmentfault.com/a/1190000012806637
参考资料:慕课网的课程