挑战一轮大厂后的面试总结 (含六个方向) - 浏览器 篇

21,756 阅读29分钟

在去年底开始换工作,直到现在算是告了一个段落,断断续续的也面试了不少公司,现在回想起来,那段时间经历了被面试官手撕,被笔试题狂怼,悲伤的时候差点留下没技术的泪水。

这篇文章我打算把我找工作遇到的各种面试题(每次面试完我都会总结)和我自己复习遇到比较有意思的题目,做一份汇总,年后是跳槽高峰期,也许能帮到一些小伙伴。

先说下这些题目难度,大部分都是基础题,因为这段经历给我的感觉就是,不管你面试的是高级还是初级,基础的知识一定会问到,甚至会有一定的深度,所以基础还是非常重要的。

我将根据类型分为几篇文章来写:

面试总结:javascript 面试点汇总(万字长文)(已完成) 【强烈大家看看这篇,面试中 js 是大头】

面试总结:nodejs 面试点汇总(已完成)

面试总结:浏览器相关 面试点汇总(已完成)

面试总结:css 面试点汇总(已完成)

面试总结:框架 vue 和工程相关的面试点汇总(已完成)

面试总结:非技术问题汇总(已完成)

我会抓紧时间把未完成的总结补全的~

这篇文章是对 浏览器 相关的题目做总结,欢迎朋友们先收藏在看。

先看看目录

目录

介绍下缓存

所有的性能优化中,缓存是最重要也是最直接有效的,毕竟现在都这么忙,可等不了网页转菊花。

缓存分为强缓存和协商缓存,看下流程图

cache

缓存机制相关的字段都是在请求和响应头上

强缓存

强缓存,在缓存有效期内,客户端直接读取本地资源。

强缓存返回的状态码是 200

Expires

http1.0 中使用,表示资源失效的具体时间点 Expires:Sat, 09 Jun 2018 08:13:56 GMT ,若是访问器和本地时间不一致,可能就会出现问题,在现在 http1.1中换成了 max-age ,为了兼容也可以加上。

Cache-control

指定指令来实现缓存机制,多个指令间逗号分隔,常见的指令有以下几个,完整的可点击下文中的 mdn 连接查看

max-age: 强缓存的有效时间,单位秒 max-age=30672000

no-cache:使用缓存协商,先与服务器确认返回的响应是否被更改。

no-store:直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源,可用于关闭缓存。

public :表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存,即使是通常不可缓存的内容(例如,该响应没有max-age指令或Expires消息头)。

private :表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。私有缓存可以缓存响应内容。

强缓存

强缓存,在缓存有效期内,客户端直接读取本地资源。

强缓存返回的状态码是 200

Expires

http1.0 中使用,表示资源失效的具体时间点 Expires:Sat, 09 Jun 2018 08:13:56 GMT ,若是访问器和本地时间不一致,可能就会出现问题,在现在 http1.1中换成了 max-age ,为了兼容也可以加上。

协商缓存

协商缓存,关键在于协商,在使用本地缓存之前,需要先跟服务器做个对比,服务器告知你的资源可用,是最新的,那就可以直接取本地资源,反之,服务器返回最新的资源给客户端,客户端收到后更新本地资源。

状态码:

  • 若本地资源是最新的,那么返回 304 (考点!)
  • 若比对后,需要从服务器获取最新资源,那就是正常的 200
Last-modified If-Modified-Since

采用资源最后修改时间来判断,单位精度秒

Last-Modified:服务器资源的最新更新时间 Tue, 14 Jan 2020 09:18:29 GMT

If-Modified-Since:客户端发起协商,把本地记录的文件更新时间传给服务器,服务器进行判断比较

这个判断方式是 http1.0 的产物,因为时间精度是秒,若文件的更新频率在秒级以内,就会出现文件不一致。

ETag If-None-Match

为了解决上面的那个问题, http1.1 加了这组标记

ETag:服务器根据内容生成唯一的字符串标识

If-None-Match:客户端发起协商,把本地记录的 hash 标识传给服务器,服务器进行判断比较。

若同时存在 Last-ModifiedETag , ETag 的优先级更高。

浏览器缓存存放位置

browser-cache

可看到有两个来源:

memory cache:内存中读取

