面试题 - 浏览器

87 阅读15分钟

最近找工作整理了一些面试题,分享给大家一起来学习。如有问题,欢迎指正。

前端面试题系列文章:

网页从输入网址到渲染完成

  1. 浏览器的地址栏输入URL并按下回车

  2. DNS域名解析

    DNS寻址过程 image.png

    1. 在浏览器中输入www.xxx.com域名,检查浏览器缓存、检查本地hosts文件是否有这个网址的映射,如果有,就调用这个IP地址映射,解析完成。
    2. 如果浏览器缓存、检查本地hosts文件没有该域名对应的IP地址,则向本地DNS服务器发送查询请求。
    3. 如果本地DNS服务器缓存中有该域名对应的IP地址,则直接返回,解析过程结束。
    4. 如果本地DNS服务器缓存中没有该域名对应的IP地址,则向根域名服务器发送查询请求。
    5. 根域名服务器返回顶级域名服务器地址。
    6. 本地DNS服务器向顶级域名服务器发送查询请求。
    7. 顶级域名服务器返回权威DNS服务器的地址。
    8. 本地DNS服务器向权威DNS服务器发送查询请求。
    9. 权威DNS服务器返回该域名对应的IP地址,并将结果返回给本地DNS服务器。
    10. 本地DNS服务器将结果保存在缓存中,并将结果返回给用户所在的计算机。
    11. 用户所在的计算机将结果保存在缓存中,并使用该IP地址访问对应的网站
  3. 根据IP建立TCP连接(三次握手)

    建立一个TCP连接需要三次握手,而终止一个TCP连接要经过四次挥手。

    三次握手原理:

    image.png

    第一次握手:客户端发送一个带有 SYN(synchronize同步)标志的数据包给服务端。
    第二次握手:服务端接收成功后,回传一个带有 SYN/ACK 标志的数据包传递确认信息,表示我收到了。
    第三次握手:客户端再回传一个带有 ACK 标志的数据包,表示我知道了,握手结束

    四次挥手

    image.png

    第一次挥手:客户端发送一个FIN,用来关闭客户端到服务器的数据传送,并且指定一个序列号。客户端进入FIN_WAIT_1状态。
    第二次挥手:服务器收到FIN后,发送一个ACK给客户端,确认序号为客户端的序列号值 +1 ,表明已经收到客户端的报文了,此时服务器处于 CLOSE_WAIT 状态。
    第三次挥手:服务器发送一个FIN,用来关闭服务器到客户端的数据传送,服务器进入LAST_ACK状态。
    第四次挥手:客户端收到FIN后,客户端进入TIME_WAIT状态,接着发送一个ACK给服务器,确认序号为收到序号+1 ,服务器进入CLOSED状态,完成四次挥手

  4. 浏览器向服务器发起Http请求获取资源

  5. 浏览器解析资源进行页面渲染

浏览器渲染页面过程

image.png

  1. 解析html代码,生成DOM 树
    • 主线程解析到link位置,此时外部的 CSS 文件还没有下载解析好,主线程不会等待,继续解析后续的 HTML。这是因为下载和解析 CSS 的工作是在预解析线程中进行的。这就是 CSS 不会阻塞 HTML 解析的根本原因
    • 主线程解析到script位置,会停止解析 HTML,转而等待 JS 文件下载好,并将全局代码解析执行完成后,才能继续解析 HTML。这是因为 JS 代码的执行过程可能会修改当前的 DOM 树,所以 DOM 树的生成必须暂停。这就是 JS 会阻塞 HTML 解析的根本原因
    • display:none 的元素也会在DOM树中。
    • 注释也会在DOM树中
    • script标签会在DOM树中
  2. 解析css代码,生成CSSOM 树
    • CSS 的下载和解析工作是在预解析线程中进行的,可以与HTML解析同进行,不会受到js的阻塞
    • 当 JS试图在浏览器还未完成CSSOMTree的下载和构建时去操作CSS样式,浏览器会暂停脚本的运行和DOM的构建,直至浏览器完成了CSSOM的下载和构建。所以JS脚本的出现会让CSSOM的构建阻塞DOM的构建
  3. 通过DOM 树 和 CSSOM 树 生成 Render 树
    • Render Tree和DOM Tree不完全对应。 - display: none的元素不在Render Tree中,但在DOM树 - visibility: hidden的元素在Render Tree中,也在DOM树中
  4. 页面布局 从渲染树的根节点开始遍历,每个节点都是一个Render Object对象,包含宽高,位置,背景色等样式信息。浏览器就可以通过这些样式信息来确定每个节点对象在页面上的确切大小和位置。 - float元素,absoulte元素,fixed元素会发生位置偏移。 - 我们常说的脱离文档流,其实就是脱离Render Tree
  5. 页面绘制 渲染器的paint()方法将布局阶段生成的盒模型像素化,呈现在页面上

