浏览器
浏览器
参考答案:
Trident 内核:IE, MaxThon, TT, The World, 360, 搜狗浏览器等。[又称 MSHTML]
Gecko 内核:Netscape6 及以上版本,FF, MozillaSuite/SeaMonkey 等
Presto 内核:Opera7 及以上。 [Opera 内核原为:Presto,现为:Blink; ]
Webkit 内核:Safari, Chrome 等。 [ Chrome 的:Blink(WebKit 的分支)]
参考答案:调用 localstorge、cookies 等本地存储方式,注意sessionstorge不可以哦
参考答案:
- 解析HTML生成DOM树。
- 解析CSS生成CSSOM规则树。
- 将DOM树与CSSOM规则树合并在一起生成渲染树。
- 遍历渲染树开始布局,计算每个节点的位置大小信息。
- 将渲染树每个节点绘制到屏幕。
解析:
- 使用 HTML 创建文档对象模型(DOM)
- 使用 CSS 创建 CSS 对象模型(CSSOM)
- 基于 DOM 和 CSSOM 执行脚本(Scripts)
- 合并 DOM 和 CSSOM 形成渲染树(Render Tree)
- 使用渲染树布局(Layout)所有元素
- 渲染(Paint)所有元素
参考答案:
- 同一产品,版本越老 bug 越多
- 同一产品,版本越新,功能越多
- 不同产品,不同标准,不同实现方式
处理兼容问题的思路
- 要不要做
- 产品的角度(产品的受众、受众的浏览器比例、效果优先还是基本功能优先)
- 成本的角度 (有无必要做某件事)
- 做到什么程度
- 让哪些浏览器支持哪些效果
- 如何做
根据兼容需求选择技术框架/库(jquery)
根据兼容需求选择兼容工具(html5shiv.js、respond.js、css reset、normalize.css、Modernizr)
条件注释、CSS Hack、js 能力检测做一些修补
渐进增强(progressive enhancement): 针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验
优雅降级 (graceful degradation): 一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
解析:参考
参考答案:
(1) BroadCast Channel
(2) Service Worker
(3) LocalStorage + window.onstorage监听
(4) Shared Worker + 定时器轮询(setInterval)
(5) IndexedDB + 定时器轮询(setInterval)
(6) cookie + 定时器轮询(setInterval)
(7) window.open + window.postMessage
(8) Websocket
参考答案:
状态 类型 说明 200 form memory cache 不请求网络资源,资源在内存当中 200 form disk ceche 不请求网络资源,在磁盘当中 200 资源大小数值 从服务器下载最新资源 304 报文大小 请求服务端发现资源没有更新,使用本地资源
一、前言
缓存可以说是性能优化中简单高效的一种优化方式了。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。
对于一个数据请求来说,可以分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。比如说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据。
接下来的内容中我们将通过缓存位置、缓存策略以及实际场景应用缓存策略来探讨浏览器缓存机制。
二、缓存位置
从缓存位置上来说分为四种,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。
Service Worker
Memory Cache
Disk Cache
Push Cache
Service Worker
Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。
当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容。
- Memory Cache
Memory Cache 也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源, 例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快, 内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。
那么既然内存缓存这么高效,我们是不是能让数据都存放在内存中呢? 这是不可能的。计算机中的内存一定比硬盘容量小得多,操作系统需要精打细算内存的使用,所以能让我们使用的内存必然不多。
当我们访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存
内存缓存中有一块重要的缓存资源是preloader相关指令(例如 )下载的资源。总所周知preloader的相关指令已经是页面优化的常见手段之一,它可以一边解析js/css文件,一边网络请求下一个资源。
需要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control是什么值,同时资源的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type,CORS等其他特征做校验。
- Disk Cache
Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。
在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Header 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自 Disk Cache,关于 HTTP 的协议头中的缓存字段,我们会在下文进行详细介绍。
浏览器会把哪些文件丢进内存中?哪些丢进硬盘中? 关于这点,网上说法不一,不过以下观点比较靠得住:
对于大文件来说,大概率是不存储在内存中的,反之优先 当前系统内存使用率高的话,文件优先存储进硬盘
- Push Cache
Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
Push Cache 在国内能够查到的资料很少,也是因为 HTTP/2 在国内不够普及。这里推荐阅读Jake Archibald的 HTTP/2 push is tougher than I thought 这篇文章,文章中的几个结论: - 所有的资源都能被推送,并且能够被缓存, 但是 Edge 和 Safari 浏览器支持相对比较差 - 可以推送 no-cache 和 no-store 的资源 - 一旦连接被关闭,Push Cache 就被释放 - 多个页面可以使用同一个HTTP/2的连接,也就可以使用同一个Push Cache。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签使用同一个HTTP连接。 - Push Cache 中的缓存只能被使用一次 - 浏览器可以拒绝接受已经存在的资源推送 - 你可以给其他域名推送资源
如果以上四种缓存都没有命中的话,那么只能发起请求来获取资源了。
那么为了性能上的考虑,大部分的接口都应该选择好缓存策略,通常浏览器缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的。
解析:参考
参考答案:
CSS选择器的解析是从右向左解析的。若从左向右的匹配,发现不符合规则,需要进行回溯,会损失很多性能。若从右向左匹配,先找到所有的最右节点,对于每一个节点,向上寻找其父节点直到找到根元素或满足条件的匹配规则,则结束这个分支的遍历。两种匹配规则的性能差别很大,是因为从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点(叶子节点),而从左向右的匹配规则的性能都浪费在了失败的查找上面。 而在 CSS 解析完毕后,需要将解析的结果与 DOM Tree 的内容一起进行分析建立一棵 Render Tree,最终用来进行绘图。在建立 Render Tree 时(WebKit 中的「Attachment」过程),浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果(Style Rules)来确定生成怎样的 Render Tree。
参考答案:
① 浏览器将获取的HTML文档解析成DOM树
② 处理CSS标记,构成层叠样式表模型CSSOM(CSS Object Model)
③ 将DOM和CSSOM合并为渲染树(rendering tree)将会被创建,代表一系列将被渲染的对象
④ 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次pass绘制操作就可以布局所有的元素
⑤ 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting
图形说明:
解析:参考
参考答案:
1. 用户界面:
用户界面主要包括:地址栏,后退/前进按钮,书签目录等;(除了从服务器请求到的网页窗口)
2. 浏览器引擎:
用来查询及操作渲染引擎的接口;
3. 渲染引擎:
用来显示请求的html内容;(包括样式,图片,js)
4. 网络:
主要是来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作;
5. UI后端:
用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口。
6. JS解释器 :
用来解释执行JS代码;
7. 数据存储:
属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术;
参考答案:
参考答案:
众所周知,想要提高系统的性能,缓存是最直接也是最简单的方法之一。缓存一方面可以减少数据库负载,另一方面还可以减少相应时间并且节省成本。今天,小编将向大家介绍几种比较常见的缓存策略,即Cache-Aside、Read-Though Cache、Write-Through Cache、Write-Around和Write-Back。下面让我们一起对比分析一下常用缓存策略的优劣以及使用场景吧!
解析:参考
参考答案:过期机制、验证机制
解析:
对于浏览器的缓存来讲,这些规则是在HTTP协议头部和HTML页面的Meta标签中定义的。他们分别从新鲜度和校验值两个维度来规定浏览器是否可以直接使用缓存中的副本,还是需要去源服务器获取新版本。
过期机制:指的是缓存副本的有效期。一个缓存的副本必须满足以下条件,浏览器会认为它是有效的,足够新的:
- 含有完整的过期时间控制头信息(HTTP协议报头),并且仍在有效期内
- 浏览器已经使用过这个缓存的副本,并且会在一个会话中已经检查过新鲜度(即服务器上的资源是否发生改变) 满足以上两种情况的一种,浏览器会直接从缓存中获取副本进行渲染
校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签Etag(Entity Tag), 它可以用来作为浏览器再次请求过程中的校验标识,如果发现校验标识不匹配,说明资源已经被修改或者过期,浏览器需要重新获取资源内容。
参考答案:
a. IE 的排版引擎是 Trident (又称为 MSHTML)
b. Trident 内核曾经几乎与 W3C 标准脱节(2005 年)
c. Trident 内核的大量 Bug 等安全性问题没有得到及时解决
d. JS 方面,有很多独立的方法,例如绑定事件的 attachEvent、创建事件的 createEventObject 等
e. CSS 方面,也有自己独有的处理方式,例如设置透明,低版本 IE 中使用滤镜的方式
参考答案:
浏览器的主要功能是将用户选择的 web 资源呈现出来,它需要从服务器请求资源,并将其显示在浏览器窗口中,资源的格式通常
是 HTML,也包括 PDF、image 及其他格式。用户用 URI(Uniform Resource Identifier 统一资源标识符)来指定所请
求资源的位置。
HTML 和 CSS 规范中规定了浏览器解释 html 文档的方式,由 W3C 组织对这些规范进行维护,W3C 是负责制定 web 标准的
组织。
但是浏览器厂商纷纷开发自己的扩展,对规范的遵循并不完善,这为 web 开发者带来了严重的兼容性问题。
简单来说浏览器可以分为两部分,shell 和 内核。
其中 shell 的种类相对比较多,内核则比较少。shell 是指浏览器的外壳:例如菜单,工具栏等。主要是提供给用户界面操作,
参数设置等等。它是调用内核来实现各种功能的。内核才是浏览器的核心。内核是基于标记语言显示内容的程序或模块。也有一些
浏览器并不区分外壳和内核。从 Mozilla 将 Gecko 独立出来后,才有了外壳和内核的明确划分。
参考答案:
主要分成两部分:渲染引擎和 JS 引擎。
渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。默认情况下,渲染引擎可以显示 html、xml 文档及图片,它也
可以借助插件(一种浏览器扩展)显示其他类型数据,例如使用 PDF 阅读器插件,可以显示 PDF 格式。
JS 引擎:解析和执行 javascript 来实现网页的动态效果。
最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。
参考答案:
Trident:这种浏览器内核是 IE 浏览器用的内核,因为在早期 IE 占有大量的市场份额,所以这种内核比较流行,以前有很多
网页也是根据这个内核的标准来编写的,但是实际上这个内核对真正的网页标准支持不是很好。但是由于 IE 的高市场占有率,微
软也很长时间没有更新 Trident 内核,就导致了 Trident 内核和 W3C 标准脱节。还有就是 Trident 内核的大量 Bug 等
安全问题没有得到解决,加上一些专家学者公开自己认为 IE 浏览器不安全的观点,使很多用户开始转向其他浏览器。
Gecko:这是 Firefox 和 Flock 所采用的内核,这个内核的优点就是功能强大、丰富,可以支持很多复杂网页效果和浏览器扩
展接口,但是代价是也显而易见就是要消耗很多的资源,比如内存。
Presto:Opera 曾经采用的就是 Presto 内核,Presto 内核被称为公认的浏览网页速度最快的内核,这得益于它在开发时的
天生优势,在处理 JS 脚本等脚本语言时,会比其他的内核快3倍左右,缺点就是为了达到很快的速度而丢掉了一部分网页兼容性。
Webkit:Webkit 是 Safari 采用的内核,它的优点就是网页浏览速度较快,虽然不及 Presto 但是也胜于 Gecko 和 Trid
ent,缺点是对于网页代码的容错性不高,也就是说对网页代码的兼容性较低,会使一些编写不标准的网页无法正确显示。WebKit
前身是 KDE 小组的 KHTML 引擎,可以说 WebKit 是 KHTML 的一个开源的分支。
Blink:谷歌在 Chromium Blog 上发表博客,称将与苹果的开源浏览器核心 Webkit 分道扬镳,在 Chromium 项目中研发 B
link 渲染引擎(即浏览器核心),内置于 Chrome 浏览器之中。其实 Blink 引擎就是 Webkit 的一个分支,就像 webkit 是
KHTML 的分支一样。Blink 引擎现在是谷歌公司与 Opera Software 共同研发,上面提到过的,Opera 弃用了自己的 Presto
内核,加入 Google 阵营,跟随谷歌一起研发 Blink。
详细的资料可以参考: 《浏览器内核的解析和对比》《五大主流浏览器内核的源起以及国内各大浏览器内核总结》
参考答案:
(1) IE 浏览器内核:Trident 内核,也是俗称的 IE 内核;
(2) Chrome 浏览器内核:统称为 Chromium 内核或 Chrome 内核,以前是 Webkit 内核,现在是 Blink内核;
(3) Firefox 浏览器内核:Gecko 内核,俗称 Firefox 内核;
(4) Safari 浏览器内核:Webkit 内核;
(5) Opera 浏览器内核:最初是自己的 Presto 内核,后来加入谷歌大军,从 Webkit 又到了 Blink 内核;
(6) 360浏览器、猎豹浏览器内核:IE + Chrome 双内核;
(7) 搜狗、遨游、QQ 浏览器内核:Trident(兼容模式)+ Webkit(高速模式);
(8) 百度浏览器、世界之窗内核:IE 内核;
(9) 2345浏览器内核:好像以前是 IE 内核,现在也是 IE + Chrome 双内核了;
(10)UC 浏览器内核:这个众口不一,UC 说是他们自己研发的 U3 内核,但好像还是基于 Webkit 和 Trident ,还有说
是基于火狐内核。
参考答案:
(1)首先解析收到的文档,根据文档定义构建一棵 DOM 树,DOM 树是由 DOM 元素及属性节点组成的。
(2)然后对 CSS 进行解析,生成 CSSOM 规则树。
(3)根据 DOM 树和 CSSOM 规则树构建渲染树。渲染树的节点被称为渲染对象,渲染对象是一个包含有颜色和大小等属性的矩形,渲染对象和 DOM 元素相对应,但这种对应关系不是一对一的,不可见的 DOM 元素不会被插入渲染树。还有一些 DOM 元素对应几个可见对象,它们一般是一些具有复杂结构的元素,无法用一个矩形来描述。
(4)当渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
(5)布局阶段结束后是绘制阶段,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。
值得注意的是,这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的 html 都解析完成之后再去构建和布局 render 树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
详细资料可以参考: 《浏览器渲染原理》《浏览器的渲染原理简介》《前端必读:浏览器内部工作原理》《深入浅出浏览器渲染原理》
参考答案:
JavaScript 的加载、解析与执行会阻塞文档的解析,也就是说,在构建 DOM 时,HTML 解析器若遇到了 JavaScript,那么
它会暂停文档的解析,将控制权移交给 JavaScript 引擎,等 JavaScript 引擎运行完毕,浏览器再从中断的地方恢复继续解
析文档。
也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的
原因。当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。
参考答案:一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker 那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。从而大大提高浏览体验。
参考答案:Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
参考答案:
参考答案:浏览器和 Node 环境下,microtask 任务队列的执行时机不同
Node 端,microtask 在事件循环的各个阶段之间执行
浏览器端,microtask 在事件循环的 macrotask 执行完之后执行
作者:晴天的太阳 链接:https://www.jianshu.com/p/502c20e17626 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
参考答案:
浏览器架构如下
* 用户界面
* 主进程
* 内核
* 渲染引擎
* JS 引擎
* 执行栈
* 事件触发线程
* 消息队列
* 微任务
* 宏任务
* 网络异步线程
* 定时器线程
来自文章结语
JS是单线程执行的,同一时间只能处理一件事。但是浏览器是有多个线程的,JS引擎通过分发这些耗时的异步事件(AJAX请求、DOM操作等)给Web APIs线程处理,因此避免了单线程被耗时的异步事件阻塞的问题。
Web APIs线程会将接收到的所有事件中已完成的事件根据类别分别将它们添加到相应的任务队列中。其中任务队列分以下两种:
- 宏任务队列(macrotask queue):其实是叫任务队列,ES5称task queue,也即本文图中的callback queue,macrotask是我们给它的别名,原因只是为了与ES6新增的microtask队列作区分而这样称呼,HTML标准中并没有macrotask这种说法。它存放的是DOM事件、AJAX事件、setTimeout事件等。
- 微任务队列(microtask queue):它存放的是Promise事件、nextTick事件等。优先级比macrotask高。
事件循环(event loop) 机制是为了协调事件(events)、用户交互(user interaction)、JS脚本(scripts)、页面渲染(rendering)、网络请求(networking)等等事件的有序执行而设置(定义由HTML标准给出,实现方式是靠各个浏览器厂商自己实现)。事件循环的过程如下:
- JS引擎执行一个事件,当遇到异步事件时则将其交给浏览器的Web APIs线程处理,然后该事件继续执行,永远不会被抢占,一直执行到该事件结束为止(run to complete)。
- 当JS引擎执行完当前事件(即执行栈变为空)之后,它会先去查看microtask队列,将microtask队列中的所有待执行事件全部执行完毕。
- 等微任务事件全部执行完毕后,再进行页面的渲染,此时表明一轮事件循环的过程结束。然后再去查看macrotask队列,取出一个宏事件添加到执行栈执行,开始一轮新的事件,执行完毕后再去执行所有微任务事件…如此往复。此即事件循环的执行过程。
打个比方帮助理解:宏任务事件就像是普通用户,而微任务事件就像是VIP用户,执行栈要先把所有在等待的VIP用户服务好了以后才能给在等待的普通用户服务,而且每次服务完一个普通用户以后都要先看看有没有VIP用户在等待,若有,则VIP用户优先(PS:人民币玩家真的可以为所欲为,hah…)。当然,执行栈正在给一个普通用户服务的时候,这时即使来了VIP用户,他也是需要等待执行栈服务完该普通用户后才能轮到他。
setTimeout设置的时间其实只是最小延迟时间,并不是确切的等待时间。实际上最小延时 >=4ms,小于4ms的会被当做4ms。
promise 对象是由关键字 new 及Promise构造函数来创建的。该构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数(即 new Promise(...)中的...的内容)。这个“处理器函数”是在promise创建时是自动执行的,.then之后的内容才是异步内容,会交给Web APIs处理,然后被添加到微任务队列。
async/await:async函数其实是Generator函数的语法糖(解释一下“语法糖”:就是添加标准以外的语法以方便开发人员使用,本质上还是基于已有标准提供的语法进行封装来实现的),async function 声明用于定义一个返回 AsyncFunction 对象的异步函数。执行async函数时,遇到await关键字时,await 语句产生一个promise,await 语句之后的代码被暂停执行,等promise有结果(状态变为settled)以后再接着执行。
解析:参考地址