disk cache:硬盘中读取

内存当然要比硬盘读取快,为啥会有存放硬盘呢?

因为内存浏览器内存有限啊,所以浏览器会有一套机制,根据文件大小何使用频率存放不同的位置,具体的实现取决于浏览器厂商,不过这微小对用户是无感知的。

参考文档

developer.mozilla.org/zh-CN/docs/…

了解 PWA 么

PWA(Progressive web apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。(来自 MDN)

先看看 PWA 有哪些核心技术,就知道它有哪些优势了

App Shell

App Shell 架构是构建 Progressive Web App 的一种方式,这种应用能可靠且即时地加载到您的用户屏幕上,与本机应用相似。

这个模型包含界面所需要的最小资源文件,如果离线缓存,可以确保重复访问都有快速响应的特性,页面可快速渲染,网络仅仅获取数据。

或者这么理解, App Shell 就类似于原生app,没网络也可以本地启动。

ServiceWork

PWA 的核心,上面说到缓存可以让页面尽快加载,但必须有网络的情况下才行,没网络下还想加载网页咋办?

ServiceWork 持久的离线缓存的能力就可以实现。

Service Worker 有以下功能和特性:

  • 一个独立的 worker 线程,独立于当前网页进程,有自己独立的 worker context

  • 一旦被 install,就永远存在,除非被手动 unregister

  • 用到的时候可以直接唤醒,不用的时候自动睡眠

  • 可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态)

  • 离线内容开发者可控

  • 能向客户端推送消息

  • 不能直接操作 DOM

  • 必须在 HTTPS 环境下才能工作

  • 异步实现,内部大都是通过 Promise 实现

js 是单线程的,ServiceWork 独立线程意味着不会阻塞js执行;可编程拦截代理请求和返回,可自定义文件缓存策略。

这些特点意味着开发者有足够的权限去操作缓存,让缓存做到优雅,效率达到极致

接下来核心是如何让设计缓存策略,

  1. 缓存优先,先查询缓存,若存在,直接返回,不存在,请求服务,更新缓存
  2. 服务端优先,不查询缓存,直接请求服务端,服务端失败才会查询缓存
  3. 稳定优先,先查询缓存,有就读取,同时请求服务端更新资源

推荐大家看看开源的 wordbox 封装的缓存策略,策略更加丰富。

代码不复杂,主要是声明周期、与js线程间通信、api调用,就不贴上来了。

参考文档:

lavas.baidu.com/pwa/offline…

developer.mozilla.org/zh-CN/docs/…

URL 输入到渲染的过程

  1. 域名解析,找到服务地址
  2. 构建 TCP 连接,若有 https,则多一层 TLS 握手,
  3. 特殊响应码处理 301 302
  4. 解析文档
  5. 构建 dom 树和 csscom
  6. 生成渲染树:从DOM树的根节点开始遍历每个可见节点,对于每个可见的节点,找到CSSOM树中对应的规则,并应用它们,根据每个可见节点以及其对应的样式,组合生成渲染树
  7. Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的集合信息
  8. Painting(重绘):根据渲染树及其回流得到的集合信息,得到节点的绝对像素。
  9. 绘制,在页面上展示,这一步还涉及到绘制层级、GPU相关的知识点
  10. 加载js脚本,加载完成解析js脚本

这是一个大致的流程,面试官会从中挑出其他点来接着问

重绘和回流(重排)

先看这图,html文档 和 css 渲染过程的图

layout

页面是采用流式布局来绘制,左到右,上到下,那么一个节点的空间属性若是发生了变化,那么会影响到其他节点的空间布局,需要重新收集节点信息,在进行绘制,这就是回流的过程。

重绘指的是对元素的外观做处理,比如颜色、背景、阴影等。

所以回流一定触发重绘。

触发回流的场景

获取位置信息或者修改几何属性,如下:

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
  • 页面一开始渲染的时候(这肯定避免不了)
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
  • 获取位置信息,因为需要回流计算最新的值
// 获取位置信息相关属性
- offsetTop offsetLeft offsetWidth offsetHeight 相对于父级容器的偏移量
- scrollTop scrollLeft scrollWidth scrollHeight 相对于父级容器滚动上去的距离
- clientTop clientLeft clientWidth clientHeight 元素边框的厚度
- getComputedStyle()
- getBoundingClientRect