回流(重排)与重绘

重排必重绘,重绘不一定重排。

  • 回流(重排): 当我们的操作引发了 DOM 树中几何尺寸的变化(改变元素的大小、位置、布局方式等),这时渲染树里有改动的节点和它影响的节点都要重新计算。这个过程就叫做重排,也称为回流。在改动发生时,要重新经历页面渲染的整个流程,所以开销是很大的。

    以下操作都会导致页面重排:

    • 浏览器窗口大小发生变化;
    • 元素的内容发生变化;
    • 元素的尺寸或者位置发生变化;
    • 元素的字体大小发生变化;
    • 激活CSS伪类;
    • 添加或者删除可见的DOM元素。

    在触发重排时,由于浏览器渲染页面是基于流式布局的,所以当触发回流时,会导致周围的DOM元素重新排列,它的影响范围有两种:

    • 全局范围:从根节点开始,对整个渲染树进行重新布局;
    • 局部范围:对渲染树的某部分或者一个渲染对象进行重新布局。
  • 重绘:当我们改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时

    下面这些属性会导致回流:

    • color、background 相关属性:background-color、background-image 等;
    • outline 相关属性:outline-color、outline-width 、text-decoration;
    • border-radius、visibility、box-shadow。

如何减少重绘和重排

  1. 减少直接操作dom元素,改用className用于控制(在用js修改盒子的多个样式时,尽量使用className来一次性对盒子进行修改。)
  2. 如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入document
  3. 使用display:none让父盒子先消失,然后再对此盒子添加一些子元素,然后再将这个父盒子display:block显示出来。浏览器一共发生了两次重排,即第一次的隐藏和最后面的显示。中间的操作都不会造成重排。(父元素下多个子元素需要改变时)
  4. 对多次操作的dom元素,使用fixed和absolute定位,比如的动画

浏览器页面的生命周期

  • DOMContentLoaded事件在DOM树构建完毕后被触发,我们可以在这个阶段使用js去访问元素。

  • load事件在页面所有资源被加载完毕后触发,通常我们不会用到这个事件,因为我们不需要等那么久。

  •  beforeunload在用户即将离开页面时触发,它返回一个字符串,浏览器会向用户展示并询问这个字符串以确定是否离开。

  •  unload在用户已经离开时触发,我们在这个阶段仅可以做一些没有延迟的操作,由于种种限制,很少被使用。

  • document.readyState表征页面的加载状态,可以在readystatechange中追踪页面的变化状态:

    • loading — 页面正在加载中。
    • interactive – 页面解析完毕,时间上和 DOMContentLoaded同时发生,不过顺序在它之前。
    • complete – 页面上的资源都已加载完毕,时间上和window.onload同时发生,不过顺序在他之前。

http缓存

HTTP 缓存可以说是HTTP性能优化中简单高效的一种优化方式了,缓存是一种保存资源副本并在下次请求时直接使用该副本的技术,当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载

  • Expires

    Expires是由 HTTP1.0 所提供的支持HTTP缓存的头部,由服务器返回,用GMT格式的字符串表示:

    expires: Tue, 14 Aug 2018 14:32:49 GMT

    读取缓存的条件:缓存时间(服务器的时间)< 当前时间(客户端的时间)

    缺点: 当用户本地的时间不许确,或用户进行跨时区的移动时,这个时间极可能就会过时,而没法发挥它本应该发挥的做用。

  • Cache-Control

    因为Expires存在着不少不足,因此HTTP1.1又为咱们提供了Cache-Control。常见的取值有 private、public、no-cache、max-age,no-store,默认为 private。

    • private:仅单用户私有,不被多用户共享
    • public:缓存可以被多用户共享
    • no-store:不使用缓存,向服务器请求返回
    • no-cache:不使用强制缓存,与服务器发生交互。
    • max-age:用来设置资源可以被缓存多长时间,单位为秒

