浏览器渲染机制/浏览器渲染过程
1. 创建/更新DOM树和请求css/image/js: 浏览器请求HTML代码后 在生成DOM树最开始的阶段(字节 -> 字符) 并行发起css/image/js请求
3. 创建/更新 渲染树: 所有CSS文件下载完成后 CSSOM树构建结束和DOM树一起生成渲染树
5. 绘画: 根据布局 按照算出来的规则 通过显卡 把内容画到屏幕上
以上五个步骤 前三步都有创建/更新是因为DOM/CSSOM/渲染树可能在第一次绘画后多次更新 比如JS修改了DOM/CSS 布局和绘画也会被重复执行 除了DOM/CSSOM更新原因外 图片下载完成后也需要调用布局和绘画来更新网页
解析HTML文件的过程
2. 遇到link外部的css,创建线程加载,并继续解析文档。
4. 遇到script外部的js,并且设置async/defere,浏览器创建线程加载,并继续解析文档。对于async属性的脚本,脚本加载完成后立即执行。(异步禁止使用document.write())
6. 当文档解析完成后,document.readyState = 'interactive' // domTree刚建立
8. document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段 // 可以监听用户的输入事件/监听事件。
10. 从此,以异步响应方式处理用户输入,网络事件等
分三步
1.创建Document对象
2.解析文档
3.文档加载完并执行完
思考解析HTML文件中引发的问题?
1. 加载JS和CSS会阻塞浏览器的渲染吗 下载JS和CSS会阻塞吗
2. js加载会阻塞dom的解析吗,js加载会阻塞dom的渲染吗 ?
3. css加载会阻塞dom的解析吗,css加载会阻塞dom的渲染吗 ?
4. 为什么生成了CSSOM树这样的结构?
加载JS和CSS会阻塞浏览器的渲染吗 下载JS和CSS会阻塞吗
下载JS和CSS不会阻塞浏览器渲染 因为加载有个下载和解析的过程 而下载只是下载这个文件 不会读取里面的内容
js加载会阻塞dom的解析吗,js加载会阻塞dom的渲染吗 ?
css加载会阻塞dom的解析吗,css加载会阻塞dom的渲染吗 ?
2. css页面后面还有外部js文件 css是阻塞了dom的解析的 这是主要是因为css后面还有js 考虑到js可能会改变css属性 所有必须等到它前面的css执行完毕
img会阻塞dom的解析吗,img会阻塞dom的渲染吗 ?
1、没有外部css文件 img对dom的解析没有阻塞 但是会阻塞dom的渲染(而所有通过src属性导入的元素,都会导致页面渲染的堵塞。)
2、有外部css文件
为什么生成了CSSOM树这样的结构?
浏览器的优化方式/性能优化?
前端优化的途径有很多,大致可以分为两类:页面级优化和代码级优化。
代码级优化有DOM操作优化、CSS选择符优化、图片优化以及HTML结构优化等等。
一丶页面级优化
1. 减少HTTP请求数(最重要最有效)
一个完整的请求都需要DNS寻址丶与服务器建立连接丶发送数据丶等待服务器响应丶接受数据这样一个漫长而复杂的过程 由于浏览器进行并发请求的请求数都是有上限的 因此请求数多了以后 浏览器需要分批进行请求 因此会增加用户的等待时间 会给用户造成网站速度慢这样的一个印象 即使可能用户能看到的第一屏的资源都已经请求完了 但是浏览器的进度条一直在加载
(1)从设计实现层面简化页面
如果你的页面和百度搜索的页面一样简单,那么也就不需要什么优化操作了。因此保持页面简洁、减少资源的使用是最直接的。
(2)合理设置HTTP缓存
缓存的力量是强大的,恰当的设置缓存可以大大减少HTTP请求
怎样才算是合理的设置?原则很简单:能缓存越多越久越好。
例如:很少变化的图片资源就可以直接通过HTTP Header中的Expires设置一个很长的过期头;变化不频繁而又可能会变的资源可以使用Last-Modified来做请求验证。尽可能的让资源能够在缓存中待的更久。
(3)资源合并与压缩
如果可以的话,尽可能的将外部脚本、样式进行合并,尽可能地合并为一个。另外,CSS、JS、Image都可以用相应的工具进行压缩,压缩后往往能节省不少空间。或者使用Webpack等前端工程化工具来进行代码的压缩和去重。
(4)使用雪碧图
雪碧图又叫做精灵图,我们可以把网站中需要用到的一些icon,全部放到一个图片资源中,然后通过改变位置来获取需要的图片,这样合并CSS图片,就可以大幅度减少HTTP请求数了。
(5)内联图片使用
data: URL scheme的方式将图片嵌入到页面或 CSS中,如果不考虑资源管理上的问题的话,不失为一个好办法。如果是嵌入页面的话换来的是增大了页面的体积,而且无法利用浏览器缓存。使用在 CSS中的图片则更为理想一些。
(6) 懒加载
这条策略实际上并不一定能减少 HTTP请求数,但是却能在某些条件下或者页面刚加载时减少 HTTP请求数。对于图片而言,在页面刚加载的时候可以只加载第一屏,当用户继续往后滚屏的时候才加载后续的图片。这样一来,假如用户只对第一屏的内容感兴趣时,那剩余的图片请求就都节省了。
首页 曾经的做法是在加载的时候把第一屏之后的图片地址缓存在 Textarea标签中,待用户往下滚屏的时候才 “惰性” 加载。
(6)瀑布流
其实懒加载并不能减少HTTP请求数,他只是可以减少页面刚加载的时候的HTTP请求数,总数是不变的。对于图片而言,在页面刚加载的时候可能只加载第一屏的图片,随着用户的滚动才会继续加载后面的图片资源,这种瀑布流的加载方式就可以有效提高性能。
2. 将外部脚本放在底部
前文有谈到,浏览器是可以并发请求的,这一特点使得其能够更快的加载资源,然而外链脚本在加载时却会阻塞其他资源,例如在脚本加载完成之前,它后面的图片、样式以及其他脚本都处于阻塞状态,直到脚本加载完成后才会开始加载。如果将脚本放在比较靠前的位置,则会影响整个页面的加载速度从而影响用户体验。解决这一问题的方法有很多,而最简单可依赖的方法就是将脚本尽可能的往后挪,减少对并发下载的影响。
3. 并发执行内联脚本
使用 script元素的defer 属性(存在兼容性问题和其他一些问题,例如不能使用document.write()、使用setTimeout ,此外,在HTML5中引入了 Web Workers的机制,恰恰可以解决此类问题。
4. 懒加载
只需要在需要资源的时候才加载资源,不需要的时候就不加载资源。
5. 将CSS放在 HEAD中
如果将 CSS放在其他地方比如 BODY中,则浏览器有可能还未下载和解析到 CSS就已经开始渲染页面了,这就导致页面由无 CSS状态跳转到 CSS状态,用户体验比较糟糕。除此之外,有些浏览器会在 CSS下载完成后才开始渲染页面,如果 CSS放在靠下的位置则会导致浏览器将渲染时间推迟。
6. 把js放到整个页面的底部,另一个方法是在script 标签上加一个 defer属性 保证让浏览器把脚本下载出来后,然后等到页面渲染完毕再执行。(参考权威指南 js时间线)
7. CSS文件用link标签加载 是阻塞状态的,我们可以使用loadCss小工具库来进行异步css文件加载,但是有一个问题是,全部异步加载css,页面最开始呈现出的只是单纯的html ,不是很好看 所以我们要选定一个关键的css文件,用critical工具来自动提取压缩关键的css. (百度自行查看使用方法)
8. 减少不必要的HTTP跳转
对于以目录形式访问的HTTP链接,很多人都会忽略链接最后是否带’/’,假如你的服务器对此区别对待的话,那么你也需要注意了,这其中很可能隐藏了301跳转,增加了多余请求。
9. 避免重复的资源请求
这种情况主要是由于在模块化开发时,我们的不同模块之间可能有相同的部分,导致资源的重复请求
二丶 代码级优化
1. DOM
DOM操作应该是脚本中最耗性能的一类操作
(1)HTMLCollection(HTML收集器,返回的是一个数组的内容信息)因为是这个集合并不是一个静态的集合,它表示的仅仅是一个特定的查询,每次访问该集合时都会重新执行这个 查询从而更新查询结果。所谓的“访问集合”包括读取集合的length属性、访问集合中的元素
(2)Reflow&Repaint
减少页面的重绘和重排。
2. 慎用with
with会改变作用域链,有可能导致我们的作用域链变长,导致查询性能下降。
每次 eval 或 Function 构造函数作用于字符串表示的源代码时,脚本引擎都需要将源代码转换成可执行代码。这是很消耗资源的操作—— 通常比简单的函数调用慢 100倍以上。
4. 减少作用域链查找
如果在循环中需要访问非本作用域下的变量的时候,请遍历之前用局部变量缓存的变量,并在遍历结束之后重写这个缓存变量
js中对直接量和局部变量的访问时最快的,对对象属性以及数组的访问需要更大的开销,当出现下面的情况的时候,建议将数据放入局部变量:
(1)对任何对象属性的访问超过1次
(2)对任何数组成员的访问次数超过1次
另外,要尽可能的减少对对象以及数组的深度查找。
6. 字符串拼接
字符串的拼接尽可能少的使用“+”,这种方式的效率是十分低下的,因为每次运行都会开辟新的内存并生成新的字符串变量,然后将拼接的结果赋值给新变量。
建议使用的是先转化为数组,然后通过数组的join方法来连接成字符串。不过由于数组也有一定的开销,因此就需要权衡一下,当拼接的字符串比较少的时候,可以考虑用“+”的方式,比较多的时候就需要考虑用数组的join方法了。
重绘和重排:页面有三个树:DOMTree、CSSTree、renderTree。(实际上多于三个),renderTree上有两个规则:repaint和reflow,重绘和重排。
重绘(repaint)是元素自身的位置和宽高不变,只改变颜色的之类的属性而不会导致后面的元素位置的变化的时候,renderTree发生的动作。
重排(reflow)是元素自身的位置或者宽高改变了从而导致的整个页面的大范围移动的时候,renderTree发生的动作。所以我们在DOM操作的时候,要尽量避免重排。
浏览器缓存
1. cookie
cookie是由服务端生成的 发送给User-Agent(一般是浏览器) (服务器告诉浏览器设置一下cookie) 浏览器会将cookie以key/value形式保存到某个目录下的文本文件内 下次请求同一网站时就发送该cookie给服务器(前提是浏览器设置为启动cookie)
为什么会有cookie?
Web应用程序是使用HTTP协议传输数据的 HTTP协议是无状态的协议 一旦数据交换完毕 客户端和服务端的连接就会关闭 再次交换数据需要建立新的连接 这就意味着服务器无法从连接上跟踪会话
cookie的特点?
cookie有保质期
满足同源策略 (不同域的话可以在服务端设置document.domain或path来实现共享)
cookie内存大小受限制(cookie有个数和大小的限制 大小一般是4K)
注意?
// 自己封装设置cookie的代码
var manageCookie = { setCookie: function(name, value, time) { document.cookie = name + '=' + value + ';max-age' + time; return this; }, removeCookie: function(name) { return this.setCookie(name, '', -1); }, getCookie: function(name, callback) { var allCookieArr = document.cookie.split('; '); for(var i = 0; i < allCookieArr.length; i++) { var itemCookieArr = allCookieArr[i].split('='); if(itemCookieArr[0] === name) { callback(itemCookieArr[1]); return this; } } callback(undefined); return this; }}复制代码
2. http缓存
即针对文件的重复请求情况下 浏览器可以根据协议头判断从服务器端请求文件还是从本地读取文件 chrome控制台下的Frames即展示的是浏览器的http文件级缓存
以下是浏览器缓存的这个机制流程 主要针对重复的http请求 在有缓存的情况下判断过程主要分为3步
判断expires 如果未过期 直接读取http缓存文件 不发http请求 否则进入下一步
判断是否含有etag 有则带上if-none-match发送请求 未修改返回304 修改返回200 否则进入下一步
判断是否含有last-modified 有则带上if-modifined-since发送请求 无效返回200 有效返回304 否则直接向服务器请求
如果通过etag和last-modifined判断 即使返回304有至少有一次http请求 只不过返回的是304的返回内容 而不是文件内容 所以合理设置实现expires参数可以减少较多的浏览器请求
3. localstorage
常用API
localStorage.setItem(key, value); // 设置记录
localStorage.getItem(key); // 获得记录
localStorage.removeItem(key); // 删除该域名下单条记录
注意: localStorage大小有限制 不适合存放过多的数据 如果数据存放超过最大限制会报错 并移除最先保存的数据
4. sesstionStorage
sesssionStorage和localStorage类似 但是浏览器关闭则会全部关闭 api和localStorage相同
.......未完待续
你的点赞是我持续输出的动力 希望能帮助到大家 互相学习 有任何问题下面留言 一定回复