回流的优化

对树的局部甚至全局重新生成是非常耗性能的,所以要避免频繁触发回流

  • 现代浏览器已经帮我们做了优化,采用队列存储多次的回流操作,然后批量执行,但获取布局信息例外,因为要获取到实时的数值,浏览器就必须要清空队列,立即执行回流。
  • 编码上,避免连续多次修改,可通过合并修改,一次触发
  • 对于大量不同的 dom 修改,可以先将其脱离文档流,比如使用绝对定位,或者 display:none ,在文档流外修改完成后再放回文档里中
  • 通过节流和防抖控制触发频率
  • css3 硬件加速,transform、opacity、filters,开启后,会新建渲染层

开启GPU加速的方法

开启后,会将 dom 元素提升为独立的渲染层,它的变化不会再影响文档流中的布局。

  • transform: translateZ(0)
  • opacity
  • filters
  • Will-change

说下对 http 协议的了解

http 是建立在 TCP 上的应用层协议,超文本传送协议。

是单向的短链接,目前有 http1.0 http 1.1 http2.0

http1.0 :客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。 http1.1 :可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求 http2.0 :可支持多路复用,一个 tcp 可同时传输多个 http 请求,头部数据还做了压缩

面试官问这个一般是更关注对 tcp 的理解

tcp

tcp 是传输层协议,它的特点是:三次握手和四次挥手。

三次握手的目的是为了防止已经失效的连接请求报文段突然又传到服务端,而产生错误,所以要建立可靠的连接发送数据

三次握手建立连接过程:

  1. 客户端发送位码为syn=1,随机产生数据包到服务器,服务器由SYN=1知道客户端要求建立联机(客户端:我要连接你)
  2. 服务器收到请求后要确认联机信息,向A发送ack number=(客户端的seq+1),syn=1,ack=1,随机生成数据包(服务器:好的,你来连吧)
  3. 客户端收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,客户端会再发送ack number=(服务器的seq+1),ack=1,服务器收到后确认seq值与ack=1则连接建立成功。(客户端:好的,我来了)

四次挥手断开连接的过程:

  1. 客户端发送请求给服务端,申请主动断开连接,进入等待状态,不在往服务端发送数据,但是接收数据(客户端:我要断开连接了)
  2. 服务端收到后,告知客户端知道了,服务端进行等待状态,不在接收数据,但是可以继续发送数据(服务端:好,我知道了,但是要等一等)
  3. 客户端收到服务端的告知后,进入下一阶段的等待。(客户端:好,我等)
  4. 服务端完成剩余数据的发送后,告知客户端可以断开了,服务端不接收和读取数据(服务端:你可以断开了)
  5. 客户端收到后,告知服务端,已收到,然后释放链接(客户端:好的,我断开链接了)
  6. 服务端收到后,也释放链接

UDP

传输层的另外一个协议 UDP 称为用户数据报协议,无连接的传输协议。

UDP 是报文的搬运工,不需要建立完全可靠的链接,不保证数据的可靠性,因为协议控制项比较少,且报文头部简单,报文体积相对要小,速度上相比更快,实时性更高,比如电话会议、多媒体数据流等场景就采用 UDP

介绍下 https

http 报文传输过程中是明文的,可以通过抓包的方式看到报文内容,这就暴露一个安全问题,易被劫持篡改。

为了解决这个问题,就有了 TLS ,https = http + TLS

TLS:安全传输层协议,用于在两个通信应用程序之间提供保密性和数据完整性,该协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。

TLS 利用非对称加密演算来对通信方做身份认证,之后交换对称密钥作为会谈密钥(Session key),因此 https 分为两个阶段

  1. 通过非对称加解密确认对方身份是否合法,若合法生成会话密钥。(这一步是核心)
  2. 报文的在发送前,先用会话密钥进行对称加密,在传输。

TLS 握手