http缓存机制

  • 强缓存

    强缓存是利用Expires或者Cache-Control这两个http response header实现的,它们都用来表示资源在客户端缓存的有效期。

    在这个有效期内当浏览器对某个资源的请求命中了强缓存时,其返回的http状态为200,而且不会去对服务器进行请求,而是直接使用其本地的缓存。

    • expires: Tue, 21 Aug 2018 10:17:45 GMT

    • cache-control: max-age=691200

    ** Catch-Control的优先级高于expires。

    失效条件:用户强制刷新或时间过期

    缺点:静态资源重新部署后,浏览器还返回之前的静态资源文件。动态资源更改后,浏览器还返回之前的动态资源

  • 协商缓存

    发送 HTTP 请求时,当资源过时时(强缓存失效),请求头携带一个缓存标识(第一次请求时响应头返回的时间/hash),服务器去对比请求头的标识,判断浏览器端的数据资源是否与服务器端一致,一致时,返回 HTTP 状态码 304(不返回资源),客户端接到之后,就直接把本地缓存返回到浏览器中。不一致时,服务器将新的资源返回客户端,并更新本地缓存数据,并返回200的状态码。

    验证缓存是否失效

    • last-modified 和 if-modified-since(http1.0)

      响应头: last-modified:Wed, 22 May 2019 03:53:42 GMT

      请求头: if-modified-since:Wed, 22 May 2019 03:53:42 GMT

      当Web服务器响应请求时,会把资源的最后修改时间放到 Last-Modified 响应头。

      再次发起请求的时候,请求头会带上上一次响应头中的 Last-Modified 的时间,并放到 If-Modified-Since 请求头属性中。服务端根据文件最后一次修改时间和 If-Modified-Since 的值进行比较。

      缺点: 当用户本地的时间不许确,或用户进行跨时区的移动时,这个时间极可能就会过时,而没法发挥它本应该发挥的做用。

    • ETag 和 If-None-Match(http1.1)

      响应头: Etag: w/哈希值(唯一标识)

      请求头: If-None-Match: w/哈希值(唯一标识)

      当Web服务器响应请求时,会告诉浏览器当前资源在服务器的惟一标识ETag(哈希值生成规则是由服务器决定的)

      再次向web服务器请求时带上头If-None-Match (上一次响应头中的Etag的值)。Web服务器收到请求后发现有头If-None-Match, 就会将其被请求的资源的相应校验字段进行对比,而后再决定返回200或304

      ** Etag 要优于 Last-Modified

浏览器的同源策略

协议、域名、端口有一个不同就不是同源,三者均相同,这两个网站才是同源, 否则跨域

同源策略是一个重要的安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。

作用:防止恶意网站页面可以获取其他网站的本地数据

  • 限制范围

    • Cookie、LocalStorage 和 IndexDB 无法读取。
    • DOM 无法获得。
    • AJAX 请求不能发送。
  • 有哪些是不受同源策略限制

    • script、img、iframe、link、等带有src属性的标签。

    • 重定向。

    • 表单提交。 直接使用action的时候,由于不关系请求的响应,所以浏览器认为是安全的

      <from action="baidu.com">
          // you form filed
      </from>
      

跨域问题解决方案

  • iframe

    • document.domain: 这种方法只适用于 Cookie 和 iframe 窗口

      Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie

      举例来说,A网页是http://w1.example.com/a.html,B网页是http://w2.example.com/b.html,那么只要设置相同的document.domain,两个网页就可以共享Cookie。

      // A网页和B网页
      document.domain = 'example.com';
      
      // A网页
      document.cookie = "test1=hello";
      
      // B网页
      var allCookie = document.cookie;
      

      另外,服务器也可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如.example.com,这样的话,二级域名和三级域名不用做任何设置,都可以读取这个Cookie

      Set-Cookie: key=value; domain=.example.com; path=/
      
    • 片段识别符:URL的#号后面的部分

      // 父窗口
      var src = originURL + '#' + data;
      document.getElementById('myIFrame').src = src;
      
      // 子窗口
      window.onhashchange = checkMessage;
      function checkMessage() {
        var message = window.location.hash;
        // ...
      }
      
    • window.name

    • window.postMessage

  • ajax请求

    • JSONP 解决跨域

      网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来

      function addScriptTag(src) {
        var script = document.createElement('script');
        script.setAttribute("type","text/javascript");
        script.src = src;
        document.body.appendChild(script);
      }
      
      window.onload = function () {
        addScriptTag('http://example.com/ip?callback=foo');
      }
      
      function foo(data) {
        console.log('Your public IP address is: ' + data.ip);
      };
      
      

      jquery 实现 jsonp跨域请求

      function showDate(data) {
          console.log(data)
      }
      $.ajax({
          url: 'http://www.example.com:5000/data.js',
          type: 'get',          //请求方式必须为 get
          dataType: 'jsonp',    //数据类型改为 jsonp
          jsonpCallback: 'showDate'   //数据返回后调用的回调函数
      })
      
    • CORS通信

      Access-Control-Allow-Origin:该字段是必须的。服务器设置。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求

      Access-Control-Allow-Credentials:该字段可选。服务器设置。表示是否允许发送Cookie,默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

      withCredentials:如果要把Cookie发到服务器,客户端需要将AJAX请求打开withCredentials,与服务器端Access-Control-Allow-Origin一同设置。此时Access-Control-Allow-Origin不能为*

    • WebSocket

