在去年末开始换工做,直到如今算是告了一个段落,断断续续的也面试了很多公司,如今回想起来,那段时间经历了被面试官手撕,被笔试题狂怼,悲伤的时候差点留下没技术的泪水。javascript
这篇文章我打算把我找工做遇到的各类面试题(每次面试完我都会总结)和我本身复习遇到比较有意思的题目,作一份汇总,年后是跳槽高峰期,也许能帮到一些小伙伴。css
先说下这些题目难度,大部分都是基础题,由于这段经历给个人感受就是,无论你面试的是高级仍是初级,基础的知识必定会问到,甚至会有必定的深度,因此基础仍是很是重要的。html
我将根据类型分为几篇文章来写:前端
面试总结:javascript 面试点汇总(万字长文)(已完成) 【强烈你们看看这篇,面试中 js 是大头】vue
面试总结:nodejs 面试点汇总(已完成)html5
面试总结:浏览器相关 面试点汇总(已完成)java
面试总结:css 面试点汇总(已完成)node
面试总结:框架 vue 和工程相关的面试点汇总(已完成)jquery
面试总结:非技术问题汇总(已完成)css3
我会抓紧时间把未完成的总结补全的~
这篇文章是对 浏览器
相关的题目作总结,欢迎朋友们先收藏在看。
先看看目录
全部的性能优化中,缓存是最重要也是最直接有效的,毕竟如今都这么忙,可等不了网页转菊花。
缓存分为强缓存和协商缓存,看下流程图
缓存机制相关的字段都是在请求和响应头上
强缓存,在缓存有效期内,客户端直接读取本地资源。
强缓存返回的状态码是 200
在 http1.0
中使用,表示资源失效的具体时间点 Expires:Sat, 09 Jun 2018 08:13:56 GMT
,如果访问器和本地时间不一致,可能就会出现问题,在如今 http1.1中换成了 max-age
,为了兼容也能够加上。
指定指令来实现缓存机制,多个指令间逗号分隔,常见的指令有如下几个,完整的可点击下文中的 mdn 链接查看
max-age
: 强缓存的有效时间,单位秒 max-age=30672000
no-cache
:使用缓存协商,先与服务器确认返回的响应是否被更改。
no-store
:直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源,可用于关闭缓存。
public
:代表响应能够被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存,即便是一般不可缓存的内容(例如,该响应没有max-age指令或Expires消息头)。
private
:代表响应只能被单个用户缓存,不能做为共享缓存(即代理服务器不能缓存它)。私有缓存能够缓存响应内容。
强缓存,在缓存有效期内,客户端直接读取本地资源。
强缓存返回的状态码是 200
在 http1.0
中使用,表示资源失效的具体时间点 Expires:Sat, 09 Jun 2018 08:13:56 GMT
,如果访问器和本地时间不一致,可能就会出现问题,在如今 http1.1中换成了 max-age
,为了兼容也能够加上。
协商缓存,关键在于协商,在使用本地缓存以前,须要先跟服务器作个对比,服务器告知你的资源可用,是最新的,那就能够直接取本地资源,反之,服务器返回最新的资源给客户端,客户端收到后更新本地资源。
状态码:
采用资源最后修改时间来判断,单位精度秒
Last-Modified:服务器资源的最新更新时间 Tue, 14 Jan 2020 09:18:29 GMT
If-Modified-Since:客户端发起协商,把本地记录的文件更新时间传给服务器,服务器进行判断比较
这个判断方式是 http1.0 的产物,由于时间精度是秒,若文件的更新频率在秒级之内,就会出现文件不一致。
为了解决上面的那个问题, http1.1 加了这组标记
ETag:服务器根据内容生成惟一的字符串标识
If-None-Match:客户端发起协商,把本地记录的 hash 标识传给服务器,服务器进行判断比较。
若同时存在 Last-Modified
和 ETag
, ETag
的优先级更高。
可看到有两个来源:
memory cache
:内存中读取
disk cache
:硬盘中读取
内存固然要比硬盘读取快,为啥会有存放硬盘呢?
由于内存浏览器内存有限啊,因此浏览器会有一套机制,根据文件大小何使用频率存放不一样的位置,具体的实现取决于浏览器厂商,不过这微小对用户是无感知的。
developer.mozilla.org/zh-CN/docs/…
PWA(Progressive web apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式加强策略来建立跨平台 Web 应用程序。(来自 MDN)
先看看 PWA 有哪些核心技术,就知道它有哪些优点了
App Shell 架构是构建 Progressive Web App 的一种方式,这种应用能可靠且即时地加载到您的用户屏幕上,与本机应用类似。
这个模型包含界面所须要的最小资源文件,若是离线缓存,能够确保重复访问都有快速响应的特性,页面可快速渲染,网络仅仅获取数据。
或者这么理解, App Shell 就相似于原生app,没网络也能够本地启动。
PWA 的核心,上面说到缓存可让页面尽快加载,但必须有网络的状况下才行,没网络下还想加载网页咋办?
ServiceWork 持久的离线缓存的能力就能够实现。
Service Worker 有如下功能和特性:
一个独立的 worker 线程,独立于当前网页进程,有本身独立的 worker context
一旦被 install,就永远存在,除非被手动 unregister
用到的时候能够直接唤醒,不用的时候自动睡眠
可编程拦截代理请求和返回,缓存文件,缓存的文件能够被网页进程取到(包括网络离线状态)
离线内容开发者可控
能向客户端推送消息
不能直接操做 DOM
必须在 HTTPS 环境下才能工做
异步实现,内部大都是经过 Promise 实现
js 是单线程的,ServiceWork 独立线程意味着不会阻塞js执行;可编程拦截代理请求和返回,可自定义文件缓存策略。
这些特色意味着开发者有足够的权限去操做缓存,让缓存作到优雅,效率达到极致
接下来核心是如何让设计缓存策略,
推荐你们看看开源的 wordbox 封装的缓存策略,策略更加丰富。
代码不复杂,主要是声明周期、与js线程间通讯、api调用,就不贴上来了。
参考文档:
developer.mozilla.org/zh-CN/docs/…
这是一个大体的流程,面试官会从中挑出其余点来接着问
先看这图,html文档 和 css 渲染过程的图
页面是采用流式布局来绘制,左到右,上到下,那么一个节点的空间属性如果发生了变化,那么会影响到其余节点的空间布局,须要从新收集节点信息,在进行绘制,这就是回流的过程。
重绘指的是对元素的外观作处理,好比颜色、背景、阴影等。
因此回流必定触发重绘。
获取位置信息或者修改几何属性,以下:
// 获取位置信息相关属性 - offsetTop offsetLeft offsetWidth offsetHeight 相对于父级容器的偏移量 - scrollTop scrollLeft scrollWidth scrollHeight 相对于父级容器滚动上去的距离 - clientTop clientLeft clientWidth clientHeight 元素边框的厚度 - getComputedStyle() - getBoundingClientRect 复制代码
对树的局部甚至全局从新生成是很是耗性能的,因此要避免频繁触发回流
开启后,会将 dom 元素提高为独立的渲染层,它的变化不会再影响文档流中的布局。
http 是创建在 TCP 上的应用层协议,超文本传送协议。
是单向的短连接,目前有 http1.0 http 1.1 http2.0
http1.0 :客户端的每次请求都要求创建一次单独的链接,在处理完本次请求后,就自动释放链接。 http1.1 :能够在一次链接中处理多个请求,而且多个请求能够重叠进行,不须要等待一个请求结束后再发送下一个请求 http2.0 :可支持多路复用,一个 tcp 可同时传输多个 http 请求,头部数据还作了压缩
面试官问这个通常是更关注对 tcp 的理解
tcp 是传输层协议,它的特色是:三次握手和四次挥手。
三次握手的目的是为了防止已经失效的链接请求报文段忽然又传到服务端,而产生错误,因此要创建可靠的链接发送数据
三次握手创建链接过程:
四次挥手断开链接的过程:
传输层的另一个协议 UDP 称为用户数据报协议,无链接的传输协议。
UDP 是报文的搬运工,不须要创建彻底可靠的连接,不保证数据的可靠性,由于协议控制项比较少,且报文头部简单,报文体积相对要小,速度上相比更快,实时性更高,好比电话会议、多媒体数据流等场景就采用 UDP
http 报文传输过程当中是明文的,能够经过抓包的方式看到报文内容,这就暴露一个安全问题,易被劫持篡改。
为了解决这个问题,就有了 TLS ,https = http + TLS
TLS:安全传输层协议,用于在两个通讯应用程序之间提供保密性和数据完整性,该协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。
TLS 利用非对称加密演算来对通讯方作身份认证,以后交换对称密钥做为会谈密钥(Session key),所以 https 分为两个阶段
步骤以下:
对前端来讲,毕竟偏向于理论,因此建议你们根据步骤画一画流程图,更利于理解记忆。
上面第一步讲到 CA证书,假如没有证书验证这一环节,那么公钥在传输过程极有可能被中间人拦截,来个狸猫换太子,将服务端的公钥换成它本身的公钥,返回给客户端,这么一来,就彻底起不到加密的做用了,也就是中间人攻击。
因此就须要一个验证的机制,保证公钥是来自服务端的,没有被篡改的,CA证书就出场了。
CA证书,是由 CA 机构颁发的一个凭证,里面关键的信息有,签名算法、签名hash算法、颁发者、有效期、公钥、指纹,这个两个算法就表示对称阶段和非对称阶段采用的算法,公钥就是服务端的公钥,在申请的时候,企业须要上传公钥给CA机构,重点是这个指纹,这个指纹是由 CA 机构经过私钥对一段签名加密生成的。
因此经过验证证书是否合法,就知道公钥是否被篡改,那么怎么验证合法呢?
天然是经过证书的指纹。
在浏览器和我的PC中,都预装了顶级的 CA 机构证书和公钥,因此浏览器获取到证书后,经过内置的公钥对指纹进行解密获得签名,而后浏览器也根据一样的规则生成一段签名,两段签名进行比较,验证经过,那么这个证书中公钥就是可信的。
那么这样一来是否是就能够彻底避免了中间人攻击呢?
毕竟顶级的 CA 证书是内置的,仍是有一种方式,你们是否还记得咱们用,咱们是能够用 Fiddle 对 https 进行抓包的,那 Fiddle 算不算中间人呢 ?
Fiddle 之因此能拦截成功是由于,咱们在抓包以前,在咱们本身手机安装一份来自 Fiddle 的证书,也就是客户端本身信任了第三方来源的证书,这么一来客户端天然能解析出 Fiddle 转发出来的报文啦。
因此只要不随意信任第三方证书,基本上是不会发生中间人攻击的。
options 一般用于,在跨域请求前发起预检请求,以检测请求是否被服务器接受。
跨域请求中分为简单请求和预检请求两种,符合如下条件可视为简单请求:
GET POST HEAD
text/plain mutipart/form-data application/x-www-form-urlencode
三种之一- Accept
- Accept-Language
- Content-Language
- Content-Type (须要注意额外的限制)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
复制代码
除去简单请求外,其余请求就会先触发预检请求。
常见的,好比使用
预检请求返回的头部报文中有
Access-Control-Allow-Origin
: 服务器可接受的请求来源
Access-Control-Request-Method
: 服务器实际请求所使用的 HTTP 方法
Access-Control-Request-Headers
: 服务器实际请求所携带的自定义首部字段。
客户端基于从预检请求得到的信息来判断,是否继续执行跨域请求。
注意:跨域请求若想发送 cookie 信息,须要服务端设置 resp.setHeader("Access-Control-Allow-Credentials","true"); 客户端设置 withCredentials: true
参考资料: cloud.tencent.com/developer/n…
直接看图
Content-Type 字段来获知请求中的消息主体是用何种方式编码
Content-Type: application/json : json 字符串 Content-Type: application/x-www-form-urlencoded : & 将
key=value
进行拼接, jquery 默认使用这个 Content-Type: multipart/form-data : 经常使用于文件上传
主要有两类 XSS
CSRF
跨站脚本攻击,攻击者将一段可执行的代码注入到网页中,如连接、输入框,分为持久形和临时性的,持久性的是恶意代码被存储到数据库里,会形成持久的攻击;临时性的是仅在当前被工具页面上生效;
防范的方式是对与网页上获取的内容要作转义处理。
跨站请求伪造,构造一个钓鱼网站,利用站点对浏览器的信任,从而欺骗用户,发起请求进行恶意操做。
用户在浏览器登陆后,站点是信任浏览器的,但浏览器是无法知道请求是不是用户自愿发起的,站点信任后,所发起的请求浏览器都是信任的。
那么用户是已登陆的状况下,钓鱼站点中发起跨域请求,跨域标签或者 form 表单,就会把用户的认证信息 cookies 带上,从而到达伪造用户身份进行攻击。
防范方式:
关于 xss csrf 网上有很详细的介绍,不过核心原理仍是比较简单的。
第一次听到有点懵,由于是 CORS ,回来查了资料才明白。
CORB 是一种判断是否要在跨站资源数据到达页面以前阻断其到达当前站点进程中的算法,下降了敏感数据暴露的风险。是站点隔离的一种实现机制,针对跨域标签,保护站点资源。
当跨域请求回来的数据 MIME type
同跨域标签应有的 MIME
类型不匹配时,浏览器会启动 CORB
保护数据不被泄漏,被保护的数据类型只有 html xml json
。
MIME 是一个互联网标准,扩展了电子邮件标准,使其能够支持更多的消息类型。常见 MIME 类型如:text/html text/plain image/png application/javascript ,用于标识返回消息属于哪种文档类型。写法为 type/subtype。 在 HTTP 请求的响应头中,以 Content-Type: application/javascript; charset=UTF-8 的形式出现,MIME type 是 Content-Type 值的一部分
这篇文章写的很是详细,建议你们直接查看 Cross-Origin Read Blocking (CORB)
主流的有一下几种
image script
发起 get 方法的跨域请求var img = new Image; img.onload = function() { }, img.onerror = function() { }, img.src = options.url; 复制代码
application/javascript
的方式进行解析,就能够触发预设好的回调函数。/* html */ let scr = document.createElement('script'); scr.src = `http://127.0.0.1:3500/xx?callback=cb` document.getElementsByTagName('head')[0].appendChild(scr) function cb(res){ console.log('into'); console.log(res); } /* server */ let data = { name: 'xiaoli' } var str = ctx.query.callback + '(' + JSON.stringify(data) + ')'; // ctx.query = {callback:'cb'} // str = 'cb({"name":"xiaoli"})' ctx.body = str; 复制代码
经常使用 nginx 作反向代理,详细配置就很少说了
这就全是服务端的工做了,主要的三个参数
Access-Control-Allow-Origin
: 服务器可接受的请求来源
Access-Control-Request-Method
: 服务器实际请求所使用的 HTTP 方法
Access-Control-Request-Headers
: 服务器实际请求所携带的自定义首部字段。
假若有 BFF 层的话,能够在这一层作一个中转,这个就得看项目架构是否有条件了。
html 的 meta 设置的缓存策略是对于当前文档有效,用于定义页面缓存。
与http的请求参数很相像,就不重复了,详细介绍可参考 设置meta标签 清除页面缓存
hash 路由,在 html5 前,为了解决单页路由跳转问题采用的方案, hash 的变化不会触发页面渲染,服务端也没法获取到 hash 值,前端可经过监听 hashchange
事件来处理hash值的变化
window.addEventListener('hashchange', function(){ // 监听hash变化,点击浏览器的前进后退会触发 }) 复制代码
history 路由,是 html5 的规范,提供了对history栈中内容的操做,经常使用api有:
window.history.pushState(state, title, url) // let currentState = history.state; 获取当前state // state:须要保存的数据,这个数据在触发popstate事件时,能够在event.state里获取 // title:标题,基本没用,通常传 null // url:设定新的历史记录的 url。新的 url 与当前 url 的 origin 必须是一樣的,不然会抛出错误。url能够是绝对路径,也能够是相对路径。 //如 当前url是 https://www.baidu.com/a/,执行history.pushState(null, null, './qq/'),则变成 https://www.baidu.com/a/qq/, //执行history.pushState(null, null, '/qq/'),则变成 https://www.baidu.com/qq/ window.history.replaceState(state, title, url) // 与 pushState 基本相同,但她是修改当前历史记录,而 pushState 是建立新的历史记录 window.addEventListener("popstate", function() { // 监听浏览器前进后退事件,pushState 与 replaceState 方法不会触发 }); 复制代码
<div class="an" onclick="aa()">aaaa</div>
document.getElementById("demo").οnclick=function(){}
document.addEventListener('name',()=>{})
浏览器中的事件触发有三个阶段:
事件委托也叫事件代理,在 dom 节点中,由于有事件冒泡机制,因此子节点的事件能够被父节点捕获。
所以,在适当的场景下将子节点的事件用父节点监听处理,支持的事件 点击事件 鼠标事件监听。
事件代理的优点:
关于事件捕获和事件冒泡的理解:
事件捕获:事件从外往里传播,addEventListener 最后一个参数设置成 true 就能够捕获事件,默认是 false ,监听事件冒泡。捕获是计算机处理输入的逻辑
事件冒泡:事件由内往外传播,冒泡是人类理解事件的思惟。
target:指的是事件流的目标阶段,获取的是被点击的元素。
currentTarget:在事件流的捕获和冒泡阶段时,是指向当前事件活动对象,只有在目标阶段的时候,二者才会相等
根据页面渲染流程可得知:
都是告知浏览器提早加载文件(图片、视频、js、css等),但执行上是有区别的。
prefetch
:其利用浏览器空闲时间来下载或预取用户在不久的未来可能访问的文档。<link href="/js/xx.js" rel="prefetch">
preload
: 能够指明哪些资源是在页面加载完成后即刻须要的,浏览器在主渲染机制介入前就进行预加载,这一机制使得资源能够更早的获得加载并可用,且更不易阻塞页面的初步渲染,进而提高性能。 <link href="/js/xxx.js" rel="preload" as="script">
须要 as 指定资源类型,目前可用的属性类型有以下:
audio: 音频文件。
document: 一个将要被嵌入到<frame>或<iframe>内部的HTML文档。
embed: 一个将要被嵌入到<embed>元素内部的资源。
fetch: 那些将要经过fetch和XHR请求来获取的资源,好比一个ArrayBuffer或JSON文件。
font: 字体文件。
image: 图片文件。
object: 一个将会被嵌入到<embed>元素内的文件。
script: JavaScript文件。
style: 样式表。
track: WebVTT文件。
worker: 一个JavaScript的web worker或shared worker。
video: 视频文件。
复制代码
用于js脚本预加载
async
: 加载脚本和渲染后续文档元素并行进行,脚本加载完成后,暂停html解析,当即解析js脚本
defer
: 加载脚本和渲染后续文档元素并行进行,但脚本的执行会等到 html 解析完成后执行
参考资料:
developer.mozilla.org/zh-CN/docs/…
developer.mozilla.org/zh-CN/docs/…
<meta name="viewport" content="width=500, initial-scale=1">
这里只指定了两个属性,宽度和缩放,实际上 viewport 能控制的更多,它能表示的所有属性以下:
由于在之前移动端双击能够缩放或者滑动,因此为了区分是点击仍是双击,加了 300ms 的延迟。
解决方案:
(后续再补上)
使用 performance.timing
这个api就能够获取到绝大部分性能相关的数据
navigationStart
:在同一个浏览器上下文中,前一个网页(与当前页面不必定同域)unload 的时间戳,若是无前一个网页 unload ,则与 fetchStart 值相等unloadEventStart
:前一个网页(与当前页面同域)unload 的时间戳,若是无前一个网页 unload 或者前一个网页与当前页面不一样域,则值为 0redirectStart
:第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,不然值为 0redirectEnd
:最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内的重定向才算,不然值为 0fetchStart
:浏览器准备好使用 HTTP 请求抓取文档的时间,这发生在检查本地缓存以前domainLookupStart
:DNS 域名查询开始的时间,若是使用了本地缓存(即无 DNS 查询)或持久链接,则与 fetchStart 值相等domainLookupEnd
:DNS 域名查询完成的时间,若是使用了本地缓存(即无 DNS 查询)或持久链接,则与 fetchStart 值相等connectStart
:HTTP(TCP) 开始创建链接的时间,若是是持久链接,则与 fetchStart 值相等,若是在传输层发生了错误且从新创建链接,则这里显示的是新创建的链接开始的时间secureConnectionStart
:HTTPS 链接开始的时间,若是不是安全链接,则值为 0connectEnd
:HTTP(TCP) 完成创建链接的时间(完成握手),若是是持久链接,则与 fetchStart 值相等,若是在传输层发生了错误且从新创建链接,则这里显示的是新创建的链接完成的时间requestStart
:HTTP 请求读取真实文档开始的时间(完成创建链接),包括从本地读取缓存,链接错误重连时,这里显示的也是新创建链接的时间responseStart
:HTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存responseEnd
:HTTP 响应所有接收完成的时间(获取到最后一个字节),包括从本地读取缓存domLoading
:开始解析渲染 DOM 树的时间,此时 Document.readyState 变为 loading,并将抛出 readystatechange 相关事件domInteractive
:完成解析 DOM 树的时间,Document.readyState 变为 interactive,并将抛出 readystatechange 相关事件domContentLoadedEventStart
:DOM 解析完成后,网页内资源加载开始的时间,表明DOMContentLoaded事件触发的时间节点domContentLoadedEventEnd
:DOM 解析完成后,网页内资源加载完成的时间(如 JS 脚本加载执行完毕),文档的DOMContentLoaded 事件的结束时间,也就是jQuery中的domready时间;domComplete
:DOM 树解析完成,且资源也准备就绪的时间,Document.readyState 变为 complete,并将抛出 readystatechange 相关事件loadEventStart
:load 事件发送给文档,也即 load 回调函数开始执行的时间,若是没有绑定 load 事件,值为 0loadEventEnd
:load 事件的回调函数执行完毕的时间,若是没有绑定 load 事件,值为 0DNS查询耗时 = domainLookupEnd - domainLookupStart
TCP连接耗时 = connectEnd - connectStart
request请求耗时 = responseEnd - responseStart
解析dom树耗时 = domComplete - domInteractive
白屏时间 = domloadng - fetchStart
domready时间 = domContentLoadedEventEnd - fetchStart
onload时间 = loadEventEnd - fetchStart
这个题有点无聊了,但仍是遇到了
输入框对于输入连续中文的时候可使用如下两个监听事件(第一次知道还有这个事件):
compositionstart:事件触发于一段文字的输入以前(相似于 keydown 事件,可是该事件仅在若干可见字符的输入以前,而这些可见字符的输入可能须要一连串的键盘操做、语音识别或者点击输入法的备选词)。
compositionend:当文本段落的组成完成或取消时,事件将被触发 (具备特殊字符的触发, 须要一系列键和其余输入, 如语音识别或移动中的字词建议)。