步骤如下:

  1. 客户端请求服务端建立SSL链接,服务端并向客户端发送一个随机数 randomC 和 CA 机构颁发的证书(注:CA相关下面详细介绍)
  2. 客户端对证书进行验证,验证通过后,生成一个随机数 randomS ,用公钥对 randomS 加密 ,同时用 randomS 生成一段签名,发送给服务端
  3. 服务端接收到后,用私钥对秘文解密,用解密后的 key 生成签名,并与客户端传来的签名进行比较,检验通过后,然后生成一个随机数 randomP ,并用私钥加密,还有随机数生成的 hash 值,一并发给客户端。
  4. 客户端用公钥解密,并校验 hash 值通过后,两端利用 randomC randomS randomP 通过一定的算法生成 session key,后续的报文将通过 session key 对称加密进行传输。

对前端来说,毕竟偏向于理论,所以建议大家根据步骤画一画流程图,更利于理解记忆。

CA 证书

上面第一步讲到 CA证书,假如没有证书验证这一环节,那么公钥在传输过程极有可能被中间人拦截,来个狸猫换太子,将服务端的公钥换成它自己的公钥,返回给客户端,这么一来,就完全起不到加密的作用了,也就是中间人攻击。

所以就需要一个验证的机制,保证公钥是来自服务端的,没有被篡改的,CA证书就出场了。

CA证书,是由 CA 机构颁发的一个凭证,里面关键的信息有,签名算法、签名hash算法、颁发者、有效期、公钥、指纹,这个两个算法就表示对称阶段和非对称阶段采用的算法,公钥就是服务端的公钥,在申请的时候,企业需要上传公钥给CA机构,重点是这个指纹,这个指纹是由 CA 机构通过私钥对一段签名加密生成的。

所以通过验证证书是否合法,就知道公钥是否被篡改,那么怎么验证合法呢?

自然是通过证书的指纹。

在浏览器和个人PC中,都预装了顶级的 CA 机构证书和公钥,所以浏览器获取到证书后,通过内置的公钥对指纹进行解密得到签名,然后浏览器也根据同样的规则生成一段签名,两段签名进行比较,验证通过,那么这个证书中公钥就是可信的。

那么这样一来是不是就可以完全避免了中间人攻击呢?

毕竟顶级的 CA 证书是内置的,还是有一种方式,大家是否还记得我们用,我们是可以用 Fiddle 对 https 进行抓包的,那 Fiddle 算不算中间人呢 ?

Fiddle 之所以能拦截成功是因为,我们在抓包之前,在我们自己手机安装一份来自 Fiddle 的证书,也就是客户端自己信任了第三方来源的证书,这么一来客户端自然能解析出 Fiddle 转发出来的报文啦。

所以只要不随意信任第三方证书,基本上是不会发生中间人攻击的。

什么情况会触发 options 请求

options 通常用于,在跨域请求前发起预检请求,以检测请求是否被服务器接受。

跨域请求中分为简单请求和预检请求两种,符合以下条件可视为简单请求:

  • 使用的 HTTP method 是 GET POST HEAD
  • content-type 是 text/plain mutipart/form-data application/x-www-form-urlencode 三种之一
  • 请求头只能包含这些
- Accept
- Accept-Language
- Content-Language
- Content-Type (需要注意额外的限制)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width

除去简单请求外,其他请求就会先触发预检请求。

常见的,比如使用

  • content-Type 为 application/xml 或 text/xml 的 POST 请求
  • 设置自定义头,比如 X-JSON、X-MENGXIANHUI 等

预检请求返回的头部报文中有

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…

http有哪些常见请求头

直接看图

rquest-head

response-head

报文提交 Content-Type 的几种区别

Content-Type 字段来获知请求中的消息主体是用何种方式编码

Content-Type: application/json : json 字符串 Content-Type: application/x-www-form-urlencoded : & 将 key=value 进行拼接, jquery 默认使用这个 Content-Type: multipart/form-data : 常用于文件上传

关于前端安全防范

主要有两类 XSS CSRF

XSS

跨站脚本攻击,攻击者将一段可执行的代码注入到网页中,如链接、输入框,分为持久形和临时性的,持久性的是恶意代码被存储到数据库里,会造成持久的攻击;临时性的是仅在当前被工具页面上生效;

防范的方式是对与网页上获取的内容要做转义处理。

CSRF

跨站请求伪造,构造一个钓鱼网站,利用站点对浏览器的信任,从而欺骗用户,发起请求进行恶意操作。