CSRF和XSS

  • xss:中文名称跨站脚本攻击,通常出现在搜索框、留言板、评论区等地方 分类:反射性、存储型、DOM型

    攻击方式:构造恶意链接,诱骗用户点击盗取用户的cookie信息 漏洞危害:xss蠕虫、会话、流量劫持、网站挂马、盗取cookie 攻击类型

    • 反射性xss:

      通常这一类xss危害较低,对网站没有什么严重的影响,具体表现在用户在搜索框输入xss语句返回弹框,仅出现一次

    • 存储型xss:

      存储型对网站危害较大,用户插入xss语句后,恶意语句会存入网站数据库中,用户访问过程都会出现弹框

    • DOM型xss:

      利用浏览器的dom解析,更不容易被发现,可以更改页面布局

    防御

    • 对输入脚本进行过滤或转码
    • 设置黑名单和白名单
    • 使用 HttpOnly 属性,服务器可以将某些 Cookie 设置为 HttpOnly 标志。使用 HttpOnly 标记的 Cookie 只能使用在 HTTP 请求过程中,所以无法通过 JavaScript 来读取这段 Cookie。
  • CSRF:跨站请求伪造,利用用户的登录状态发起的跨站请求

    • 危害:盗用用户身份、执行用户操作,修改信息

    • 漏洞原理

      1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

      2. 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;

      3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;

      4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;

      5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A(站点 A 没有做任何 CSRF 防御)并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。    

    • 防护方法

      1. 设置referer字段;但是并不是所有服务器在任何时候都可以接受referer字段,所以这个方法存在一定的局限性

      2. 设置验证码;在用户操作的过程中设置身份确认的验证码,该方法会很有用,不过验证码频繁的话会影响用户体验

      3. 设置token值,在用户请求中设置一个随机没有规律的token值可以有效的防止攻击者利用漏洞攻击

session和cookie

  • Cookie

在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie数据自动的携带给服务器。服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie只能存储一些小量的数据

  • Session

session和cookie的作用有点类似,都是为了存储用户相关的信息,cookie是存储在本地浏览器,而session存储在服务器。存储在服务器的数据会更加的安全,不容易被窃取,存储在服务器也有一定的弊端,就是会占用服务器的资源

  • Cookie与Session的区别

    1. 使用方式

    cookie机制:如果不在浏览器中设置过期事时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称为会话cookie。如果在浏览器中设置了cookie的过期设置,cookie会被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期事件结束才消失。

    session机制:当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid。如果有sessionid,服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid,服务器会创建新的session对象,并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端,在交互中浏览器按照规则将sessionid发送给服务器。

    2. 保存状态

    cookie数据保存在客户端,session数据保存在服务器端

    3. 存储的大小

    单个cookie保存的数据不能超过4kb;session大小没有限制。

    4. 存储内容

    cookie只能保存字符串类型,以文本的方式。session通过类似于Hashtable的数据结构来保存,支持任何类型的对象

    5. 安全性

    session的安全性大于cookie,

    1. sessionid存储在cookie中,若要攻破session首先要攻破cookie;
    2. sessionid是要有人登录,或者启动session_start才会有,所以攻破cookie也不一定能得到sessionid;
    3. 第二次启动session_start后,前一次的sessionid就是失效了,session过期后,sessionid也随之失效
    4. sessionid是加密的。
  • 应用场景:

    • cookie:

      1. 判断用户是否登录过网站,以便下次登录时能够实现自动登录(或者记住密码)。
      2. 保存上次登录的事件等信息。
      3. 保存上次查看的页面
      4. 浏览计数
    • session:

      1. 网上商城中的购物车
      2. 保存用户登录信息
      3. 将某些数据放入session中,供同一用户的不同页面使用
      4. 防止用户非法登录

cookie、sessionStorage、localStorage区别

图片1.png