用户在浏览器登录后,站点是信任浏览器的,但浏览器是没法知道请求是否是用户自愿发起的,站点信任后,所发起的请求浏览器都是信任的。

那么用户是已登录的情况下,钓鱼站点中发起跨域请求,跨域标签或者 form 表单,就会把用户的认证信息 cookies 带上,从而到达伪造用户身份进行攻击。

防范方式:

  1. 服务端校验 Referer ,但某些浏览器可能可以修改 Referer
  2. 随机 token ,每访问页面就生成一个 token ,页面中的请求把这个 token 带上,服务端对 token 进行校验。注意这个 token 不能存储在 cookie 中

关于 xss csrf 网上有很详细的介绍,不过核心原理还是比较简单的。

了解 CORB

第一次听到有点懵,因为是 CORS ,回来查了资料才明白。

CORB 是一种判断是否要在跨站资源数据到达页面之前阻断其到达当前站点进程中的算法,降低了敏感数据暴露的风险。是站点隔离的一种实现机制,针对跨域标签,保护站点资源。

当跨域请求回来的数据 MIME type 同跨域标签应有的 MIME 类型不匹配时,浏览器会启动 CORB 保护数据不被泄漏,被保护的数据类型只有 html xml json

MIME type

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 方法的跨域请求

  1. image 标签实现
var img = new Image;
img.onload = function() {
},
img.onerror = function() {
},
img.src = options.url;
  1. script 标签实现 这个就是常说的 JSONP 。script 会执行返回的字符串,那么可以通过约定一个参数,前端的参数指定一个全局的方法,服务端获取到全局方法后,构造一个执行函数的字符串,并把报文放入函数的参数中。浏览器接收到后以 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 做反向代理,详细配置就不多说了

CORS

这就全是服务端的工作了,主要的三个参数

Access-Control-Allow-Origin: 服务器可接受的请求来源

Access-Control-Request-Method: 服务器实际请求所使用的 HTTP 方法

Access-Control-Request-Headers: 服务器实际请求所携带的自定义首部字段。

中间层 BFF 做转换

假如有 BFF 层的话,可以在这一层做一个中转,这个就得看项目架构是否有条件了。

html的meta里设置缓存和http请求头设置缓存有什么区别吗

html 的 meta 设置的缓存策略是对于当前文档有效,用于定义页面缓存。

与http的请求参数很相像,就不重复了,详细介绍可参考 设置meta标签 清除页面缓存

history 路由和 hash 路由

hash 路由

hash 路由,在 html5 前,为了解决单页路由跳转问题采用的方案, hash 的变化不会触发页面渲染,服务端也无法获取到 hash 值,前端可通过监听 hashchange 事件来处理hash值的变化

window.addEventListener('hashchange', function(){ 
    // 监听hash变化,点击浏览器的前进后退会触发
})

history 路由

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 方法不会触发              
});

js 事件的几种绑定方式

  • 在 dom 元素中直接绑定,<div class="an" onclick="aa()">aaaa</div>
  • js 中绑定 document.getElementById("demo").οnclick=function(){}
  • 添加监听事件 document.addEventListener('name',()=>{})

什么是事件委托

浏览器中的事件触发有三个阶段:

  1. 最从外层开始往里传播,即事件捕获阶段
  2. 事件抵达了目标节点,即目标阶段
  3. 从目标阶段往外层返回,即冒泡阶段

事件委托也叫事件代理,在 dom 节点中,因为有事件冒泡机制,所以子节点的事件可以被父节点捕获。

因此,在适当的场景下将子节点的事件用父节点监听处理,支持的事件 点击事件 鼠标事件监听。

事件代理的优势:

  1. 可以减少监听器的数量,减少内存占用
  2. 对于动态新增的子节点,可以实现事件监听

关于事件捕获和事件冒泡的理解:

事件捕获:事件从外往里传播,addEventListener 最后一个参数设置成 true 就可以捕获事件,默认是 false ,监听事件冒泡。捕获是计算机处理输入的逻辑

事件冒泡:事件由内往外传播,冒泡是人类理解事件的思维。

target与currentTarget区别

target:指的是事件流的目标阶段,获取的是被点击的元素。

currentTarget:在事件流的捕获和冒泡阶段时,是指向当前事件活动对象,只有在目标阶段的时候,两者才会相等

CSS加载问题

根据页面渲染流程可得知:

  1. css加载不会阻塞DOM树的解析;
  2. css加载会阻塞DOM树的渲染;
  3. css加载会阻塞后面js语句的执行

介绍下资源预加载 prefetch/preload async/defer

prefetch preload

都是告知浏览器提前加载文件(图片、视频、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 和 defer 的区别

用于js脚本预加载

async : 加载脚本和渲染后续文档元素并行进行,脚本加载完成后,暂停html解析,立即解析js脚本

defer : 加载脚本和渲染后续文档元素并行进行,但脚本的执行会等到 html 解析完成后执行

js-async-defer.png

参考资料:

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…

介绍下 viewport

<meta name="viewport" content="width=500, initial-scale=1">

这里只指定了两个属性,宽度和缩放,实际上 viewport 能控制的更多,它能表示的全部属性如下:

  • width:页面宽度,可以取值具体的数字,也可以是 device-width,表示跟设备宽度相等。
  • height:页面高度,可以取值具体的数字,也可以是 device-height,表示跟设备高度相等。
  • initial-scale:初始缩放比例。
  • minimum-scale:最小缩放比例。
  • maximum-scale:最大缩放比例。
  • user-scalable:是否允许用户缩放。

移动端300ms延时的原因? 如何处理?

因为在以前移动端双击可以缩放或者滑动,所以为了区分是点击还是双击,加了 300ms 的延迟。

解决方案:

  • css touch-action touch-action的默为 auto,将其置为 none 即可移除目标元素的 300 毫秒延迟 缺点: 新属性,可能存在浏览器兼容问题
  • 利用touchstart和touchend来模拟click事件,缺点有点击穿透
  • fastclick 原理: 在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后真正的click事件阻止掉
  • 所有版本的Android Chrome浏览器,如果设置viewport meta的值有user-scalable=no,浏览器也是会马上出发点击事件。

Web Worker

(后续再补上)

浏览器性能监控

使用 performance.timing 这个api就可以获取到绝大部分性能相关的数据

performance

  • navigationStart :在同一个浏览器上下文中,前一个网页(与当前页面不一定同域)unload 的时间戳,如果无前一个网页 unload ,则与 fetchStart 值相等
  • unloadEventStart :前一个网页(与当前页面同域)unload 的时间戳,如果无前一个网页 unload 或者前一个网页与当前页面不同域,则值为 0
  • redirectStart :第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0
  • redirectEnd :最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内的重定向才算,否则值为 0

开始加载当前页面

  • fetchStart :浏览器准备好使用 HTTP 请求抓取文档的时间,这发生在检查本地缓存之前

网络传输阶段 DNS TCP

  • domainLookupStart :DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
  • domainLookupEnd :DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
  • connectStart :HTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等,如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间
  • secureConnectionStart :HTTPS 连接开始的时间,如果不是安全连接,则值为 0
  • connectEnd :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 事件,值为 0
  • loadEventEnd :load 事件的回调函数执行完毕的时间,如果没有绑定 load 事件,值为 0

各个阶段时间段查询

DNS查询耗时 = domainLookupEnd - domainLookupStart

TCP链接耗时 = connectEnd - connectStart

request请求耗时 = responseEnd - responseStart

解析dom树耗时 = domComplete - domInteractive

白屏时间 = domloadng - fetchStart

domready时间 = domContentLoadedEventEnd - fetchStart

onload时间 = loadEventEnd - fetchStart

举几个h5的新特性

这个题有点无聊了,但还是遇到了

  • 新增语义化标签
  • 新增api、本地存储
  • css 边框、背景、动画

对于连续的中文输入有哪些事件

输入框对于输入连续中文的时候可以使用以下两个监听事件(第一次知道还有这个事件):

compositionstart:事件触发于一段文字的输入之前(类似于 keydown 事件,但是该事件仅在若干可见字符的输入之前,而这些可见字符的输入可能需要一连串的键盘操作、语音识别或者点击输入法的备选词)。

compositionend:当文本段落的组成完成或取消时,事件将被触发 (具有特殊字符的触发, 需要一系列键和其他输入, 如语音识别或移动中的字词建议)。